wushaodong 4 éve
commit
4f42ab6c63
100 módosított fájl, 3052 hozzáadás és 0 törlés
  1. 14 0
      .gitignore
  2. 0 0
      apps/WechatApplet/__init__.py
  3. 17 0
      apps/WechatApplet/filters.py
  4. 0 0
      apps/WechatApplet/migrations/__init__.py
  5. 266 0
      apps/WechatApplet/models.py
  6. 0 0
      apps/WechatTp/__init__.py
  7. 0 0
      apps/WechatTp/migrations/__init__.py
  8. 84 0
      apps/WechatTp/models.py
  9. 0 0
      apps/__init__.py
  10. 1 0
      apps/account/__init__.py
  11. 23 0
      apps/account/filters.py
  12. 0 0
      apps/account/migrations/__init__.py
  13. 147 0
      apps/account/models.py
  14. 6 0
      apps/admin/__init__.py
  15. 52 0
      apps/admin/serializers.py
  16. 0 0
      apps/admin/tenant/__init__.py
  17. 41 0
      apps/admin/tenant/serializers.py
  18. 11 0
      apps/admin/tenant/urls.py
  19. 49 0
      apps/admin/tenant/views.py
  20. 16 0
      apps/admin/urls.py
  21. 0 0
      apps/admin/user/__init__.py
  22. 35 0
      apps/admin/user/serializers.py
  23. 13 0
      apps/admin/user/urls.py
  24. 58 0
      apps/admin/user/views.py
  25. 41 0
      apps/admin/views.py
  26. 0 0
      apps/admin/wechatapplet/__init__.py
  27. 19 0
      apps/admin/wechatapplet/serializers.py
  28. 13 0
      apps/admin/wechatapplet/urls.py
  29. 126 0
      apps/admin/wechatapplet/views.py
  30. 0 0
      apps/admin/wechattp/__init__.py
  31. 19 0
      apps/admin/wechattp/serializers.py
  32. 12 0
      apps/admin/wechattp/urls.py
  33. 29 0
      apps/admin/wechattp/views.py
  34. 0 0
      apps/api/__init__.py
  35. 10 0
      apps/api/urls.py
  36. 91 0
      apps/api/views.py
  37. 157 0
      apps/base.py
  38. 0 0
      apps/dashboard/__init__.py
  39. 9 0
      apps/dashboard/views.py
  40. 0 0
      apps/log/__init__.py
  41. 20 0
      apps/log/filters.py
  42. 0 0
      apps/log/migrations/__init__.py
  43. 58 0
      apps/log/models.py
  44. 184 0
      apps/serializer_errors.py
  45. 5 0
      apps/tenant/__init__.py
  46. 0 0
      apps/tenant/area/__init__.py
  47. 21 0
      apps/tenant/area/filters.py
  48. 0 0
      apps/tenant/area/migrations/__init__.py
  49. 47 0
      apps/tenant/area/models.py
  50. 41 0
      apps/tenant/area/serializers.py
  51. 16 0
      apps/tenant/area/urls.py
  52. 97 0
      apps/tenant/area/views.py
  53. 19 0
      apps/tenant/biz.py
  54. 0 0
      apps/tenant/building/__init__.py
  55. 11 0
      apps/tenant/building/filters.py
  56. 0 0
      apps/tenant/building/migrations/__init__.py
  57. 27 0
      apps/tenant/building/models.py
  58. 11 0
      apps/tenant/building/resources.py
  59. 29 0
      apps/tenant/building/serializer.py
  60. 12 0
      apps/tenant/building/urls.py
  61. 109 0
      apps/tenant/building/views.py
  62. 0 0
      apps/tenant/config/__init__.py
  63. 12 0
      apps/tenant/config/filters.py
  64. 0 0
      apps/tenant/config/migrations/__init__.py
  65. 68 0
      apps/tenant/config/models.py
  66. 17 0
      apps/tenant/config/serializers.py
  67. 13 0
      apps/tenant/config/urls.py
  68. 50 0
      apps/tenant/config/views.py
  69. 0 0
      apps/tenant/employee/__init__.py
  70. 15 0
      apps/tenant/employee/filters.py
  71. 0 0
      apps/tenant/employee/migrations/__init__.py
  72. 91 0
      apps/tenant/employee/models.py
  73. 106 0
      apps/tenant/employee/serializers.py
  74. 14 0
      apps/tenant/employee/urls.py
  75. 88 0
      apps/tenant/employee/views.py
  76. 0 0
      apps/tenant/equipment/__init__.py
  77. 15 0
      apps/tenant/equipment/filters.py
  78. 0 0
      apps/tenant/equipment/migrations/__init__.py
  79. 22 0
      apps/tenant/equipment/models.py
  80. 35 0
      apps/tenant/equipment/serializers.py
  81. 12 0
      apps/tenant/equipment/urls.py
  82. 62 0
      apps/tenant/equipment/views.py
  83. 12 0
      apps/tenant/filters.py
  84. 0 0
      apps/tenant/inspection_order/__init__.py
  85. 12 0
      apps/tenant/inspection_order/filters.py
  86. 0 0
      apps/tenant/inspection_order/migrations/__init__.py
  87. 89 0
      apps/tenant/inspection_order/models.py
  88. 29 0
      apps/tenant/inspection_order/serializers.py
  89. 12 0
      apps/tenant/inspection_order/urls.py
  90. 38 0
      apps/tenant/inspection_order/views.py
  91. 0 0
      apps/tenant/migrations/__init__.py
  92. 41 0
      apps/tenant/models.py
  93. 1 0
      apps/tenant/notices/__init__.py
  94. 9 0
      apps/tenant/notices/filters.py
  95. 0 0
      apps/tenant/notices/migrations/__init__.py
  96. 27 0
      apps/tenant/notices/models.py
  97. 23 0
      apps/tenant/notices/serializers.py
  98. 13 0
      apps/tenant/notices/urls.py
  99. 50 0
      apps/tenant/notices/views.py
  100. 10 0
      apps/tenant/option/filters.py

+ 14 - 0
.gitignore

@@ -0,0 +1,14 @@
+# See https://help.github.com/ignore-files/ for more about ignoring files.
+
+**/migrations/*
+!**/migrations/__init__.py
+
+*.py[cod]
+local_settings.*
+*.bat
+*.txt
+*.whl
+venv
+.idea
+python*
+.history

+ 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


+ 266 - 0
apps/WechatApplet/models.py

@@ -0,0 +1,266 @@
+# coding=utf-8
+
+import time
+import os
+from django.conf import settings
+from django.db import models
+from django.utils import timezone
+from apps.WechatTp.models import WechatTp
+from utils.wx.wechat import WeChat
+from utils.exceptions import CustomError
+from utils.file_operation import CertPath
+from apps.tenant.models import Tenant
+
+
+class WechatApplet(models.Model):
+    AUDIT_SUCCESS = 0
+    AUDIT_REJECT = 1
+    AUDITING = 2
+    RECALL = 3
+    AUDIT_DELAY = 4
+
+    AUDIT_STATUS_CHOICE = (
+        (AUDIT_SUCCESS, u'审核通过'),
+        (AUDIT_REJECT, u'审核拒绝'),
+        (AUDITING, u'审核中'),
+        (RECALL, u'已撤回'),
+        (AUDIT_DELAY, u'审核延后'),
+    )
+
+    tenant = models.ForeignKey(Tenant, verbose_name=u'租户', on_delete=models.PROTECT, editable=False)
+    wechat_tp = models.ForeignKey(WechatTp, verbose_name=u'第三方平台', on_delete=models.PROTECT, editable=False)
+    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'刷新令牌')
+    authorizer_access_token = models.CharField(max_length=512, verbose_name=u'令牌')
+    access_token_gtime = models.DateTimeField(verbose_name=u"获取令牌时间")
+    expires_in = models.IntegerField(verbose_name=u'令牌有效期')
+    is_authorize = models.BooleanField(verbose_name=u'是否授权', default=False)
+    message_template_id = models.CharField(max_length=255, verbose_name=u'消息模版ID', null=True)
+
+    nick_name = models.CharField(max_length=255, verbose_name=u'昵称', default="")
+    head_img = models.CharField(max_length=255, verbose_name=u'头像', default="")
+    principal_name = models.CharField(max_length=255, verbose_name=u'主体名称', default="")
+    qrcode_url = models.CharField(max_length=512, verbose_name=u'二维码', default="")
+    user_version = models.CharField(max_length=512, verbose_name=u'当前程序版本', default="")
+    template_id = models.CharField(max_length=512, verbose_name=u'当前模板', default="")
+    tenant_num = models.CharField(max_length=512, verbose_name=u'商户号', default="")
+    tenant_key = models.CharField(max_length=512, verbose_name=u'商户密钥', default="")
+
+    apiclient_cert = models.CharField(max_length=255, verbose_name=u'API证书cert', default="")
+    apiclient_key = models.CharField(max_length=255, verbose_name=u'API证书key', default="")
+
+    auditid = models.CharField(max_length=512, verbose_name=u'待审核ID', default="")
+    wait_audit_version = models.CharField(max_length=512, verbose_name=u'待审核版本', null=True)
+    wait_audit_template = models.CharField(max_length=512, verbose_name=u'待审核模板', null=True)
+    audit_status = models.IntegerField(choices=AUDIT_STATUS_CHOICE, verbose_name=u'审核状态', null=True)
+    reject_reason = models.CharField(max_length=512, verbose_name=u'拒绝原因', default="")
+
+    class Meta:
+        db_table = "wechat_applet"
+        ordering = ['-id']
+        index_together = ()
+        verbose_name = u"小程序"
+        default_permissions = ()
+
+    @staticmethod
+    def getById(id):
+        try:
+            id = int(id)
+        except:
+            raise CustomError(u'无效的小程序ID!')
+
+        instance = WechatApplet.objects.filter(pk=id).first()
+        if not instance:
+            raise CustomError(u'未找到相应的小程序!')
+        return instance
+
+    @staticmethod
+    def getByAppid(appid):
+        instance = WechatApplet.objects.filter(authorizer_appid=appid).first()
+        if not instance:
+            raise CustomError(u'未找到相应的小程序!')
+        return instance
+
+    @staticmethod
+    def addAuthorizer(wechat_component, authorization_code, tenant):
+        gtime = timezone.now()
+        component_appid = wechat_component.getAppid()
+        component_access_token = wechat_component.getAccessToken()
+        res = WeChat.getAuthorizationInfo(component_appid, authorization_code, component_access_token)
+        ares = WeChat.getAuthorizerInfo(component_appid, res['authorization_info']['authorizer_appid'], component_access_token)
+        authorizer = WechatApplet.getByAppidAndComponentAppid(res['authorization_info']['authorizer_appid'], component_appid)
+        if authorizer:
+            if authorizer.is_authorize:
+                raise CustomError(u'该小程序已授权!')
+            authorizer.refresh(res['authorization_info'], ares['authorizer_info'], gtime, tenant)
+        else:
+            authorizer = WechatApplet.objects.create(
+                tenant=tenant,
+                wechat_tp=wechat_component,
+                authorizer_appid=res['authorization_info']['authorizer_appid'],
+                authorizer_refresh_token=res['authorization_info']['authorizer_refresh_token'],
+                authorizer_access_token=res['authorization_info']['authorizer_access_token'],
+                access_token_gtime=gtime,
+                expires_in=res['authorization_info']['expires_in'],
+                principal_name=ares['authorizer_info']['principal_name'],
+                nick_name=ares['authorizer_info']['nick_name'],
+                head_img=ares['authorizer_info']['head_img'],
+                qrcode_url=ares['authorizer_info']['qrcode_url'],
+                is_authorize=True
+            )
+        # 设置服务器域名
+        authorizer.setDomain()
+        return authorizer
+
+    @staticmethod
+    def getByAppidAndComponentAppid(appid, component_appid):
+        authorizer = WechatApplet.objects.filter(authorizer_appid=appid, wechat_tp_id__component_appid=component_appid).first()
+        return authorizer
+
+    def getComponent(self):
+        return self.wechat_tp
+
+    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
+        if not self.authorizer_refresh_token:
+            return ''
+        gtime = timezone.now()
+        component = self.getComponent()
+        res = WeChat.getAuthorizerAccessToken(component.getAppid(), self.authorizer_appid, self.authorizer_refresh_token, component.getAccessToken())
+        self.refreshAccessToken(gtime, res['authorizer_access_token'], res['expires_in'], res['authorizer_refresh_token'])
+        return self.authorizer_access_token
+
+    def revoke(self):
+        tenant = self.tenant
+        tenant.is_bind_app = False
+        tenant.save()
+        self.is_authorize = False
+        self.save()
+
+    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()
+
+    def refresh(self, authorization_info, authorizer_info, access_token_gtime, tenant):
+        self.authorizer_refresh_token = authorization_info['authorizer_refresh_token']
+        self.authorizer_access_token = authorization_info['authorizer_access_token']
+        self.access_token_gtime = access_token_gtime
+        self.expires_in = authorization_info['expires_in']
+        self.principal_name = authorizer_info['principal_name']
+        self.nick_name = authorizer_info['nick_name']
+        self.head_img = authorizer_info['head_img']
+        self.qrcode_url = authorizer_info['qrcode_url']
+        self.is_authorize = True
+        self.tenant = tenant
+        self.save()
+
+    def uploadCode(self, template_id, user_version, user_desc):
+        WeChat.commitCode(self.getAccessToken(), template_id, user_version, user_desc)
+        result = WeChat.submitAuditCode(self.getAccessToken())
+        self.auditid = result['auditid']
+        self.wait_audit_version = user_version
+        self.wait_audit_template = template_id
+        self.audit_status = WechatApplet.AUDITING
+        self.reject_reason = ''
+        self.save()
+
+    def refreshAuditStatus(self):
+        result = WeChat.getLastSubmitAuditCodeStatus(self.getAccessToken())
+        if self.auditid == str(result['auditid']):
+            if result['status'] == WechatApplet.AUDIT_SUCCESS:
+                self.weapp_audit_success()
+            elif result['status'] == WechatApplet.AUDIT_REJECT:
+                self.weapp_audit_fail(result['reason'])
+            elif result['status'] == WechatApplet.RECALL:
+                self.weapp_audit_recall()
+            elif result['status'] == WechatApplet.AUDIT_DELAY:
+                self.weapp_audit_delay(result['reason'])
+
+    def weapp_audit_recall(self):
+        self.auditid = ''
+        self.wait_audit_template = None
+        self.wait_audit_version = None
+        self.audit_status = None
+        self.reject_reason = ''
+        self.save()
+
+    def weapp_audit_success(self):
+        self.user_version = self.wait_audit_version
+        self.template_id = self.wait_audit_template
+        self.auditid = ''
+        self.wait_audit_template = None
+        self.wait_audit_version = None
+        self.audit_status = None
+        self.reject_reason = ''
+        self.save()
+
+    def weapp_audit_fail(self, reason):
+        self.audit_status = WechatApplet.AUDIT_REJECT
+        self.reject_reason = reason
+        self.save()
+
+    def weapp_audit_delay(self, reason):
+        self.audit_status = WechatApplet.AUDIT_DELAY
+        self.reject_reason = reason
+        self.save()
+
+    def setDomain(self):
+        requestdomain = uploaddomain = downloaddomain = ['https://baoxiu360.top', ]
+        wsrequestdomain = []
+        WeChat.modify_domain(self.getAccessToken(), 'set', requestdomain, wsrequestdomain, uploaddomain, downloaddomain)
+
+    def addPlugin(self):
+        result = WeChat.addPlugin(self.getAccessToken())
+        return result
+
+    def releaseApplet(self):
+        result = WeChat.releaseCode(self.getAccessToken())
+        return result
+
+    def getMsgTemplateId(self):
+        if self.message_template_id:
+            return self.message_template_id
+        templates = WeChat.getTemplateList(self.getAccessToken())
+        for template in templates:
+            if template['title'] == u'维修完成通知':
+                self.message_template_id = template['priTmplId']
+                self.save()
+                return template['priTmplId']
+        return ''
+
+    def sendMsg(self, openid, name, address, fault_des, no):
+        template_id = self.getMsgTemplateId()
+        if not template_id:
+            return
+        time = timezone.now().strftime('%Y-%m-%d %H:%M:%S')
+        data = {
+            'time5': {'value': time},
+            'name4':{'value':name},
+            'thing3':{'value':address},
+            'thing2':{'value': fault_des},
+            'character_string7':{'value': no},
+        }
+        page = 'pages/repairList/repairList?sort=baoxiu&name=我的报修'
+        WeChat.sendSubscribeMessage(self.getAccessToken(), openid, template_id, page, data)
+
+    def upload_cert_file(self, file):
+        path = 'zzly_xcx_cert/%d/' % self.id
+        upload_path = CertPath(path)
+        filename = '%s%s' % (upload_path.path, file.name)
+        full_filename = "%s/%s" % (settings.MEDIA_ROOT, filename)
+        with open(full_filename, 'wb+') as destination:
+            for chunk in file.chunks():
+                destination.write(chunk)
+        if file.name == 'apiclient_cert.pem':
+            self.apiclient_cert = full_filename
+        elif file.name == 'apiclient_key.pem':
+            self.apiclient_key = full_filename
+        self.save()

+ 0 - 0
apps/WechatTp/__init__.py


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


+ 84 - 0
apps/WechatTp/models.py

@@ -0,0 +1,84 @@
+# coding=utf-8
+
+import time
+from django.db import models
+from django.utils import timezone
+
+from utils.exceptions import CustomError
+
+from utils.wx.wechat import WeChat
+
+
+class WechatTp(models.Model):
+    component_appid = models.CharField(max_length=512, verbose_name=u'第三方平台appid')
+    component_appsecret = models.CharField(max_length=512, verbose_name=u'第三方平台appsecret')
+    m_encode_key = models.CharField(max_length=512, verbose_name=u'消息加密key')
+    m_token = models.CharField(max_length=512, verbose_name=u'消息校验token')
+
+    component_verify_ticket = models.CharField(max_length=512, verbose_name=u'验证票据', null=True)
+    component_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)
+
+    class Meta:
+        db_table = "wechat_tp"
+        ordering = ['-id']
+        index_together = ()
+        verbose_name = u"第三方平台"
+        default_permissions = ()
+
+    @staticmethod
+    def getDefault():
+        tp = WechatTp.objects.filter().first()
+        if not tp:
+            raise CustomError(u'未找到微信第三方平台!')
+        return tp
+
+    @staticmethod
+    def getByAppid(appid):
+        component = WechatTp.objects.filter(component_appid=appid).first()
+        if not component:
+            raise CustomError(u'未找到相应的第三方平台!')
+        return component
+
+    def getAppid(self):
+        return self.component_appid
+
+    def getToken(self):
+        return self.m_token
+
+    def getEncodeKey(self):
+        return self.m_encode_key
+
+    def getAccessToken(self):
+        if self.component_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.component_access_token
+        res = WeChat.getComponentAccessToken(self.component_appid, self.component_appsecret, self.component_verify_ticket)
+        self.refreshAccessToken(timezone.now(), res['component_access_token'], res['expires_in'])
+        return self.component_access_token
+
+    def getPreAuthCode(self):
+        res = WeChat.getPreAuthCode(self.component_appid, self.getAccessToken())
+        return res['pre_auth_code']
+
+    def refreshVerifyTicket(self, component_verify_ticket):
+        self.component_verify_ticket = component_verify_ticket
+        self.save()
+
+    def refreshAccessToken(self, gtime, component_access_token, expires_in):
+        self.access_token_gtime = gtime
+        self.component_access_token = component_access_token
+        self.expires_in = expires_in
+        self.save()
+
+    def getAuthUrl(self, tenant_id):
+        url = 'https://mp.weixin.qq.com/cgi-bin/componentloginpage?component_appid=' + self.getAppid() + '&pre_auth_code=' + self.getPreAuthCode()
+        url += '&redirect_uri=https://baoxiu360.top/api/redirect_authorize/' + tenant_id + '/&auth_type=2'
+        return url
+
+    def getTemplateList(self):
+        template_list = WeChat.getCodeTemplateList(self.getAccessToken())
+        return template_list

+ 0 - 0
apps/__init__.py


+ 1 - 0
apps/account/__init__.py

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

+ 23 - 0
apps/account/filters.py

@@ -0,0 +1,23 @@
+# coding=utf-8
+import django_filters
+
+from django.contrib.auth import get_user_model
+from django.contrib.auth.models import Group
+
+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']
+
+
+class GroupFilter(django_filters.FilterSet):
+
+    class Meta:
+        model = Group
+        fields = '__all__'

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


+ 147 - 0
apps/account/models.py

@@ -0,0 +1,147 @@
+# coding=utf-8
+
+from django.db import models
+from django.contrib.auth.models import AbstractBaseUser, PermissionsMixin, BaseUserManager, Group
+from django.utils import timezone
+from rest_framework.utils import model_meta
+
+from utils.exceptions import CustomError
+from apps.tenant.models import Tenant
+
+class UserManager(BaseUserManager):
+    def create_administrator(self, username, password=None, **extra_fields):
+        return self.create_user(User.ADMINSTRATOR, username, password, **extra_fields)
+
+    def create_tenant(self, username, password=None, **extra_fields):
+        return self.create_user(User.TENANT, username, password, **extra_fields)
+
+    def create_superuser_tenant(self, username, password=None, **extra_fields):
+        u = self.create_tenant(username, password, **extra_fields)
+        u.is_active = True
+        u.is_superuser = True
+        u.save(using=self._db)
+        return u
+
+    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_administrator(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):
+    ADMINSTRATOR = 1 #管理员
+    TENANT = 2 #租户
+    CUSTOMER = 3 #游客
+
+    type = models.PositiveSmallIntegerField(verbose_name=u"类型", 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 = [
+            ('browse_group', u'查看'),
+            ('add_group', u'添加'),
+            ('delete_group', u'删除'),
+        ]
+
+    def __unicode__(self):
+        return self.username
+
+    def addAdministrator(self):
+        self.type = self.type | self.ADMINSTRATOR
+
+    def addTenant(self):
+        self.type = self.type | self.TENANT
+
+    def addCustomer(self):
+        self.type = self.type | self.CUSTOMER
+
+    def is_customer(self):
+        if self.type == self.CUSTOMER:
+            return True
+        return False
+
+    def is_tenant(self):
+        if self.type == self.TENANT:
+            return True
+        return False
+
+    def is_administrator(self):
+        if self.type == self.ADMINSTRATOR:
+            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
+
+
+Group.add_to_class('display_name', models.CharField(verbose_name=u'名称显示', max_length=80))
+Group.add_to_class('tenant', models.ForeignKey(Tenant, verbose_name=u'租户', on_delete=models.PROTECT, editable=False))
+Group.add_to_class('create_user', models.ForeignKey(User, verbose_name=u"创建人", on_delete=models.PROTECT, editable=False))

+ 6 - 0
apps/admin/__init__.py

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

+ 52 - 0
apps/admin/serializers.py

@@ -0,0 +1,52 @@
+# coding=utf-8
+
+from django.contrib.auth import get_user_model, authenticate
+
+from rest_framework import serializers
+from rest_framework_jwt.serializers import JSONWebTokenSerializer
+from rest_framework_jwt.settings import api_settings
+from apps.log.models import BizLog
+from apps.admin import admin_log
+from utils import get_remote_addr
+
+User = get_user_model()
+jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER
+jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER
+
+class AdminUserJWTSerializer(JSONWebTokenSerializer):
+    def validate(self, attrs):
+        credentials = {
+            self.username_field: attrs.get(self.username_field),
+            'password': attrs.get('password')
+        }
+
+        if all(credentials.values()):
+            user = authenticate(**credentials)
+
+            if user:
+                if not user.is_active:
+                    msg = u'禁用帐户,禁止登录'
+                    admin_log(user, BizLog.INSERT,u'禁用帐户[%s]尝试登录系统,IP[%s]' % (user.username, get_remote_addr(self.request)))
+                    raise serializers.ValidationError(msg)
+
+                if not user.is_administrator():
+                    msg = u'非管理员账号,禁止登录'
+                    admin_log(user, BizLog.INSERT,u'非管理员账号[%s]尝试登录系统,IP[%s]' % (user.username, get_remote_addr(self.request)))
+                    raise serializers.ValidationError(msg)
+
+                payload = jwt_payload_handler(user)
+                admin_log(user, BizLog.INSERT, u'[%s]登录系统,IP[%s]' % (user.username,get_remote_addr(self.request)))
+
+                return {
+                    'token': jwt_encode_handler(payload),
+                    'user_id': user.id,
+                    'username': user.username
+                }
+            else:
+                msg = u'账号或者密码错误!'
+                admin_log(None, BizLog.INSERT, u'登录失败[%s][%s],IP[%s]' % (attrs[self.username_field], attrs['password'], get_remote_addr(self.request)))
+                raise serializers.ValidationError(msg)
+        else:
+            msg = u'必须包含“{username field}”和“password'
+            msg = msg.format(username_field=self.username_field)
+            raise serializers.ValidationError(msg)

+ 0 - 0
apps/admin/tenant/__init__.py


+ 41 - 0
apps/admin/tenant/serializers.py

@@ -0,0 +1,41 @@
+# coding=utf-8
+from rest_framework import serializers
+from apps.tenant.models import Tenant
+from apps.tenant.employee.models import Employee
+from utils.exceptions import CustomError
+from apps.account.models import User
+
+class TenantSerializer(serializers.ModelSerializer) :
+    create_user_name = serializers.CharField(source='create_user.username', read_only=True)
+    create_time = serializers.DateTimeField(format='%Y-%m-%d %H:%M', read_only=True)
+    username = serializers.SerializerMethodField()
+
+    class Meta:
+        model = Tenant
+        fields = '__all__'
+
+    def get_username(self, obj):
+        names = Employee.objects.filter(tenant=obj, user__type=User.TENANT).values('name')
+        name = []
+        for i in names:
+            name.append(i.get('name'))
+        return ','.join(name)
+
+    def create(self, validated_data):
+        validated_data['create_user'] = self.context['request'].user
+        if Tenant.is_exist(validated_data['name']):
+            raise CustomError(u'名称为[%s]的租户已存在' % validated_data['name'])
+        instance = super(TenantSerializer, self).create(validated_data)
+        return instance
+
+    def update(self, instance, validated_data):
+        if instance.delete:
+            raise CustomError(u'租户[%s]已经被删除,禁止修改' % instance.name)
+        if Tenant.is_exist(validated_data['name'], instance.id):
+            raise CustomError(u'名称为[%s]的租户已存在' % validated_data['name'])
+
+        instance = super(TenantSerializer, self).update(instance, validated_data)
+        return instance
+
+
+

+ 11 - 0
apps/admin/tenant/urls.py

@@ -0,0 +1,11 @@
+# coding=utf-8
+
+from rest_framework.routers import SimpleRouter
+from .views import *
+
+urlpatterns = [
+]
+
+router = SimpleRouter()
+router.register(r'', TenantViewSet)
+urlpatterns += router.urls

+ 49 - 0
apps/admin/tenant/views.py

@@ -0,0 +1,49 @@
+# coding=utf-8
+
+from utils.custom_modelviewset import CustomModelViewSet
+from utils.permission import IsAdministratorUser
+from apps.tenant.models import Tenant
+from apps.tenant.employee.models import Employee
+from apps.admin import admin_log
+from apps.log.models import BizLog
+from .serializers import TenantSerializer
+from django.db import transaction
+from utils import response_ok
+from utils.exceptions import CustomError
+from rest_framework.decorators import action
+from apps.WechatTp.models import WechatTp
+
+class TenantViewSet(CustomModelViewSet):
+    permission_classes = [IsAdministratorUser, ]
+    queryset = Tenant.objects.all()
+    serializer_class = TenantSerializer
+
+    def perform_create(self, serializer):
+        super(TenantViewSet, self).perform_create(serializer)
+        instance = serializer.instance
+        validated_data = serializer.validated_data
+        admin_log(self.request.user, BizLog.INSERT, u'添加租户[%s] id=%d' % (instance.name, instance.id), validated_data)
+
+    def perform_update(self, serializer):
+        super(TenantViewSet, self).perform_update(serializer)
+        instance = serializer.instance
+        validated_data = serializer.validated_data
+        admin_log(self.request.user, BizLog.INSERT, u'修改租户[%s] id=%d' % (instance.name, instance.id), validated_data)
+
+    @action(methods=['post'], detail=True)
+    def super_tenant(self,  request, pk):
+        username = request.POST.get('username')
+        password = request.POST.get('password')
+        with transaction.atomic():
+            tenant = Tenant.objects.filter(pk=pk).first()
+            instance = Employee.create_admin(tenant, username, password)
+            admin_log(self.request.user, BizLog.INSERT, u'添加租户超级账号[%s], id=%d' % (instance.name, instance.id))
+        return response_ok()
+
+    @action(methods=['post'], detail=True)
+    def bind_wechat_tp(self, request, pk):
+        tp = WechatTp.getDefault()
+        if not tp:
+            raise CustomError(u'未找到微信第三方平台!')
+        url = tp.getAuthUrl(pk)
+        return response_ok(url)

+ 16 - 0
apps/admin/urls.py

@@ -0,0 +1,16 @@
+# coding=utf-8
+
+from django.conf.urls import url, include
+from .views import *
+
+urlpatterns = [
+    url(r'^token/obtain/$', AdminUserLoginView.as_view()),
+    url(r'^token_refresh/$', AdminUserRefreshTokenView.as_view()),
+    url(r'^token_verify/$', AdminUserVerifyTokenView.as_view()),
+
+    url(r'^user/', include('apps.admin.user.urls')),
+    url(r'^wechatapp/', include('apps.admin.wechatapplet.urls')),
+    url(r'^wechattp/', include('apps.admin.wechattp.urls')),
+
+    url(r'^tenant/', include('apps.admin.tenant.urls')),
+]

+ 0 - 0
apps/admin/user/__init__.py


+ 35 - 0
apps/admin/user/serializers.py

@@ -0,0 +1,35 @@
+# coding=utf-8
+
+from rest_framework import serializers
+from utils.exceptions import CustomError
+from apps.account.models import User
+from apps.tenant.employee.models import Employee
+
+class UserSerializer(serializers.ModelSerializer):
+    type = serializers.IntegerField(read_only=True)
+    password = serializers.CharField(write_only=True, allow_blank=True)
+
+    class Meta:
+        model = User
+        fields = '__all__'
+
+    def create(self, validated_data):
+        validated_data['type'] = User.ADMINSTRATOR
+        if validated_data['password'].strip() == '':
+            raise CustomError(u'密码不能为空!')
+        instance = super(UserSerializer, self).create(validated_data)
+        instance.set_password(validated_data['password'])
+        instance.save()
+        Employee.getOrRegister(user=instance,tenant=None)
+        return instance
+
+    def update(self, instance, validated_data):
+        instance.update_item(validated_data)
+        # if not 'password' in validated_data or not validated_data['password']:
+        #     validated_data['password'] = instance.password
+        #     instance = super(UserSerializer, self).update(instance, validated_data)
+        # else:
+        #     instance = super(UserSerializer, self).update(instance, validated_data)
+        #     instance.set_password(validated_data['password'])
+        # instance.save()
+        return instance

+ 13 - 0
apps/admin/user/urls.py

@@ -0,0 +1,13 @@
+# coding=utf-8
+
+from django.conf.urls import url
+from rest_framework.routers import SimpleRouter
+from .views import *
+
+urlpatterns = [
+    url(r'^change_password/$', ChangePasswrodView.as_view()),
+]
+
+router = SimpleRouter()
+router.register(r'', UserViewSet)
+urlpatterns += router.urls

+ 58 - 0
apps/admin/user/views.py

@@ -0,0 +1,58 @@
+# coding=utf-8
+
+from django.db import transaction
+from django.contrib.auth import get_user_model
+from rest_framework.views import APIView
+
+from utils import response_ok
+from utils.permission import IsAdministratorUser
+from utils.custom_modelviewset import CustomModelViewSet
+from apps.log.models import BizLog
+from apps.admin import admin_log
+
+from apps.account.filters import UserFilter
+from .serializers import UserSerializer
+
+User = get_user_model()
+
+class UserViewSet(CustomModelViewSet):
+    permission_classes = [IsAdministratorUser, ]
+    queryset = User.objects.filter(is_active=True, type=User.ADMINSTRATOR)
+    serializer_class = UserSerializer
+
+    def filter_queryset(self, queryset):
+        f = UserFilter(self.request.GET, queryset=queryset)
+        return f.qs
+
+    def perform_create(self, serializer):
+        super(UserViewSet, self).perform_create(serializer)
+        instance = serializer.instance
+        validated_data = serializer.validated_data
+        admin_log(self.request.user, BizLog.INSERT, u'添加用户[%s],id=%d' % (instance.username, instance.id),validated_data)
+
+    def perform_update(self, serializer):
+        super(UserViewSet, self).perform_update(serializer)
+        instance = serializer.instance
+        validated_data = serializer.validated_data
+        admin_log(self.request.user, BizLog.UPDATE, u'修改用户[%s],id=%d' % (instance.username, instance.id), validated_data)
+
+    def destroy(self, request, *args, **kwargs):
+        with transaction.atomic():
+            instance = self.get_object()
+            instance.is_active = False
+            instance.save()
+            admin_log(self.request.user, BizLog.DELETE, u'禁用用户[%s],id=%d' % (instance.username, instance.id))
+        return response_ok()
+
+class ChangePasswrodView(APIView):
+    permission_classes = [IsAdministratorUser, ]
+
+    def post(self, request):
+        new_password = request.POST.get('new_password')
+        confirm_password = request.POST.get('confirm_password')
+        old_password = request.POST.get('old_password')
+
+        with transaction.atomic():
+            request.user.change_password(new_password, confirm_password, old_password)
+            request.user.save()
+        return response_ok()

+ 41 - 0
apps/admin/views.py

@@ -0,0 +1,41 @@
+# coding=utf-8
+
+from django.contrib.auth import get_user_model
+from rest_framework_jwt.views import ObtainJSONWebToken, VerifyJSONWebToken, RefreshJSONWebToken
+from rest_framework.serializers import ValidationError
+from utils import response_error, response_ok
+from .serializers import AdminUserJWTSerializer
+
+User = get_user_model()
+
+class AdminUserLoginView(ObtainJSONWebToken):
+    serializer_class = AdminUserJWTSerializer
+
+    def post(self, request, *args, **kwargs):
+        try:
+            ser = self.serializer_class(data=request.data)
+            ser.request = request
+            if ser.is_valid(raise_exception=True):
+                return response_ok(ser.validated_data)
+        except ValidationError as e:
+            return response_error(e.detail['error'][0])
+
+
+class AdminUserVerifyTokenView(VerifyJSONWebToken):
+    def post(self, request, *args, **kwargs):
+        try:
+            ser = self.serializer_class(data=request.data)
+            if ser.is_valid(raise_exception=True):
+                return response_ok({'token': ser.validated_data['token']})
+        except ValidationError as e:
+            return response_error(u'登录状态失效,请重新登录[' + e.detail['error'][0] + ']')
+
+
+class AdminUserRefreshTokenView(RefreshJSONWebToken):
+    def post(self, request, *args, **kwargs):
+        try:
+            ser = self.serializer_class(data=request.data)
+            if ser.is_valid(raise_exception=True):
+                return response_ok({'token': ser.validated_data['token']})
+        except ValidationError as e:
+            return response_error(u'登录状态失效,请重新登录[' + e.detail['error'][0] + ']')

+ 0 - 0
apps/admin/wechatapplet/__init__.py


+ 19 - 0
apps/admin/wechatapplet/serializers.py

@@ -0,0 +1,19 @@
+# coding=utf-8
+
+from rest_framework import serializers
+from apps.WechatApplet.models import WechatApplet
+
+
+class WechatAppletSerializer(serializers.ModelSerializer):
+    audit_status_text = serializers.CharField(source='get_audit_status_display', read_only=True)
+    tenant_name = serializers.CharField(source='tenant.name', read_only=True)
+    authorize_text = serializers.SerializerMethodField()
+
+    def get_authorize_text(self, obj):
+        if obj.is_authorize:
+            return u'是'
+        return u'否'
+
+    class Meta:
+        model = WechatApplet
+        fields = '__all__'

+ 13 - 0
apps/admin/wechatapplet/urls.py

@@ -0,0 +1,13 @@
+# coding=utf-8
+
+from rest_framework.routers import SimpleRouter
+from django.conf.urls import url, include
+from .views import *
+
+urlpatterns = [
+    url(r'templates/$', TemplateListViewSet.as_view())
+]
+
+router = SimpleRouter()
+router.register(r'', WechatAppletViewSet)
+urlpatterns += router.urls

+ 126 - 0
apps/admin/wechatapplet/views.py

@@ -0,0 +1,126 @@
+# coding=utf-8
+
+import json
+from django.db import transaction
+from rest_framework.views import APIView
+from rest_framework.decorators import action
+
+from utils.custom_modelviewset import CustomModelViewSet
+from utils.permission import IsAdministratorUser
+from utils import response_ok
+from apps.log.models import BizLog
+from apps.admin import admin_log
+
+from .serializers import WechatAppletSerializer
+from apps.WechatApplet.filters import WechatAppletFilter
+from apps.WechatApplet.models import WechatApplet
+from apps.WechatTp.models import WechatTp
+
+
+class TemplateListViewSet(APIView):
+    permission_classes = [IsAdministratorUser, ]
+
+    def get(self, request):
+        tp = WechatTp.getDefault()
+        ret = tp.getTemplateList()
+        return response_ok(ret)
+
+
+class WechatAppletViewSet(CustomModelViewSet):
+    # permission_classes = [IsAdministratorUser, ]
+    queryset = WechatApplet.objects.filter()
+    serializer_class = WechatAppletSerializer
+
+    def filter_queryset(self, queryset):
+        f = WechatAppletFilter(self.request.GET, queryset=queryset)
+        return f.qs
+
+    @action(methods=['post'], detail=True)
+    def upload_code(self, request, pk):
+        # 上传代码
+        template_id = request.POST.get('template_id')
+        user_version = request.POST.get('user_version')
+        user_desc = request.POST.get('user_desc')
+        app = WechatApplet.getById(pk)
+        with transaction.atomic():
+            app.uploadCode(template_id, user_version, user_desc)
+            admin_log(request.user, BizLog.INSERT,u'小程序上传审核代码,id=%d' % app.id, request.POST.dict())
+        return response_ok()
+
+    @action(methods=['post'], detail=False)
+    def batch_upload_code(self, request):
+        # 批量上传代码
+        template_id = request.POST.get('template_id')
+        user_version = request.POST.get('user_version')
+        user_desc = request.POST.get('user_desc')
+        app_ids = json.loads(request.POST.get('ids'))
+
+        apps = WechatApplet.objects.filter(id__in=app_ids)
+        for app in apps:
+            try:
+                with transaction.atomic():
+                    app.uploadCode(template_id, user_version, user_desc)
+            except:
+                pass
+        admin_log(request.user, BizLog.INSERT, u'小程序批量上传审核代码', request.POST.dict())
+
+        return response_ok()
+
+    @action(methods=['post'], detail=False)
+    def refresh_audit_status(self, request):
+        # 查询最新一次提交代码的审核状态  多选更新 审核状态
+        app_ids = json.loads(request.POST.get('ids'))
+        for app_id in app_ids:
+            app = WechatApplet.getById(app_id)
+            app.refreshAuditStatus()
+        return response_ok()
+
+    @action(methods=['post'], detail=True)
+    def set_secret(self, request, pk):
+        secret = request.POST.get('secret')
+        app = WechatApplet.getById(pk)
+        with transaction.atomic():
+            app.secret = secret
+            app.save()
+            admin_log(request.user, BizLog.INSERT, u'设置小程序秘钥, id=%d' % app.id, secret)
+        return response_ok()
+
+    @action(methods=['post'], detail=True)
+    def set_merchant(self, request, pk):
+        tenant_num = request.POST.get('tenant_num')
+        tenant_key = request.POST.get('tenant_key')
+        app = WechatApplet.getById(pk)
+        with transaction.atomic():
+            app.tenant_num = tenant_num
+            app.tenant_key = tenant_key
+            app.save()
+            admin_log(request.user, BizLog.INSERT, u'设置小程序商户号和商户秘钥, id=%d' % app.id)
+        return response_ok()
+
+    @action(methods=['post'], detail=False)
+    def add_plugin(self, request):
+        app_ids = json.loads(request.POST.get('ids'))
+        for app_id in app_ids:
+            apps = WechatApplet.getById(app_id)
+            apps.addPlugin()
+        return response_ok()
+
+    @action(methods=['post'], detail=False)
+    def release(self, request):
+        app_ids = json.loads(request.POST.get('ids'))
+        for app_id in app_ids:
+            app = WechatApplet.getById(app_id)
+            app.releaseApplet()
+        return response_ok()
+
+    @action(methods=['post'], detail=True)
+    def upload_cert(self, request, pk):
+        file = request.FILES.get('file', None)
+        app = WechatApplet.getById(pk)
+        with transaction.atomic():
+            app.upload_cert_file(file)
+            admin_log(request.user, BizLog.INSERT, u'设置小程序企业支付api证书, id=%d' % app.id)
+        return response_ok()
+
+
+

+ 0 - 0
apps/admin/wechattp/__init__.py


+ 19 - 0
apps/admin/wechattp/serializers.py

@@ -0,0 +1,19 @@
+# coding=utf-8
+
+from rest_framework import serializers
+from apps.WechatTp.models import WechatTp
+
+
+class WechatTpSerializer(serializers.ModelSerializer):
+
+    class Meta:
+        model = WechatTp
+        fields = '__all__'
+
+    def create(self, validated_data):
+        instance = WechatTp.objects.filter().first()
+        if instance:
+            instance = super(WechatTpSerializer, self).update(instance, validated_data)
+        else:
+            instance = super(WechatTpSerializer, self).create(validated_data)
+        return instance

+ 12 - 0
apps/admin/wechattp/urls.py

@@ -0,0 +1,12 @@
+# coding=utf-8
+
+from rest_framework.routers import SimpleRouter
+from django.conf.urls import url, include
+from .views import *
+
+urlpatterns = [
+]
+
+router = SimpleRouter()
+router.register(r'', WechatTpViewSet)
+urlpatterns += router.urls

+ 29 - 0
apps/admin/wechattp/views.py

@@ -0,0 +1,29 @@
+# coding=utf-8
+
+from utils.custom_modelviewset import CustomModelViewSet
+from utils.permission import IsAdministratorUser
+from utils import response_ok
+from apps.log.models import BizLog
+from apps.admin import admin_log
+
+from .serializers import WechatTpSerializer
+from apps.WechatTp.models import WechatTp
+
+
+class WechatTpViewSet(CustomModelViewSet):
+    permission_classes = [IsAdministratorUser, ]
+    queryset = WechatTp.objects.filter()
+    serializer_class = WechatTpSerializer
+
+    def list(self, request, *args, **kwargs):
+        instance = WechatTp.objects.filter().first()
+        if not instance:
+            return response_ok()
+        serializer = self.get_serializer(instance)
+        return response_ok(serializer.data)
+
+    def perform_create(self, serializer):
+        super(WechatTpViewSet, self).perform_create(serializer)
+        instance = serializer.instance
+        validated_data = serializer.validated_data
+        admin_log(self.request.user, BizLog.INSERT, u'添加/修改用户第三方平台信息,id=%d' % instance.id, validated_data)

+ 0 - 0
apps/api/__init__.py


+ 10 - 0
apps/api/urls.py

@@ -0,0 +1,10 @@
+# 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()),
+]
+

+ 91 - 0
apps/api/views.py

@@ -0,0 +1,91 @@
+# coding=utf-8
+
+import json
+import xmltodict
+
+from django.db import transaction
+from django.http import HttpResponse
+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.tenant.biz import TenantBiz
+
+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')

+ 157 - 0
apps/base.py

@@ -0,0 +1,157 @@
+#coding=utf-8
+from utils.exceptions import CustomError
+import re
+import tablib
+import datetime
+from openpyxl.utils.exceptions import InvalidFileException
+
+class Formater():
+    @staticmethod
+    def formatStr(value):
+        res = u''
+        if value != None:
+            try:
+                res = str(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)
+
+class ExcelImporter():
+    def getExcelData(self,file):
+        if not file:
+            raise CustomError(u'请上传数据文件')
+        try:
+            data = tablib.import_set(file.read(), format='xlsx').dict
+            if not len(data):
+                raise CustomError(u'上传的文件内没有发现数据')
+            return data
+        except InvalidFileException:
+            raise CustomError(u'请上传<strong>xlsx</strong>格式的数据文件,老版本的xls格式不被支持!')
+
+    def validRow(self,row):
+        data = {}
+        for (k,v) in self.fields.items():
+            is_required = v[0]
+            format_proc = v[1]
+
+            try:
+                value = row[k]
+            except:
+                raise CustomError(u'缺少[%s]列,请检查模板文件或重新下载' % k)
+
+            if is_required and value == None:
+                raise CustomError(u'%s:为必填项' % k)
+
+            if format_proc and value != None:
+                try:
+                    value = format_proc(value)
+                except CustomError as e:
+                    raise CustomError(u'%s:错误的值[%s]' % (k,e.get_error_msg()))
+
+            data[k] = value
+        return data
+
+    @staticmethod
+    def formatUnicode(value):
+        try:
+            return str(value).strip(u' ')
+        except:
+            return ''
+
+    @staticmethod
+    def formatInt(value):
+        try:
+            return int(float(str(value).strip(u' ')))
+        except:
+            raise CustomError(u'不能转换为整数')
+
+    @staticmethod
+    def formatFloat(value):
+        try:
+            return round(float(str(value).strip(u' ')) or 0,2)
+        except:
+            raise CustomError(u'不能转换为小数')
+
+    @staticmethod
+    def formatFloatGtZ(value):
+        v = ExcelImporter.formatFloat(value)
+        if v <= 0:
+            raise CustomError(u'必须大于0')
+        return v
+
+    @staticmethod
+    def formatFloatGeZ(value):
+        v = ExcelImporter.formatFloat(value)
+        if v < 0:
+            raise CustomError(u'必须大于等于0')
+        return v
+
+    @staticmethod
+    def formatTel(value):
+        try:
+            value = str(value).strip(u' ')
+        except:
+            value = ''
+        if value == '':
+            return value
+        is_mobile = re.match(r'^0?(13[0-9]|14[0-9]|15[0-9]|17[0-9]|18[0-9]|19[0-9]|166)[0-9]{8}$', value)
+        is_tel = re.match(r'^([0-9]{3,4}-)?[0-9]{7,8}$', value)
+        if not (is_mobile or is_tel):
+            raise CustomError(u'不合法的手机号或者固话号')
+        return value
+
+    @staticmethod
+    def formatDate(value):
+        try:
+            return datetime.datetime.strptime(value, '%Y-%m-%d').date()
+        except:
+            raise CustomError(u'日期格式错误,格式为:2018-01-01')

+ 0 - 0
apps/dashboard/__init__.py


+ 9 - 0
apps/dashboard/views.py

@@ -0,0 +1,9 @@
+#coding=utf-8
+
+from django.http import HttpResponseRedirect
+
+def index(request):
+    return HttpResponseRedirect('/tenant/account/login.html')
+
+def admin_index(request):
+    return HttpResponseRedirect('/zzlyadmin/account/login.html')

+ 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


+ 58 - 0
apps/log/models.py

@@ -0,0 +1,58 @@
+# 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
+
+from apps.tenant.models import Tenant
+
+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
+    IMPORT = 4
+    TYPE_CHOICES = (
+        (INSERT, u'添加'),
+        (UPDATE, u'修改'),
+        (DELETE, u'删除'),
+        (IMPORT, 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)
+    tenant = models.ForeignKey(Tenant, verbose_name=u'租户', on_delete=models.PROTECT, null=True, blank=True)
+
+    objects = BizLogManager()
+
+    class Meta:
+        db_table = "system_log"
+        ordering = ['-id']
+        index_together = (
+            'create_time',
+            'type',
+        )
+        verbose_name = u"系统日志"
+        default_permissions = ()

+ 184 - 0
apps/serializer_errors.py

@@ -0,0 +1,184 @@
+#coding=utf-8
+
+from rest_framework import fields
+
+def dump_serializer_errors(serializer):
+    error_messages = {
+        u'blank': u'此字段不能为空',
+        u'required': u'此字段必填',
+        u'max_length':u'此字段不超过%d个字符',
+        u'min_length':u'此字段不少于%d个字符',
+        u'null': u'该字段不能为空'  # This field may not be null
+    }
+    field_error_messages = {
+        fields.BooleanField:{
+            u'invalid': u'此字段不是一个合法的布尔值'
+        },
+        fields.CharField: {
+            u'invalid': u'此字段不是一个合法的字符串'
+        },
+        fields.NullBooleanField: {
+            u'invalid': u'此字段不是一个合法的布尔值'
+        },
+        fields.EmailField: {
+            u'invalid': u'邮箱格式不正确'
+        },
+        fields.RegexField: {
+            u'invalid': u'此字段没有匹配的一个必要的模板'
+        },
+        fields.SlugField: {
+            u'invalid': u'此字段不是一个合法的slug,应该由字母,数字,下划线,连字符组成',
+            u'invalid_unicode': u'此字段不是一个合法的slug,应该由unicode字母,数字,下划线,连字符组成'
+        },
+        fields.URLField: {
+            u'invalid': u'此字段不是一个合法的URL'
+        },
+        fields.UUIDField: {
+            u'invalid': u'此字段不是一个合法的UUID'
+        },
+        fields.IPAddressField: {
+            u'invalid': u'此字段不是一个合法的IPv4或者IPv6地址'
+        },
+        fields.IntegerField: {
+            u'invalid': u'此字段不是一个合法的整数',
+            u'max_value': u'此字段的数值要小于等于%d',
+            u'min_value': u'此字段的数值要大于等于%d',
+            u'max_string_length': u'此字段长度超过1000',
+        },
+        fields.FloatField: {
+            u'invalid': u'此字段不是一个合法的浮点数',
+            u'max_value': u'此字段的数值要小于等于%d',
+            u'min_value': u'此字段的数值要大于等于%d',
+            u'max_string_length': u'此字段长度超过1000',
+        },
+        fields.DecimalField: {
+            u'invalid': u'此字段不是一个合法的数字',
+            u'max_value': u'此字段的数值要小于等于%d',
+            u'min_value': u'此字段的数值要大于等于%d',
+            u'max_digits': u'此字段的数值不超过%d位',
+            u'max_decimal_places': u'此字段十进制位不超过%d',
+            u'max_whole_digits': u'此字段小数点前的数字不超过%d。',
+            u'max_string_length': u'此字段长度超过1000',
+        },
+        fields.DateTimeField: {
+            u'invalid': u'日期时间格式不合法',
+            u'date': u'不能只输入日期,应输入日期和时间',
+            u'make_aware': u'时区%d不合法',
+            u'overflow': u'日期时间超出范围',
+        },
+        fields.DateField: {
+            u'invalid': u'日期格式不合法',
+            u'date': u'应输入日期,不能输入日期和时间',
+        },
+        fields.TimeField: {
+            u'invalid': u'时间格式不合法',
+        },
+        fields.DurationField: {
+            u'invalid': u'时间段格式不合法,应该用格式:%d',
+        },
+        fields.ChoiceField: {
+            u'invalid_choice': u'此字段不是一个合法的选项',
+        },
+        fields.MultipleChoiceField: {
+            u'invalid_choice': u'此字段不是一个合法的选项',
+            u'not_a_list': u'应输入数组(list)格式',
+            u'empty': u'此选项不能为空',
+        },
+        fields.FilePathField: {
+            u'invalid_choice': u'此字段不是一个合法的路径',
+        },
+        fields.FileField: {
+            u'required': u'没有提交文件',
+            u'invalid': u'提交的不是文件,请检查表单的编码类型',
+            u'no_name': u'没有文件名',
+            u'empty': u'提交的是一个空文件',
+            u'max_length': u'文件内容要小于%d个字符',
+        },
+        fields.ImageField: {
+            u'invalid_image': u'提交的不是图片文件或者图片文件损坏,请提交一个合法的图片文件',
+        },
+        fields.ListField: {
+            u'not_a_list': u'此字段应传入数组(list)类型',
+            u'empty': u'传入的list不能为空',
+            u'min_length': u'该字段至少要%d个元素',
+            u'max_length': u'该字段最多能有%d个元素',
+        },
+        fields.DictField: {
+            u'not_a_dict': u'此字段应传入字典(dict)类型',
+        },
+        fields.JSONField: {
+            u'invalid': u'此字段不是一个合法的JSON类型'
+        },
+        fields.ModelField: {
+            u'max_length': u'此字段长度要小于等于%d个字符'
+        }
+    }
+
+    result = [u'数据错误<br />', ]
+    for i in range(0, len(serializer.errors.keys())):
+        k = serializer.errors.keys()[i]
+        v = serializer.errors.values()[i]
+        field = serializer.fields.get(k)
+
+        label = field.label
+        if label:
+            label += ' - '
+        else:
+            label = ''
+
+        error_detail = v[0]
+        try:
+            msg = field_error_messages[type(field)][error_detail.code]
+            if type(field) == fields.IntegerField or type(field) == fields.FloatField:
+                if error_detail.code == u'max_value':
+                    msg = msg % field.max_value
+                if error_detail.code == u'min_value':
+                    msg = msg % field.min_value
+
+            if type(field) == fields.DecimalField:
+                if error_detail.code == u'max_value':
+                    msg = msg % field.max_value
+                if error_detail.code == u'min_value':
+                    msg = msg % field.min_value
+                if error_detail.code == u'max_digits':
+                    msg = msg % field.max_digits
+                if error_detail.code == u'max_decimal_places':
+                    msg = msg % field.decimal_places
+                if error_detail.code == u'max_whole_digits':
+                    msg = msg % field.max_whole_digits
+
+            if type(field) == fields.DateTimeField:
+                if error_detail.code == u'make_aware':
+                    msg = msg % field.timezone
+
+            if type(field) == fields.DurationField:
+                if error_detail.code == u'invalid':
+                    msg = msg % u'[DD] [HH:[MM:]]ss[.uuuuuu]'
+
+            if type(field) == fields.DateTimeField:
+                if error_detail.code == u'make_aware':
+                    msg = msg % field.timezone
+
+            if type(field) == fields.ListField:
+                if error_detail.code == u'min_length':
+                    msg = msg % field.min_length
+                if error_detail.code == u'max_length':
+                    msg = msg % field.max_length
+
+            if type(field) == fields.ModelField:
+                if error_detail.code == u'max_length':
+                    msg = msg % field.max_length
+
+        except:
+            try:
+                msg = error_messages[error_detail.code]
+                if error_detail.code == u'max_length':
+                    msg = msg % field.max_length
+                if error_detail.code == u'min_length':
+                    msg = msg % field.min_length
+
+            except:
+                msg = str(error_detail)
+        msg = label + msg
+        result.append(msg)
+    return u'<br />'.join(result)

+ 5 - 0
apps/tenant/__init__.py

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

+ 0 - 0
apps/tenant/area/__init__.py


+ 21 - 0
apps/tenant/area/filters.py

@@ -0,0 +1,21 @@
+# coding=utf-8
+
+import django_filters
+from .models import Area, Department
+
+
+class AreaFilter(django_filters.FilterSet):
+    name = django_filters.CharFilter(field_name='name', lookup_expr='icontains')
+    tenant = django_filters.CharFilter(field_name='tenant_id')
+
+    class Meta:
+        model = Area
+        fields = '__all__'
+
+class AreaDepartmentFilter(django_filters.FilterSet):
+    name = django_filters.CharFilter(field_name='name', lookup_expr='icontains')
+    area_name = django_filters.CharFilter(field_name='area__name', lookup_expr='icontains')
+
+    class Meta:
+        model = Department
+        fields = '__all__'

+ 0 - 0
apps/tenant/area/migrations/__init__.py


+ 47 - 0
apps/tenant/area/models.py

@@ -0,0 +1,47 @@
+from django.conf import settings
+from django.db import models
+
+from apps.tenant.models import Tenant
+
+
+class Area(models.Model):
+    name = models.CharField(max_length=100, verbose_name=u'区域名称')
+    tenant = models.ForeignKey(Tenant, verbose_name=u'租户', editable=False, on_delete=models.PROTECT)
+    notes = models.CharField(max_length=500, verbose_name=u"备注", null=True)
+    create_user = models.ForeignKey(settings.AUTH_USER_MODEL, verbose_name=u"创建人", on_delete=models.PROTECT,
+                                    editable=False)
+    create_time = models.DateTimeField(verbose_name='创建时间', auto_now_add=True)
+    sort = models.PositiveSmallIntegerField(verbose_name=u'排序', help_text=u'越小越靠前', default=0)
+    enable = models.BooleanField(verbose_name=u"在用", default=True)
+    delete = models.BooleanField(verbose_name=u"删除", default=False, editable=False)
+
+    class Meta:
+        db_table = 'area'
+        verbose_name = u'区域信息'
+        ordering = ['-id',  'sort', ]
+        default_permissions = ()
+        permissions = [
+            ('browse_area', u'查看'),
+            ('add_area', u'添加'),
+            ('delete_area', u'删除')
+        ]
+
+
+class Department(models.Model):
+    name = models.CharField(max_length=100, verbose_name=u'部门名称')
+    area = models.ForeignKey(Area, verbose_name=u'所属区域',  on_delete=models.PROTECT)
+    notes = models.CharField(max_length=500, verbose_name=u"备注", null=True)
+    create_user = models.ForeignKey(settings.AUTH_USER_MODEL, verbose_name=u"创建人", on_delete=models.PROTECT,
+                                    editable=False)
+    create_time = models.DateTimeField(verbose_name='创建时间', auto_now_add=True)
+    sort = models.PositiveSmallIntegerField(verbose_name=u'排序', help_text=u'越小越靠前', default=0)
+    enable = models.BooleanField(verbose_name=u"在用", default=True)
+    delete = models.BooleanField(verbose_name=u"删除", default=False, editable=False)
+
+    class Meta:
+        db_table = 'area_department'
+        verbose_name = u'区域部门'
+        ordering = ['-id', 'sort', ]
+        default_permissions = ()
+        permissions = [
+        ]

+ 41 - 0
apps/tenant/area/serializers.py

@@ -0,0 +1,41 @@
+# coding=utf-8
+
+from rest_framework import serializers
+from .models import Area,Department
+
+
+class AreaSerializer(serializers.ModelSerializer):
+    enable_text = serializers.SerializerMethodField()
+
+    def get_enable_text(self,obj):
+        if obj.enable:
+            return '是'
+        return '否'
+
+    class Meta:
+        model = Area
+        fields = '__all__'
+
+    def create(self, validated_data):
+        validated_data['tenant'] = self.context['request'].user.employee.tenant
+        validated_data['create_user'] = self.context['request'].user
+        instance = super(AreaSerializer, self).create(validated_data)
+        return instance
+
+class AreaDepartmentSerializer(serializers.ModelSerializer):
+    area_name = serializers.CharField(source='area.name', read_only=True)
+    enable_text = serializers.SerializerMethodField()
+
+    def get_enable_text(self,obj):
+        if obj.enable:
+            return '是'
+        return '否'
+
+    class Meta:
+        model = Department
+        fields = '__all__'
+
+    def create(self, validated_data):
+        validated_data['create_user'] = self.context['request'].user
+        instance = super(AreaDepartmentSerializer, self).create(validated_data)
+        return instance

+ 16 - 0
apps/tenant/area/urls.py

@@ -0,0 +1,16 @@
+# coding=utf-8
+
+
+from django.conf.urls import url, include
+from rest_framework.routers import SimpleRouter
+
+from .views import AreaViewSet, AreaDepartmentViewSet, DictView
+
+urlpatterns = [
+    url(r'dict/$', DictView.as_view()),
+]
+
+router = SimpleRouter()
+router.register(r'area', AreaViewSet)
+router.register(r'department', AreaDepartmentViewSet)
+urlpatterns += router.urls

+ 97 - 0
apps/tenant/area/views.py

@@ -0,0 +1,97 @@
+# coding=utf-8
+from django.db import transaction
+from rest_framework.views import APIView
+from utils import response_ok
+from utils.custom_modelviewset import CustomModelViewSet
+from utils.exceptions import CustomError
+from utils.permission import IsTenantUser, permission_required
+from apps.tenant import tenant_log
+from apps.log.models import BizLog
+from .serializers import AreaSerializer, AreaDepartmentSerializer
+from .models import Area, Department
+from .filters import AreaFilter, AreaDepartmentFilter
+
+class DictView(APIView):
+    permission_classes = [IsTenantUser, ]
+
+    def get(self, request):
+        tenant = request.user.employee.tenant
+        ret = {
+            'area': AreaSerializer(Area.objects.filter(delete=False, enable=True, tenant=tenant), many=True).data,
+            'department': AreaDepartmentSerializer(Department.objects.filter(delete=False, enable=True, area__tenant=tenant), many=True).data
+        }
+        return response_ok(ret)
+
+
+class AreaViewSet(CustomModelViewSet):
+    permission_classes = [IsTenantUser, ]
+    queryset = Area.objects.filter(delete=False)
+    serializer_class = AreaSerializer
+
+    @permission_required('area.browse_area')
+    def filter_queryset(self, queryset):
+        queryset = queryset.filter(tenant=self.request.user.employee.tenant)
+        f = AreaFilter(self.request.GET, queryset=queryset)
+        return f.qs
+
+    @permission_required('area.add_area')
+    def perform_create(self, serializer):
+        super(AreaViewSet, self).perform_create(serializer)
+        instance = serializer.instance
+        validated_data = serializer.validated_data
+        tenant_log(self.request.user.employee, BizLog.INSERT, u'添加区域[%s],id=%d' % (instance.name, instance.id),
+                   validated_data)
+
+    @permission_required('area.add_area')
+    def perform_update(self, serializer):
+        super(AreaViewSet, self).perform_update(serializer)
+        instance = serializer.instance
+        validated_data = serializer.validated_data
+        tenant_log(self.request.user.employee, BizLog.UPDATE, u'修改区域[%s],id=%d' % (instance.name, instance.id),
+                   validated_data)
+
+    @permission_required('area.delete_area')
+    def destroy(self, request, *args, **kwargs):
+        with transaction.atomic():
+            instance = self.get_object()
+            instance.delete = True
+            instance.save()
+            tenant_log(self.request.user.employee, BizLog.DELETE, u'删除区域[%s],id=%d' % (instance.name, instance.id))
+        return response_ok()
+
+
+class AreaDepartmentViewSet(CustomModelViewSet):
+    permission_classes = [IsTenantUser, ]
+    queryset = Department.objects.filter(delete=False,area__delete=False)
+    serializer_class = AreaDepartmentSerializer
+
+    @permission_required('area.browse_area')
+    def filter_queryset(self, queryset):
+        queryset = queryset.filter(area__tenant=self.request.user.employee.tenant)
+        f = AreaDepartmentFilter(self.request.GET, queryset=queryset)
+        return f.qs
+
+    @permission_required('area.add_area')
+    def perform_create(self, serializer):
+        super(AreaDepartmentViewSet, self).perform_create(serializer)
+        instance = serializer.instance
+        validated_data = serializer.validated_data
+        tenant_log(self.request.user.employee, BizLog.INSERT, u'添加区域部门[%s],id=%d' % (instance.name, instance.id),
+                   validated_data)
+
+    @permission_required('area.add_area')
+    def perform_update(self, serializer):
+        super(AreaDepartmentViewSet, self).perform_update(serializer)
+        instance = serializer.instance
+        validated_data = serializer.validated_data
+        tenant_log(self.request.user.employee, BizLog.UPDATE, u'修改区域部门[%s],id=%d' % (instance.name, instance.id),
+                   validated_data)
+
+    @permission_required('area.delete_area')
+    def destroy(self, request, *args, **kwargs):
+        with transaction.atomic():
+            instance = self.get_object()
+            instance.delete = True
+            instance.save()
+            tenant_log(self.request.user.employee, BizLog.DELETE, u'删除区域[%s]部门[%s],id=%d' % (instance.area.name,instance.name, instance.id))
+        return response_ok()

+ 19 - 0
apps/tenant/biz.py

@@ -0,0 +1,19 @@
+# coding=utf-8
+
+from apps.WechatApplet.models import WechatApplet
+from apps.WechatTp.models import WechatTp
+from utils.exceptions import CustomError
+
+class TenantBiz():
+
+    @staticmethod
+    def bindWechatApplet(tenant, auth_code):
+        tp = WechatTp.getDefault()
+        if tenant.delete:
+            raise CustomError(u'该租户已被删除!')
+        if tenant.is_bind_app:
+            raise CustomError(u'该租户已绑定小程序!')
+        app = WechatApplet.addAuthorizer(tp, auth_code, tenant)
+        tenant.is_bind_app = True
+        tenant.save()
+        return app

+ 0 - 0
apps/tenant/building/__init__.py


+ 11 - 0
apps/tenant/building/filters.py

@@ -0,0 +1,11 @@
+# coding=utf-8
+
+import django_filters
+from .models import Building
+
+class BuildingFilter(django_filters.FilterSet):
+    building = django_filters.CharFilter(field_name='building', lookup_expr='icontains')
+
+    class Meta:
+        model = Building
+        fields = '__all__'

+ 0 - 0
apps/tenant/building/migrations/__init__.py


+ 27 - 0
apps/tenant/building/models.py

@@ -0,0 +1,27 @@
+from django.db import models
+from utils.exceptions import CustomError
+from apps.tenant.area.models import Area
+from django.conf import settings
+
+class Building(models.Model):
+    building = models.CharField(verbose_name=u'楼宇', max_length=100)
+    floor = models.CharField(verbose_name=u'楼层',max_length=100)
+    location = models.CharField(verbose_name='地点', max_length=100)
+    area = models.ForeignKey(Area, verbose_name=u'区域信息', on_delete=models.PROTECT, editable=False)
+    create_user = models.ForeignKey(settings.AUTH_USER_MODEL, verbose_name=u"创建人", on_delete=models.PROTECT,
+                                    editable=False)
+    create_time = models.DateTimeField(verbose_name='创建时间', auto_now_add=True)
+
+    class Meta:
+        db_table = 'building'
+        ordering = ['-id']
+        verbose_name = u'建筑信息'
+        default_permissions = ()
+        permissions = [
+            ('browse_building', u'查看'),
+            ('add_building', u'添加'),
+            ('delete_building', u'删除'),
+        ]
+
+    def get_full_name(self):
+        return self.area.name+'-' +self.building + self.floor + self.location

+ 11 - 0
apps/tenant/building/resources.py

@@ -0,0 +1,11 @@
+# -*- coding: utf-8 -*-
+
+from apps.base import ExcelImporter
+
+class BuildingImporter(ExcelImporter):
+    fields = {
+        u'区域': (True, ExcelImporter.formatUnicode),
+        u'楼宇': (True, ExcelImporter.formatUnicode),
+        u'楼层': (True, ExcelImporter.formatUnicode),
+        u'地点': (True, ExcelImporter.formatUnicode),
+    }

+ 29 - 0
apps/tenant/building/serializer.py

@@ -0,0 +1,29 @@
+from rest_framework import serializers
+from .models import Building
+
+
+class BuildingDetailSerializer(serializers.ModelSerializer):
+    area_name = serializers.CharField(source='area.name', read_only=True)
+
+    class Meta:
+        model = Building
+        fields = ('id', 'area_name', 'building', 'floor', 'location')
+
+
+class BuildingSerializer(serializers.ModelSerializer):
+    area_name = serializers.CharField(source='area.name', read_only=True)
+
+    class Meta:
+        model = Building
+        fields = '__all__'
+
+    def create(self, validated_data):
+        validated_data['area_id'] = self.context['request'].data['area']
+        validated_data['create_user'] = self.context['request'].user
+        instance = super(BuildingSerializer, self).create(validated_data)
+        return instance
+
+    def update(self, instance, validated_data):
+        validated_data['area_id'] = self.context['request'].data['area']
+        instance = super(BuildingSerializer, self).update(instance, validated_data)
+        return instance

+ 12 - 0
apps/tenant/building/urls.py

@@ -0,0 +1,12 @@
+from rest_framework.routers import SimpleRouter
+from django.conf.urls import url
+from .views import BuildingViewSet, BuildingImportView,LocationSearch
+
+urlpatterns = [
+    url(r'import/$', BuildingImportView.as_view()),
+    url(r'location/search/$', LocationSearch.as_view()),
+]
+
+router = SimpleRouter()
+router.register(r'', BuildingViewSet)
+urlpatterns += router.urls

+ 109 - 0
apps/tenant/building/views.py

@@ -0,0 +1,109 @@
+# coding=utf-8
+
+from utils.custom_modelviewset import CustomModelViewSet
+from rest_framework.views import APIView
+from django.db.models import Q
+import traceback
+from .models import Building
+from utils.permission import IsTenantUser, permission_required
+from .serializer import BuildingSerializer
+from apps.log.models import BizLog
+from apps.tenant import tenant_log
+from .filters import BuildingFilter
+from django.db import transaction
+from utils.exceptions import CustomError
+from utils import response_ok, response_error
+from .resources import BuildingImporter
+from apps.tenant.area.models import Area
+
+class LocationSearch(APIView):
+    permission_classes = [IsTenantUser, ]
+
+    def get(self, request):
+        keyword = request.GET.get('keywords')
+        tenant = request.user.employee.tenant
+        rows = Building.objects.filter(area__tenant=tenant)
+        if keyword:
+            rows = rows.filter(
+                Q(building__icontains=keyword) |
+                Q(location__icontains=keyword) |
+                Q(floor__icontains=keyword)
+            )
+        serializer = BuildingSerializer(rows, many=True)
+        return response_ok(serializer.data)
+
+class BuildingViewSet(CustomModelViewSet):
+    permission_classes = [IsTenantUser, ]
+    queryset = Building.objects.filter()
+    serializer_class = BuildingSerializer
+
+    @permission_required('building.browse_building')
+    def filter_queryset(self, queryset):
+        queryset = queryset.filter(area__tenant=self.request.user.employee.tenant)
+        f = BuildingFilter(self.request.GET, queryset=queryset)
+        return f.qs
+
+    @permission_required('building.add_building')
+    def perform_create(self, serializer):
+        super(BuildingViewSet, self).perform_create(serializer)
+        instance = serializer.instance
+        validated_data = serializer.validated_data
+        tenant_log(self.request.user.employee, BizLog.INSERT, u'添加建筑信息[%s],id=%d' % (instance.building, instance.id),
+                   validated_data)
+
+    @permission_required('building.add_building')
+    def perform_update(self, serializer):
+        super(BuildingViewSet, self).perform_update(serializer)
+        instance = serializer.instance
+        validated_data = serializer.validated_data
+        tenant_log(self.request.user.employee, BizLog.UPDATE, u'修改建筑信息[%s],id=%d' % (instance.building, instance.id),
+                   validated_data)
+
+    @permission_required('building.delete_building')
+    def destroy(self, request, *args, **kwargs):
+        with transaction.atomic():
+            instance = self.get_object()
+            if instance.area.tenant != request.user.employee.tenant:
+                raise CustomError(u'禁止跨租户操作!')
+            super(BuildingViewSet,self).destroy(self, request, *args, **kwargs)
+            tenant_log(self.request.user.employee, BizLog.DELETE, u'删除建筑信息[%s],id=%d' % (instance.building, instance.id))
+        return response_ok()
+
+class BuildingImportView(APIView):
+    permission_classes = [IsTenantUser, ]
+
+    def post(self, request):
+        if not request.user.has_perm('building.add_building'):
+            raise CustomError(u"您没有[建筑信息-添加]权限,无法执行该操作,请联系管理员分配权限!")
+        file = request.FILES.get('excel_file')
+        try:
+            line = 2
+            importer = BuildingImporter()
+            excel_rows = importer.getExcelData(file)
+            with transaction.atomic():
+                for excel_row in excel_rows:
+                    try:
+                        row = importer.validRow(excel_row)
+                        area_name = row[u'区域']
+                        area = Area.objects.filter(tenant=request.user.employee.tenant,name=area_name).first()
+                        if not area :
+                            raise CustomError(u'第%d行区域名称不正确:%s' % (line, area_name))
+                        data = {}
+                        data['area'] = area
+                        data['building'] = row[u'楼宇']
+                        data['floor'] = row[u'楼层']
+                        data['location'] = row[u'地点']
+                        data['create_user'] = request.user
+                        Building.objects.create(**data)
+                    except CustomError as e:
+                        raise CustomError(u'第%d行:%s' % (line, e.get_error_msg()))
+                    except Exception as e:
+                        raise CustomError(u'第%d行:%s' % (line, str(e)))
+                    line += 1
+                tenant_log(self.request.user.employee, BizLog.IMPORT, u"导入建筑信息[%s]条" % (line-2))
+        except CustomError as e:
+            return response_error(e.get_error_msg())
+        except Exception as e:
+            traceback.print_exc()
+            return response_error(u'导入失败!')
+        return response_ok()

+ 0 - 0
apps/tenant/config/__init__.py


+ 12 - 0
apps/tenant/config/filters.py

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

+ 0 - 0
apps/tenant/config/migrations/__init__.py


+ 68 - 0
apps/tenant/config/models.py

@@ -0,0 +1,68 @@
+# coding=utf-8
+
+from django.db import models
+from django.conf import settings
+from PIL import Image
+from apps.tenant.models import Tenant
+from utils.file_operation import UploadFile, DeleteFile
+
+config_file = 'config/'
+class Config(models.Model):
+    KEY_REPAIRS_PERSON = 'repairs_person'  #报修人员
+    KEY_SEND_ORDER_TYPE = 'send_order_type'  #派单方式
+    WXAPP_HOME_IMG = 'wxapp_home_img'  #小程序首页图片
+
+    tenant = models.ForeignKey(Tenant, verbose_name=u'租户', on_delete=models.PROTECT, editable=False)
+    property = models.CharField(max_length=100, verbose_name=u'属性')
+    value = models.TextField(verbose_name=u'值')
+
+    class Meta:
+        db_table = "system_config"
+        verbose_name = u"基础设置"
+        index_together = (
+            'property',
+        )
+        default_permissions = ()
+        permissions = [
+            ('browse_config', u'查看'),
+            ('set_config', u'设置'),
+        ]
+
+    @staticmethod
+    def getRepairsPerson(tenant_id):
+        try:
+            row = Config.objects.get(property=Config.KEY_REPAIRS_PERSON, tenant_id=tenant_id)
+            return row.value
+        except:
+            return ''
+
+    @staticmethod
+    def getSendOrderType(tenant_id):
+        try:
+            row = Config.objects.get(property=Config.KEY_SEND_ORDER_TYPE, tenant_id=tenant_id)
+            return row.value
+        except:
+            return ''
+
+    @staticmethod
+    def add_img(employee, property, file):
+        path = '{0}{1}/{2}/'.format(config_file,property,employee.user.id)
+        filename = UploadFile(file, path)
+        fullname = "%s/%s" % (settings.MEDIA_ROOT, filename)
+
+        try:
+            img = Image.open(fullname)
+            x, y = img.size
+            if x > 1440:
+                img = img.resize((1440, int((y / x) * 1440)), Image.ANTIALIAS)
+                img.save(fullname)
+        except:
+            pass
+
+        return filename
+
+    @staticmethod
+    def del_images(instance):
+        picture = instance.value
+        instance.delete()
+        DeleteFile(picture)

+ 17 - 0
apps/tenant/config/serializers.py

@@ -0,0 +1,17 @@
+# coding=utf-8
+
+from rest_framework import serializers
+from apps.tenant.config.models import Config
+from django.conf import settings
+
+class ConfigSerializer(serializers.ModelSerializer):
+    url = serializers.SerializerMethodField()
+
+    def get_url(self, obj):
+        if obj.property in [Config.WXAPP_HOME_IMG]:
+            return '%s%s' % (settings.MEDIA_URL, obj.value)
+        return ''
+
+    class Meta:
+        model = Config
+        fields = '__all__'

+ 13 - 0
apps/tenant/config/urls.py

@@ -0,0 +1,13 @@
+# coding=utf-8
+
+from rest_framework.routers import SimpleRouter
+
+from .views import *
+
+urlpatterns = [
+
+]
+
+router = SimpleRouter()
+router.register(r'', ConfigViewSet)
+urlpatterns += router.urls

+ 50 - 0
apps/tenant/config/views.py

@@ -0,0 +1,50 @@
+# coding=utf-8
+import json
+from django.db import transaction
+
+from utils.permission import IsTenantUser, permission_required
+from .serializers import ConfigSerializer
+from utils.custom_modelviewset import CustomModelViewSet
+from .models import Config
+from .filters import ConfigFilter
+from utils.exceptions import CustomError
+from apps.tenant import tenant_log
+from apps.log.models import BizLog
+from utils import response_ok, response_error
+
+class ConfigViewSet(CustomModelViewSet):
+    permission_classes = [IsTenantUser, ]
+    queryset = Config.objects.filter()
+    serializer_class = ConfigSerializer
+
+    @permission_required('config.browse_config')
+    def filter_queryset(self, queryset):
+        queryset = queryset.filter(tenant=self.request.user.employee.tenant)
+        f = ConfigFilter(self.request.GET, queryset=queryset)
+        return f.qs
+
+    @permission_required('config.set_config')
+    def create(self, request):
+        data = json.loads(request.POST.get('data'))
+        wxapp_home_img = request.FILES.get('wxapp_home_img')
+        keys = ['repairs_person', 'send_order_type']
+        with transaction.atomic():
+            for item in data:
+                config = Config.objects.filter(property=item['key'], tenant=self.request.user.employee.tenant).first()
+                if item['key'] not in keys:
+                    raise CustomError(u'系统设置属性[%s]不存在' % item['key'])
+                if config:
+                    config.value = item['value']
+                    config.save()
+                else:
+                    Config.objects.create(tenant=self.request.user.employee.tenant, property=item['key'],
+                                                   value=item['value'])
+            if wxapp_home_img:
+                old_config =  Config.objects.filter(property='wxapp_home_img', tenant=self.request.user.employee.tenant).first()
+                if old_config:
+                    old_config.del_images(old_config)
+                picture = Config.add_img(request.user.employee, 'wxapp_home_img', wxapp_home_img)
+                Config.objects.create(tenant=self.request.user.employee.tenant, property='wxapp_home_img',
+                                      value=picture)
+            tenant_log(self.request.user.employee, BizLog.INSERT, u'修改系统设置', data)
+        return response_ok()

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


+ 15 - 0
apps/tenant/employee/filters.py

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

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


+ 91 - 0
apps/tenant/employee/models.py

@@ -0,0 +1,91 @@
+# coding=utf-8
+
+from django.db import models
+from django.conf import settings
+from apps.account.models import User
+from apps.tenant.models import Tenant
+from apps.tenant.area.models import Department
+from apps.tenant.option.models import Option
+from utils.exceptions import CustomError
+from django.db import transaction
+
+class Employee(models.Model):
+    LEAVE_UNUSED = 1
+    WORKING = 2
+    DAY_OFF = 3
+    LEAVE_OFFICE = 4
+    STATUS_CHOICES = (
+        (LEAVE_UNUSED, u'在职'),
+        (WORKING, u'工作中'),
+        (DAY_OFF, u'调休'),
+        (LEAVE_OFFICE, u'离职'),
+    )
+    EMPLOYEE = 1
+    VISITOR = 2
+    TYPE_CHOICES = (
+        (EMPLOYEE, u'职工'),
+        (VISITOR, 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'性别', default=settings.MALE)
+    status = models.PositiveSmallIntegerField(choices=STATUS_CHOICES, verbose_name=u'状态',default=LEAVE_UNUSED)
+    user = models.OneToOneField(settings.AUTH_USER_MODEL, editable=False, related_name='employee',
+                                on_delete=models.PROTECT, verbose_name=u'职工', null=True)
+    position = models.ForeignKey(Option, verbose_name=u'职位', on_delete=models.PROTECT, null=True)
+    department = models.ForeignKey(Department, verbose_name=u'部门', on_delete=models.PROTECT, null=True)
+    create_user = models.ForeignKey(settings.AUTH_USER_MODEL, verbose_name=u"创建人", on_delete=models.PROTECT,
+                                    editable=False, related_name='create_user',null=True)
+    face = models.CharField(max_length=200, verbose_name=u'头像', null=True)
+    baoxiu_count = models.IntegerField(verbose_name='报修次数', default=0)
+    forbid_baoxiu = models.BooleanField(verbose_name='是否禁用报修', default=False)
+    tenant = models.ForeignKey(Tenant, verbose_name=u'租户', editable=False, on_delete=models.PROTECT,null=True)
+    type = models.PositiveSmallIntegerField(verbose_name=u'人员类型', choices=TYPE_CHOICES, default=VISITOR)
+
+    class Meta:
+        db_table = "employee"
+        verbose_name = u"员工管理"
+        ordering = ['-id']
+        index_together = (
+            'name',
+            'tel',
+        )
+        default_permissions = ()
+        permissions = [
+            ('browse_employee', u'查看'),
+            ('add_employee', u'添加'),
+            ('delete_employee', u'删除'),
+        ]
+
+    @staticmethod
+    def create_admin(tenant, username, password):
+        user = User.objects.create_superuser_tenant(username, password)
+        instance = Employee.objects.create(
+            name=username,
+            tel='',
+            gender=settings.MALE,
+            user=user,
+            tenant=tenant,
+            type=Employee.EMPLOYEE,
+        )
+        return instance
+
+    @staticmethod
+    def get_instance_by_id(id, tenant):
+        try:
+            id = int(id)
+        except:
+            raise CustomError('无效的employee_id')
+        instance = Employee.objects.filter(pk=id, tenant=tenant).first()
+        if not instance:
+            raise CustomError('未找到对应的员工实例')
+        return instance
+
+    @staticmethod
+    def getOrRegister(tenant, user):
+        customer = Employee.objects.filter(tenant=tenant, user=user).first()
+        if customer:
+            return customer
+        with transaction.atomic():
+            customer = Employee.objects.create(user=user,type=Employee.VISITOR, tel=user.username, name=user.username, tenant=tenant)
+        return customer

+ 106 - 0
apps/tenant/employee/serializers.py

@@ -0,0 +1,106 @@
+# coding=utf-8
+
+import json
+from rest_framework import serializers
+from apps.tenant.employee.models import Employee
+
+from utils.exceptions import CustomError
+from apps.account.models import User
+
+
+class EmployeeSerializer(serializers.ModelSerializer):
+    gender_text = serializers.CharField(source='get_gender_display', read_only=True)
+    status_text = serializers.CharField(source='get_status_display', read_only=True)
+    create_time = serializers.DateTimeField(source='user.date_joined', read_only=True)
+    username = serializers.CharField(source='user.username', read_only=True)
+    position_text = serializers.CharField(source='position.name', read_only=True)
+    area_text = serializers.SerializerMethodField()
+    department_text = serializers.CharField(source='department.name', read_only=True)
+    area = serializers.SerializerMethodField()
+    group_ids = serializers.PrimaryKeyRelatedField(source='user.groups', many=True, read_only=True)
+    groups = serializers.SerializerMethodField()
+    forbid_baoxiu_text = serializers.SerializerMethodField()
+    type_text = serializers.CharField(source='get_type_display', read_only=True)
+
+    def get_area_text(self, obj):
+        if obj.department:
+            return obj.department.area.name
+        return ''
+
+    def get_area(self, obj):
+        if obj.department:
+            return obj.department.area.id
+        return ''
+
+    def get_forbid_baoxiu_text(self, obj):
+        if obj.forbid_baoxiu:
+            return u'是'
+        return u'否'
+
+    def get_groups(self, obj):
+        return ','.join(obj.user.groups.values_list('display_name', flat=True))
+
+    class Meta:
+        model = Employee
+        fields = '__all__'
+
+    def create(self, validated_data):
+        username = self.initial_data['tel']
+        # 判断用户是否已经注册登陆过
+        password = None
+        if self.initial_data['password']:
+            password = self.initial_data['password']
+        user = User.objects.filter(username=username).first()
+        if not user:
+            # 创建user实例
+            is_active = 1 if self.initial_data['status'] != '4' else 0
+            user = User.objects.create_tenant(username, password, is_active=is_active)
+
+        validated_data['user'] = user
+        validated_data['create_user'] = self.context['request'].user
+        validated_data['tenant'] = self.context['request'].user.employee.tenant
+        validated_data['type'] = Employee.EMPLOYEE
+        try:
+            instance = super(EmployeeSerializer, self).create(validated_data)
+        except Exception as e:
+            if e.args[0] == 1062:
+                raise CustomError(u'该手机号已被游客或者其他员工使用,请检查!')
+
+        groups = self.context['request'].data.get('groups', None)
+        if groups:
+            groups = json.loads(groups)
+        else:
+            groups = []
+        for group in groups:
+            instance.user.groups.add(group)
+        return instance
+
+    def update(self, instance, validated_data):
+        instance.user.groups.clear()
+        groups = self.context['request'].data.get('groups', None)
+        if groups:
+            groups = json.loads(groups)
+        else:
+            groups = []
+        for group in groups:
+            instance.user.groups.add(group)
+        if instance.tenant != self.context['request'].user.employee.tenant:
+            raise CustomError(u'禁止跨租户操作!')
+        username = self.initial_data['tel']
+        if username and self.initial_data['password'].strip() != '':
+            if instance.user:
+                user = {
+                    'username': username,
+                    'password': self.initial_data['password'],
+                    'is_active': 1 if self.initial_data['status'] != '4' else 0,
+                    'type':2,
+                }
+                instance.user.update_item(user)
+            else:
+                is_active = 1 if self.initial_data['status'] != '4' else 0
+                user = User.objects.create_tenant(username,
+                                                  self.initial_data['password'],
+                                                  is_active=is_active)
+                validated_data['user'] = user
+        instance = super(EmployeeSerializer, self).update(instance, validated_data)
+        return instance

+ 14 - 0
apps/tenant/employee/urls.py

@@ -0,0 +1,14 @@
+# coding=utf-8
+from django.conf.urls import url, include
+from rest_framework.routers import SimpleRouter
+
+from .views import EmployeeViewSet, DictView, VisitorView
+
+urlpatterns = [
+    url(r'dict/$', DictView.as_view()),
+]
+
+router = SimpleRouter()
+router.register(r'visitor', VisitorView)
+router.register(r'', EmployeeViewSet)
+urlpatterns += router.urls

+ 88 - 0
apps/tenant/employee/views.py

@@ -0,0 +1,88 @@
+# coding=utf-8
+from rest_framework.views import APIView
+from django.db import transaction
+from utils import response_ok, response_error
+from utils.custom_modelviewset import CustomModelViewSet
+from utils.permission import IsTenantUser, permission_required
+from apps.tenant.employee.models import Employee
+from apps.tenant import tenant_log
+from apps.log.models import BizLog
+from apps.tenant.employee.filters import EmployeeFilter
+from .serializers import EmployeeSerializer
+from apps.tenant.option.serializers import OptionSerializer,Option
+from apps.tenant.area.models import Area,Department
+from apps.tenant.area.serializers import AreaSerializer,AreaDepartmentSerializer
+from rest_framework.decorators import action
+from utils.exceptions import CustomError
+
+class DictView(APIView):
+    permission_classes = [IsTenantUser, ]
+
+    def get(self, request):
+        tenant = request.user.employee.tenant
+        ret = {
+            'status': Employee.STATUS_CHOICES,  # 职工状态
+            'position': OptionSerializer(Option.objects.filter(delete=False,type=Option.JOB_TYPE, enable=True, tenant=tenant), many=True).data,
+            'area': AreaSerializer(Area.objects.filter(delete=False, enable=True, tenant=tenant), many=True).data,
+            'department': AreaDepartmentSerializer(Department.objects.filter(delete=False, enable=True, area__tenant=tenant), many=True).data,
+        }
+        return response_ok(ret)
+
+class VisitorView(CustomModelViewSet):
+    permission_classes = [IsTenantUser]
+    queryset = Employee.objects.filter()
+    serializer_class = EmployeeSerializer
+
+    @permission_required('employee.browse_employee')
+    def filter_queryset(self, queryset):
+        queryset = queryset.filter(tenant=self.request.user.employee.tenant)
+        f = EmployeeFilter(self.request.GET, queryset=queryset)
+        return f.qs
+
+    @action(methods=['post'], detail=True)
+    def forbid_baoxiu(self, request, pk):
+        forbid_baoxiu = request.POST.get('forbid_baoxiu')
+        instance = Employee.objects.filter(id=pk).first()
+        if instance:
+            instance.forbid_baoxiu = forbid_baoxiu and True or False
+            instance.save()
+        else:
+            return response_error(u'游客信息有误,请刷新重试。')
+        return response_ok()
+
+class EmployeeViewSet(CustomModelViewSet):
+    permission_classes = [IsTenantUser]
+    queryset = Employee.objects.filter()
+    serializer_class = EmployeeSerializer
+
+    @permission_required('employee.browse_employee')
+    def filter_queryset(self, queryset):
+        queryset = queryset.filter(tenant=self.request.user.employee.tenant)
+        f = EmployeeFilter(self.request.GET, queryset=queryset)
+        return f.qs
+
+    @permission_required('employee.add_employee')
+    def perform_create(self, serializer):
+        super(EmployeeViewSet, self).perform_create(serializer)
+        instance = serializer.instance
+        validated_data = serializer.validated_data
+        tenant_log(self.request.user.employee, BizLog.INSERT, u'添加人员[%s],id=%d' % (instance.name, instance.id),
+                   validated_data)
+
+    @permission_required('employee.add_employee')
+    def perform_update(self, serializer):
+        super(EmployeeViewSet, self).perform_update(serializer)
+        instance = serializer.instance
+        validated_data = serializer.validated_data
+        tenant_log(self.request.user.employee, BizLog.UPDATE, u'修改人员[%s],id=%d' % (instance.name, instance.id),
+                   validated_data)
+
+    @permission_required('employee.delete_employee')
+    def destroy(self, request, *args, **kwargs):
+        with transaction.atomic():
+            instance = self.get_object()
+            if instance.tenant != request.user.employee.tenant:
+                raise CustomError(u'禁止跨租户操作!')
+            super(EmployeeViewSet, self).destroy(self, request, *args, **kwargs)
+        tenant_log(self.request.user.employee, BizLog.DELETE, u'删除人员[%s],id=%d' % (instance.name, instance.id))
+        return response_ok()

+ 0 - 0
apps/tenant/equipment/__init__.py


+ 15 - 0
apps/tenant/equipment/filters.py

@@ -0,0 +1,15 @@
+# coding=utf-8
+
+import django_filters
+
+from .models import Equipment
+
+
+class EquipmentFilter(django_filters.FilterSet):
+    name = django_filters.CharFilter(field_name='name', lookup_expr='icontains')
+    type = django_filters.CharFilter(field_name='type__name', lookup_expr='icontains')
+    supplier = django_filters.CharFilter(field_name='supplier__name', lookup_expr='icontains')
+
+    class Meta:
+        model = Equipment
+        fields = '__all__'

+ 0 - 0
apps/tenant/equipment/migrations/__init__.py


+ 22 - 0
apps/tenant/equipment/models.py

@@ -0,0 +1,22 @@
+
+from django.db import models
+from apps.tenant.option.models import Option
+from apps.tenant.building.models import Building
+
+class Equipment(models.Model):
+    name = models.CharField(verbose_name=u'设备名称', max_length=100)
+    quality_date = models.DateField(verbose_name=u'质保到期', null=True)
+    location = models.ForeignKey(Building, verbose_name=u'设备位置', on_delete=models.PROTECT, null=True)
+    type = models.ForeignKey(Option, verbose_name=u'设备分类', on_delete=models.PROTECT, null=True, related_name='type_option')
+    supplier = models.ForeignKey(Option, verbose_name=u'设备供应商', on_delete=models.PROTECT, null=True, related_name='supplier_option')
+
+    class Meta:
+        db_table = 'equipment'
+        verbose_name = u'设备信息'
+        ordering = ['-id', ]
+        default_permissions = ()
+        permissions = [
+            ('browse_equipment', u'查看'),
+            ('add_equipment', u'添加'),
+            ('delete_equipment', u'删除')
+        ]

+ 35 - 0
apps/tenant/equipment/serializers.py

@@ -0,0 +1,35 @@
+# coding=utf-8
+
+import json
+from rest_framework import serializers
+from .models import Equipment
+
+from utils.exceptions import CustomError
+from apps.account.models import User
+
+
+class EquipmentSerializer(serializers.ModelSerializer):
+    type_text = serializers.CharField(source='type.name', read_only=True)
+    supplier_text = serializers.CharField(source='supplier.name', read_only=True)
+    location_text = serializers.SerializerMethodField()
+
+    def get_location_text(self, obj):
+        return obj.location.get_full_name()
+
+    class Meta:
+        model = Equipment
+        fields = '__all__'
+
+    def create(self, validated_data):
+        validated_data['location_id'] = self.context['request'].data['location']
+        validated_data['type_id'] = self.context['request'].data['type']
+        validated_data['supplier_id'] = self.context['request'].data['supplier']
+        instance = super(EquipmentSerializer, self).create(validated_data)
+        return instance
+
+    def update(self,instance, validated_data):
+        validated_data['location_id'] = self.context['request'].data['location']
+        validated_data['type_id'] = self.context['request'].data['type']
+        validated_data['supplier_id'] = self.context['request'].data['supplier']
+        instance = super(EquipmentSerializer, self).update(instance, validated_data)
+        return instance

+ 12 - 0
apps/tenant/equipment/urls.py

@@ -0,0 +1,12 @@
+# coding=utf-8
+from django.conf.urls import url
+from rest_framework.routers import SimpleRouter
+from .views import EquipmentViewSet,DictView
+
+urlpatterns = [
+    url(r'dict/$', DictView.as_view()),
+]
+
+router = SimpleRouter()
+router.register(r'', EquipmentViewSet)
+urlpatterns += router.urls

+ 62 - 0
apps/tenant/equipment/views.py

@@ -0,0 +1,62 @@
+# coding=utf-8
+from django.db import transaction
+from rest_framework.views import APIView
+from utils import response_ok
+from utils.custom_modelviewset import CustomModelViewSet
+from utils.permission import IsTenantUser, permission_required
+from .models import Equipment
+from .serializers import EquipmentSerializer
+from .filters import EquipmentFilter
+from apps.tenant import tenant_log
+from apps.log.models import BizLog
+from utils.exceptions import CustomError
+from apps.tenant.option.serializers import OptionSerializer,Option
+
+class DictView(APIView):
+    permission_classes = [IsTenantUser, ]
+
+    def get(self, request):
+        tenant=request.user.employee.tenant
+        ret = {
+            'types': OptionSerializer(Option.objects.filter(delete=False,type=Option.EQUIPMENT_TYPE, enable=True, tenant=tenant), many=True).data,
+            'suppliers': OptionSerializer(Option.objects.filter(delete=False,type=Option.EQUIPMENT_SUPPLIER, enable=True, tenant=tenant), many=True).data,
+        }
+        return response_ok(ret)
+
+class EquipmentViewSet(CustomModelViewSet):
+    permission_classes = [IsTenantUser, ]
+    queryset = Equipment.objects.all()
+    serializer_class = EquipmentSerializer
+
+    @permission_required('equipment.browse_equipment')
+    def filter_queryset(self, queryset):
+        queryset = queryset.filter(type__tenant=self.request.user.employee.tenant)
+        f = EquipmentFilter(self.request.GET, queryset=queryset)
+        return f.qs
+
+    @permission_required('equipment.add_equipment')
+    def perform_create(self, serializer):
+        super(EquipmentViewSet, self).perform_create(serializer)
+        instance = serializer.instance
+        validated_data = serializer.validated_data
+        tenant_log(self.request.user.employee, BizLog.INSERT, u'添加设备[%s],id=%d' % (instance.name, instance.id),
+                   validated_data)
+
+    @permission_required('equipment.add_equipment')
+    def perform_update(self, serializer):
+        super(EquipmentViewSet, self).perform_update(serializer)
+        instance = serializer.instance
+        validated_data = serializer.validated_data
+        tenant_log(self.request.user.employee, BizLog.UPDATE, u'修改设备[%s],id=%d' % (instance.name, instance.id),
+                   validated_data)
+
+    @permission_required('equipment.delete_equipment')
+    def destroy(self, request, *args, **kwargs):
+        with transaction.atomic():
+            with transaction.atomic():
+                instance = self.get_object()
+                if instance.type.tenant != request.user.employee.tenant:
+                    raise CustomError(u'禁止跨租户操作!')
+                super(EquipmentViewSet, self).destroy(self, request, *args, **kwargs)
+            tenant_log(self.request.user.employee, BizLog.DELETE, u'删除设备[%s],id=%d' % (instance.name, instance.id))
+        return response_ok()

+ 12 - 0
apps/tenant/filters.py

@@ -0,0 +1,12 @@
+# coding=utf-8
+
+import django_filters
+
+from .models import Tenant
+
+class TenantFilter(django_filters.FilterSet):
+    name = django_filters.CharFilter(field_name='name', lookup_expr='icontains')
+
+    class Meta:
+        model = Tenant
+        fields = ['name',]

+ 0 - 0
apps/tenant/inspection_order/__init__.py


+ 12 - 0
apps/tenant/inspection_order/filters.py

@@ -0,0 +1,12 @@
+# coding=utf-8
+
+import django_filters
+from .models import InspectionOrder
+
+
+class InspectionOrderFilter(django_filters.FilterSet):
+    status = django_filters.CharFilter(field_name='status')
+    no = django_filters.CharFilter(field_name='no', lookup_expr='icontains')
+    class Meta:
+        model = InspectionOrder
+        fields = '__all__'

+ 0 - 0
apps/tenant/inspection_order/migrations/__init__.py


+ 89 - 0
apps/tenant/inspection_order/models.py

@@ -0,0 +1,89 @@
+import random
+import string
+from django.utils import timezone
+from django.conf import settings
+from django.db import models
+from apps.tenant.building.models import Building
+from apps.tenant.models import Tenant
+from apps.tenant.option.models import Option
+from apps.upload.models import Upload
+from apps.tenant.repair_order.models import RepairOrder
+from utils.exceptions import CustomError
+
+
+class InspectionOrder(models.Model):
+    NOT_REPAIR = 1
+    HAS_REPAIR = 2
+    REPAIR = 3
+    STATUS_CHOICES = (
+        (NOT_REPAIR, '巡检正常'),
+        (HAS_REPAIR, '已报修'),
+        (REPAIR, '已修复'),
+    )
+
+    building = models.ForeignKey(Building, verbose_name=u'巡检地点', on_delete=models.PROTECT)
+    no = models.CharField(max_length=50, verbose_name=u'编号', blank=True)
+    status = models.PositiveSmallIntegerField(choices=STATUS_CHOICES, verbose_name=u'状态', default=NOT_REPAIR)
+    tenant = models.ForeignKey(Tenant, verbose_name=u'租户', on_delete=models.PROTECT, blank=True)
+    user = models.ForeignKey(settings.AUTH_USER_MODEL, verbose_name=u"巡检人员", on_delete=models.PROTECT,
+                             editable=False, related_name='inspection_order_user')
+    problem = models.CharField(max_length=500, verbose_name=u'巡检问题')
+    create_time = models.DateTimeField(verbose_name=u'巡检时间', auto_now_add=True)
+    images = models.ManyToManyField(Upload, verbose_name=u'图片', blank=True)
+    repair_type = models.ForeignKey(Option, verbose_name=u'报修类型', on_delete=models.PROTECT, null=True)
+    repair_order = models.ForeignKey(RepairOrder, verbose_name=u'转报修', on_delete=models.PROTECT, null=True)
+    turn_repair_time = models.DateTimeField(verbose_name=u'转报修时间', null=True)
+    turn_repair_user = models.ForeignKey(settings.AUTH_USER_MODEL, verbose_name=u"转报修人", on_delete=models.PROTECT,
+                                         null=True, related_name='turn_repair_user')
+
+    class Meta:
+        db_table = 'inspection_order'
+        verbose_name = '巡检工单'
+        ordering = ['-create_time']
+        index_together = (
+            'status',
+            'create_time',
+        )
+        default_permissions = ()
+        permissions = [
+            ('browse_inspection_order', u'查看'),
+            ('add_inspection_order', u'添加'),
+            ('delete_inspection_order', u'删除'),
+            ('dturn_repair_inspection_order', u'转报修'),
+        ]
+
+    @staticmethod
+    def get_instance_by_id(id, tenant):
+        try:
+            id = int(id)
+        except:
+            raise CustomError('无效的inspection_order_id')
+        instance = InspectionOrder.objects.filter(pk=id, tenant=tenant).first()
+        if not instance:
+            raise CustomError('未找到对应的巡检工单实例')
+        return instance
+
+    def get_no(self):
+        now = timezone.now()
+        no = '%s%d-%s' % ('XJ', self.tenant.id, now.strftime('%Y%m%d%H%M%S'))
+        return no
+
+    def turn_repair(self, user):
+        # 巡检转报修
+        repair_order = RepairOrder.objects.create(tenant=self.tenant, building=self.building,
+                                                  fault_des=self.problem, repair_type=self.repair_type, user=self.user,
+                                                  tel=self.user.employee.tel,name=self.user.employee.name,
+                                                  order_type=RepairOrder.INSPECTION_ORDER)
+        repair_order.no = repair_order.get_no()
+        images = self.images.all()
+        for image in images:
+            repair_order.images.add(image.id)
+        repair_order.save()
+
+        self.status = InspectionOrder.HAS_REPAIR
+        self.repair_order = repair_order
+        self.turn_repair_time = timezone.now()
+        self.turn_repair_user = user
+
+        self.save()
+        return repair_order

+ 29 - 0
apps/tenant/inspection_order/serializers.py

@@ -0,0 +1,29 @@
+# coding=utf-8
+from rest_framework import serializers
+from apps.tenant.building.serializer import BuildingSerializer
+from .models import InspectionOrder
+from ...upload.serializers import UploadSerializer
+
+
+class InspectionOrderSerializer(serializers.ModelSerializer):
+    status = serializers.CharField(source='get_status_display', read_only=True)
+    building_text = serializers.SerializerMethodField()
+    user_name = serializers.CharField(source='user.employee.name', read_only=True)
+    user_tel = serializers.CharField(source='user.employee.tel', read_only=True)
+    repair_type = serializers.CharField(source='repair_type.name', read_only=True)
+    repair_order = serializers.CharField(source='repair_order.no', read_only=True)
+    turn_repair_user_name = serializers.CharField(source='turn_repair_user.employee.name', read_only=True)
+    images = serializers.SerializerMethodField()
+    status_text = serializers.CharField(source='get_status_display', read_only=True)
+
+    def get_images(self, obj):
+        return UploadSerializer(obj.images, many=True).data
+
+    def get_building_text(self, obj):
+        return obj.building.get_full_name()
+
+    class Meta:
+        model = InspectionOrder
+        fields = (
+            'id', 'status', 'building_text', 'no', 'problem', 'create_time', 'turn_repair_time', 'user_name',
+            'user_tel', 'repair_type', 'repair_order','status_text', 'turn_repair_user_name', 'images')

+ 12 - 0
apps/tenant/inspection_order/urls.py

@@ -0,0 +1,12 @@
+# coding=utf-8
+
+from rest_framework.routers import SimpleRouter
+
+from .views import InspectionOrderViewSet
+
+urlpatterns = [
+]
+
+router = SimpleRouter()
+router.register(r'', InspectionOrderViewSet)
+urlpatterns += router.urls

+ 38 - 0
apps/tenant/inspection_order/views.py

@@ -0,0 +1,38 @@
+from django.db import transaction
+from rest_framework.decorators import action
+
+from utils import response_error, response_ok
+from utils.custom_modelviewset import CustomModelViewSet
+from utils.exceptions import CustomError
+from utils.permission import IsTenantUser, permission_required
+from .models import InspectionOrder
+from .filters import InspectionOrderFilter
+from .serializers import InspectionOrderSerializer
+
+
+class InspectionOrderViewSet(CustomModelViewSet):
+    permission_classes = [IsTenantUser, ]
+    queryset = InspectionOrder.objects.filter()
+    serializer_class = InspectionOrderSerializer
+
+    @permission_required('inspection_order.browse_inspection_order')
+    def filter_queryset(self, queryset):
+        queryset = queryset.filter(tenant=self.request.user.employee.tenant)
+        f = InspectionOrderFilter(self.request.GET, queryset=queryset)
+        return f.qs
+
+    @action(methods=['post'], detail=True)
+    def turn_repair(self, request, pk):
+        # 转报修
+        try:
+            if not self.request.user.has_perm('inspection_order.dturn_repair_inspection_order'):
+                raise CustomError(u"您没有[报修工单-转报修]权限,无法执行该操作,请联系管理员分配权限!")
+            with transaction.atomic():
+                user = self.request.user
+                instance = InspectionOrder.get_instance_by_id(pk, user.employee.tenant)
+                instance.turn_repair(user)
+        except CustomError as e:
+            return response_error(e.get_error_msg())
+        except Exception as e:
+            return response_error(str(e))
+        return response_ok('转报修完成!')

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


+ 41 - 0
apps/tenant/models.py

@@ -0,0 +1,41 @@
+# coding=utf-8
+from django.db import models
+from django.conf import settings
+from django.db.models import Q
+from utils.exceptions import CustomError
+
+class Tenant(models.Model):
+    name = models.CharField(max_length=200, verbose_name=u'名称')
+    create_user = models.ForeignKey(settings.AUTH_USER_MODEL, verbose_name=u"创建人", on_delete=models.PROTECT,
+                                    editable=False)
+    create_time = models.DateTimeField(verbose_name=u'创建时间', auto_now_add=True, editable=False)
+    is_bind_app = models.BooleanField(verbose_name=u'是否绑定小程序', default=False, editable=False)
+    delete = models.BooleanField(verbose_name=u'删除', default=False, editable=False)
+
+    class Meta:
+        db_table = 'tenant'
+        ordering = ['-id']
+        verbose_name = '租户'
+        default_permissions = ()
+
+    @staticmethod
+    def is_exist(name, exclude_id=None):
+        rows = Tenant.objects.filter(delete=False, name=name)
+        if exclude_id:
+            rows = rows.filter(~Q(id=exclude_id))
+        return rows.count()
+
+    @staticmethod
+    def getById(id):
+        try:
+            id = int(id)
+        except:
+            raise CustomError(u'无效的商户id!')
+        tenant = Tenant.objects.filter(id=id,delete=False).first()
+        if not tenant:
+            raise CustomError(u'未找到相应的商户信息!')
+        return tenant
+
+
+
+

+ 1 - 0
apps/tenant/notices/__init__.py

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

+ 9 - 0
apps/tenant/notices/filters.py

@@ -0,0 +1,9 @@
+# coding=utf-8
+import django_filters
+from .models import Notices
+
+
+class NoticesFilter(django_filters.FilterSet):
+    class Meta:
+        model = Notices
+        fields = '__all__'

+ 0 - 0
apps/tenant/notices/migrations/__init__.py


+ 27 - 0
apps/tenant/notices/models.py

@@ -0,0 +1,27 @@
+# coding=utf-8
+
+
+from django.db import models
+from django.conf import settings
+
+from apps.tenant.models import Tenant
+
+
+class Notices(models.Model):
+    content = models.CharField(max_length=200, verbose_name=u"内容")
+    title = models.CharField(max_length=50, verbose_name=u'标题')
+    create_time = models.DateTimeField(verbose_name='发布时间', auto_now_add=True)
+    create_user = models.ForeignKey(settings.AUTH_USER_MODEL, verbose_name=u"创建者", on_delete=models.PROTECT,
+                                    editable=False)
+    tenant = models.ForeignKey(Tenant, verbose_name=u'租户', on_delete=models.PROTECT, blank=True)
+
+    class Meta:
+        db_table = "notices"
+        verbose_name = u"通知"
+        ordering = ['-id']
+        default_permissions = ()
+        permissions = [
+            ('browse_notices', u'查看'),
+            ('add_notices', u'添加'),
+            ('delete_notices', u'删除'),
+        ]

+ 23 - 0
apps/tenant/notices/serializers.py

@@ -0,0 +1,23 @@
+# coding=utf-8
+
+from rest_framework import serializers
+from .models import Notices
+
+
+class NoticesSerializer(serializers.ModelSerializer):
+    create_user_name = serializers.CharField(source='create_user.employee.name', read_only=True)
+    class Meta:
+        model = Notices
+        fields = '__all__'
+
+    def create(self, validated_data):
+        validated_data['create_user'] = self.context['request'].user
+        validated_data['tenant'] = self.context['request'].user.employee.tenant
+        instance = super(NoticesSerializer, self).create(validated_data)
+        return instance
+
+
+class NoticesWXSerializer(serializers.ModelSerializer):
+    class Meta:
+        model = Notices
+        fields = '__all__'

+ 13 - 0
apps/tenant/notices/urls.py

@@ -0,0 +1,13 @@
+# coding=utf-8
+from django.conf.urls import url, include
+from rest_framework.routers import SimpleRouter
+
+from .views import NoticesViewSet
+
+urlpatterns = [
+
+]
+
+router = SimpleRouter()
+router.register(r'', NoticesViewSet)
+urlpatterns += router.urls

+ 50 - 0
apps/tenant/notices/views.py

@@ -0,0 +1,50 @@
+# coding=utf-8
+from django.db import transaction
+
+from utils import response_ok
+from utils.custom_modelviewset import CustomModelViewSet
+from utils.exceptions import CustomError
+from utils.permission import IsTenantUser, permission_required
+from apps.tenant import tenant_log
+from apps.log.models import BizLog
+from .serializers import NoticesSerializer
+from .models import Notices
+from .filters import NoticesFilter
+
+
+class NoticesViewSet(CustomModelViewSet):
+    serializer_class = NoticesSerializer
+    queryset = Notices.objects.filter()
+    permission_classes = [IsTenantUser, ]
+
+    @permission_required('notices.browse_notices')
+    def filter_queryset(self, queryset):
+        queryset = queryset.filter(tenant=self.request.user.employee.tenant)
+        f = NoticesFilter(self.request.GET, queryset=queryset)
+        return f.qs
+
+    @permission_required('notices.add_notices')
+    def perform_create(self, serializer):
+        super(NoticesViewSet, self).perform_create(serializer)
+        instance = serializer.instance
+        validated_data = serializer.validated_data
+        tenant_log(self.request.user.employee, BizLog.INSERT, u'添加通知[%s],id=%d' % (instance.title, instance.id),
+                   validated_data)
+
+    @permission_required('notices.add_notices')
+    def perform_update(self, serializer):
+        super(NoticesViewSet, self).perform_update(serializer)
+        instance = serializer.instance
+        validated_data = serializer.validated_data
+        tenant_log(self.request.user.employee, BizLog.UPDATE, u'修改通知[%s],id=%d' % (instance.title, instance.id),
+                   validated_data)
+
+    @permission_required('notices.delete_notices')
+    def destroy(self, request, *args, **kwargs):
+        with transaction.atomic():
+            instance = self.get_object()
+            if instance.tenant != request.user.employee.tenant:
+                raise CustomError(u'禁止跨租户操作!')
+            super(NoticesViewSet, self).destroy(self, request, *args, **kwargs)
+        tenant_log(self.request.user.employee, BizLog.DELETE, u'删除通知[%s],id=%d' % (instance.title, instance.id))
+        return response_ok()

+ 10 - 0
apps/tenant/option/filters.py

@@ -0,0 +1,10 @@
+import django_filters
+from .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__'

Nem az összes módosított fájl került megjelenítésre, mert túl sok fájl változott