作者 wanweibin

完善后台接口

1 # Default ignored files 1 # Default ignored files
2 -/workspace.xml  
  2 +/workspace.xml
  3 +# Datasource local storage ignored files
  4 +/dataSources/
  5 +/dataSources.local.xml
  1 +<?xml version="1.0" encoding="UTF-8"?>
  2 +<project version="4">
  3 + <component name="DataSourceManagerImpl" format="xml" multifile-model="true">
  4 + <data-source source="LOCAL" name="SQLite - db.sqlite3" uuid="40358238-ad1d-4a4e-aa6e-a585a40363bc">
  5 + <driver-ref>sqlite.xerial</driver-ref>
  6 + <synchronize>true</synchronize>
  7 + <jdbc-driver>org.sqlite.JDBC</jdbc-driver>
  8 + <jdbc-url>jdbc:sqlite:$PROJECT_DIR$/db.sqlite3</jdbc-url>
  9 + </data-source>
  10 + </component>
  11 +</project>
1 <component name="ProjectDictionaryState"> 1 <component name="ProjectDictionaryState">
2 <dictionary name="a2"> 2 <dictionary name="a2">
3 <words> 3 <words>
  4 + <w>abelzhu</w>
  5 + <w>agentid</w>
  6 + <w>authsucc</w>
4 <w>blocksize</w> 7 <w>blocksize</w>
  8 + <w>btntxt</w>
  9 + <w>corpid</w>
  10 + <w>corpsecret</w>
5 <w>corsheaders</w> 11 <w>corsheaders</w>
  12 + <w>getuserdetail</w>
  13 + <w>getuserinfo</w>
  14 + <w>lconf</w>
  15 + <w>msgtype</w>
  16 + <w>simplejwt</w>
  17 + <w>textcard</w>
  18 + <w>touser</w>
6 <w>usercenter</w> 19 <w>usercenter</w>
  20 + <w>userid</w>
  21 + <w>userlogin</w>
7 <w>vanwhebin</w> 22 <w>vanwhebin</w>
  23 + <w>wxlogin</w>
8 <w>xlsx</w> 24 <w>xlsx</w>
9 </words> 25 </words>
10 </dictionary> 26 </dictionary>
@@ -10,6 +10,7 @@ @@ -10,6 +10,7 @@
10 # @Date 2018-02-24 10 # @Date 2018-02-24
11 # 11 #
12 # 12 #
  13 +from abc import ABC
13 14
14 from .CorpApi import * 15 from .CorpApi import *
15 16
@@ -26,8 +27,9 @@ SERVICE_CORP_API_TYPE = { @@ -26,8 +27,9 @@ SERVICE_CORP_API_TYPE = {
26 } 27 }
27 28
28 29
29 -class ServiceCorpApi(CorpApi):  
30 - def __init__(self, suite_id, suite_secret, suite_ticket, auth_corpid=None, permanent_code=None): 30 +class ServiceCorpApi(CorpApi, ABC):
  31 + def __init__(self, suite_id, suite_secret, suite_ticket, corpid, secret, auth_corpid=None, permanent_code=None):
  32 + super().__init__(corpid, secret)
31 self.suite_id = suite_id 33 self.suite_id = suite_id
32 self.suite_secret = suite_secret 34 self.suite_secret = suite_secret
33 self.suite_ticket = suite_ticket 35 self.suite_ticket = suite_ticket
@@ -39,7 +41,7 @@ class ServiceCorpApi(CorpApi): @@ -39,7 +41,7 @@ class ServiceCorpApi(CorpApi):
39 self.access_token = None 41 self.access_token = None
40 self.suite_access_token = None 42 self.suite_access_token = None
41 43
42 - ## override CorpApi 的 refreshAccessToken, 使用第三方服务商的方法 44 + # override CorpApi 的 refreshAccessToken, 使用第三方服务商的方法
43 def getAccessToken(self): 45 def getAccessToken(self):
44 if self.access_token is None: 46 if self.access_token is None:
45 self.refreshAccessToken() 47 self.refreshAccessToken()
@@ -54,8 +56,6 @@ class ServiceCorpApi(CorpApi): @@ -54,8 +56,6 @@ class ServiceCorpApi(CorpApi):
54 }) 56 })
55 self.access_token = response.get('access_token') 57 self.access_token = response.get('access_token')
56 58
57 - ##  
58 -  
59 def getSuiteAccessToken(self): 59 def getSuiteAccessToken(self):
60 if self.suite_access_token is None: 60 if self.suite_access_token is None:
61 self.refreshSuiteAccessToken() 61 self.refreshSuiteAccessToken()
@@ -10,6 +10,7 @@ @@ -10,6 +10,7 @@
10 # @Date 2018-02-26 10 # @Date 2018-02-26
11 # 11 #
12 # 12 #
  13 +from abc import ABC
13 14
14 from .AbstractApi import * 15 from .AbstractApi import *
15 16
@@ -18,13 +19,14 @@ SERVICE_PROVIDER_API_TYPE = { @@ -18,13 +19,14 @@ SERVICE_PROVIDER_API_TYPE = {
18 'GET_LOGIN_INFO': ['/cgi-bin/service/get_login_info?access_token=PROVIDER_ACCESS_TOKEN', 'POST'], 19 'GET_LOGIN_INFO': ['/cgi-bin/service/get_login_info?access_token=PROVIDER_ACCESS_TOKEN', 'POST'],
19 'GET_REGISTER_CODE': ['/cgi-bin/service/get_register_code?provider_access_token=PROVIDER_ACCESS_TOKEN', 'POST'], 20 'GET_REGISTER_CODE': ['/cgi-bin/service/get_register_code?provider_access_token=PROVIDER_ACCESS_TOKEN', 'POST'],
20 'GET_REGISTER_INFO': ['/cgi-bin/service/get_register_info?provider_access_token=PROVIDER_ACCESS_TOKEN', 'POST'], 21 'GET_REGISTER_INFO': ['/cgi-bin/service/get_register_info?provider_access_token=PROVIDER_ACCESS_TOKEN', 'POST'],
21 - 'SET_AGENT_SCOPE': ['/cgi-bin/agent/set_scope', 'POST'], ### TODO 22 + 'SET_AGENT_SCOPE': ['/cgi-bin/agent/set_scope', 'POST'], # TODO
22 'SET_CONTACT_SYNC_SUCCESS': ['/cgi-bin/sync/contact_sync_success', 'GET'], 23 'SET_CONTACT_SYNC_SUCCESS': ['/cgi-bin/sync/contact_sync_success', 'GET'],
23 } 24 }
24 25
25 26
26 -class ServiceProviderApi(AbstractApi): 27 +class ServiceProviderApi(AbstractApi, ABC):
27 def __init__(self, corpid, provider_secret): 28 def __init__(self, corpid, provider_secret):
  29 + super().__init__()
28 self.corpid = corpid 30 self.corpid = corpid
29 self.provider_secret = provider_secret 31 self.provider_secret = provider_secret
30 32
@@ -8,7 +8,7 @@ @@ -8,7 +8,7 @@
8 # @Date 2018-02-23 8 # @Date 2018-02-23
9 9
10 # 设置为true会打印一些调试信息 10 # 设置为true会打印一些调试信息
11 -from wxProject.wxProject.settings import DEBUG 11 +from wxProject.settings import DEBUG
12 12
13 DEBUG = DEBUG 13 DEBUG = DEBUG
14 14
@@ -19,10 +19,10 @@ Conf = { @@ -19,10 +19,10 @@ Conf = {
19 "CORP_ID": "ww0f3efc2873ad11c3", 19 "CORP_ID": "ww0f3efc2873ad11c3",
20 20
21 # "通讯录同步"应用的secret, 开启api接口同步后,可以在管理端->"通讯录同步"看到 21 # "通讯录同步"应用的secret, 开启api接口同步后,可以在管理端->"通讯录同步"看到
22 - "CONTACT_SYNC_SECRET": "xWPfryWX7Fv1BcJSpivflpPQXC_v5iH0HY2zfu1soTA", 22 + "CONTACT_SYNC_SECRET": "7MHpdQICiegx9rIc4iZrEPunb1aYUqdJYKSW9v7a1A8",
23 23
24 # 运维组ID 24 # 运维组ID
25 - "OPS_DEP_ID": '346', 25 + # "OPS_DEP_ID": '346',
26 26
27 # 某个自建应用的id及secret, 在管理端 -> 企业应用 -> 自建应用, 点进相应应用可以看到 27 # 某个自建应用的id及secret, 在管理端 -> 企业应用 -> 自建应用, 点进相应应用可以看到
28 "APP_ID": '1000078', 28 "APP_ID": '1000078',
  1 +# Generated by Django 3.1.1 on 2020-10-07 04:21
  2 +
  3 +from django.db import migrations, models
  4 +
  5 +
  6 +class Migration(migrations.Migration):
  7 +
  8 + dependencies = [
  9 + ('project', '0006_auto_20200930_1745'),
  10 + ]
  11 +
  12 + operations = [
  13 + migrations.AlterField(
  14 + model_name='project',
  15 + name='auditor',
  16 + field=models.ManyToManyField(blank=True, related_name='project_auditor', to='project.Auditor', verbose_name='审核人员'),
  17 + ),
  18 + ]
  1 +# Generated by Django 3.1.1 on 2020-10-07 07:44
  2 +
  3 +from django.db import migrations, models
  4 +
  5 +
  6 +class Migration(migrations.Migration):
  7 +
  8 + dependencies = [
  9 + ('project', '0007_auto_20201007_1221'),
  10 + ]
  11 +
  12 + operations = [
  13 + migrations.AddField(
  14 + model_name='project',
  15 + name='is_done',
  16 + field=models.BooleanField(blank=True, default=False, verbose_name='是否完成'),
  17 + ),
  18 + migrations.AddField(
  19 + model_name='project',
  20 + name='is_pass',
  21 + field=models.BooleanField(blank=True, default=False, verbose_name='是否通过'),
  22 + ),
  23 + ]
  1 +# Generated by Django 3.1.1 on 2020-10-07 08:26
  2 +
  3 +from django.conf import settings
  4 +from django.db import migrations, models
  5 +import django.db.models.deletion
  6 +
  7 +
  8 +class Migration(migrations.Migration):
  9 +
  10 + dependencies = [
  11 + migrations.swappable_dependency(settings.AUTH_USER_MODEL),
  12 + ('project', '0008_auto_20201007_1544'),
  13 + ]
  14 +
  15 + operations = [
  16 + migrations.AlterModelOptions(
  17 + name='project',
  18 + options={'ordering': ('-is_done',)},
  19 + ),
  20 + migrations.RenameField(
  21 + model_name='result',
  22 + old_name='program',
  23 + new_name='project',
  24 + ),
  25 + migrations.AddField(
  26 + model_name='project',
  27 + name='creator',
  28 + field=models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, related_name='project_creator', to=settings.AUTH_USER_MODEL, verbose_name='创建人员'),
  29 + ),
  30 + ]
  1 +# Generated by Django 3.1.1 on 2020-10-07 08:26
  2 +
  3 +from django.conf import settings
  4 +from django.db import migrations, models
  5 +import django.db.models.deletion
  6 +
  7 +
  8 +class Migration(migrations.Migration):
  9 +
  10 + dependencies = [
  11 + migrations.swappable_dependency(settings.AUTH_USER_MODEL),
  12 + ('project', '0009_auto_20201007_1626'),
  13 + ]
  14 +
  15 + operations = [
  16 + migrations.AlterField(
  17 + model_name='project',
  18 + name='creator',
  19 + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='project_creator', to=settings.AUTH_USER_MODEL, verbose_name='创建人员'),
  20 + ),
  21 + ]
  1 +# Generated by Django 3.1.1 on 2020-10-07 09:16
  2 +
  3 +from django.db import migrations, models
  4 +
  5 +
  6 +class Migration(migrations.Migration):
  7 +
  8 + dependencies = [
  9 + ('project', '0010_auto_20201007_1626'),
  10 + ]
  11 +
  12 + operations = [
  13 + migrations.AlterModelOptions(
  14 + name='auditor',
  15 + options={'ordering': ('order',)},
  16 + ),
  17 + migrations.AddField(
  18 + model_name='auditor',
  19 + name='order',
  20 + field=models.PositiveSmallIntegerField(blank=True, default=0, null=True, verbose_name='排序'),
  21 + ),
  22 + ]
@@ -20,10 +20,14 @@ class Auditor(models.Model): @@ -20,10 +20,14 @@ class Auditor(models.Model):
20 null=True, 20 null=True,
21 blank=True, 21 blank=True,
22 verbose_name="上级领导") 22 verbose_name="上级领导")
  23 + order = models.PositiveSmallIntegerField(default=0, null=True, blank=True, verbose_name="排序")
23 24
24 def __str__(self): 25 def __str__(self):
25 return self.user.username 26 return self.user.username
26 27
  28 + class Meta:
  29 + ordering = ('order', )
  30 +
27 31
28 class Project(models.Model): 32 class Project(models.Model):
29 category = models.CharField(max_length=100, default="", verbose_name="产品类目") 33 category = models.CharField(max_length=100, default="", verbose_name="产品类目")
@@ -31,14 +35,17 @@ class Project(models.Model): @@ -31,14 +35,17 @@ class Project(models.Model):
31 market_share_analysis = models.TextField(default="", verbose_name="产品市场占有率分析") 35 market_share_analysis = models.TextField(default="", verbose_name="产品市场占有率分析")
32 context_analysis = models.TextField(default="", verbose_name="产品场景分析") 36 context_analysis = models.TextField(default="", verbose_name="产品场景分析")
33 attachments = models.CharField(max_length=800, default="", verbose_name="附件地址") 37 attachments = models.CharField(max_length=800, default="", verbose_name="附件地址")
34 - auditor = models.ManyToManyField(Auditor, related_name="project_auditor", verbose_name="审核人员") 38 + auditor = models.ManyToManyField(Auditor, related_name="project_auditor", blank=True, verbose_name="审核人员")
  39 + is_done = models.BooleanField(default=False, blank=True, verbose_name="是否完成")
  40 + is_pass = models.BooleanField(default=False, blank=True, verbose_name="是否通过")
35 create_time = models.DateTimeField(auto_now_add=True) 41 create_time = models.DateTimeField(auto_now_add=True)
  42 + creator = models.ForeignKey(User, related_name="project_creator", on_delete=models.CASCADE, verbose_name="创建人员")
36 43
37 def __str__(self): 44 def __str__(self):
38 return self.category + '-' + self.model_type 45 return self.category + '-' + self.model_type
39 46
40 class Meta: 47 class Meta:
41 - ordering = ('-create_time',) 48 + ordering = ('-is_done',)
42 49
43 50
44 class Result(models.Model): 51 class Result(models.Model):
@@ -48,7 +55,7 @@ class Result(models.Model): @@ -48,7 +55,7 @@ class Result(models.Model):
48 ) 55 )
49 56
50 auditor = models.ForeignKey(Auditor, on_delete=models.CASCADE, related_name="result_auditor", verbose_name="审核人员") 57 auditor = models.ForeignKey(Auditor, on_delete=models.CASCADE, related_name="result_auditor", verbose_name="审核人员")
51 - program = models.ForeignKey(Project, on_delete=models.CASCADE, related_name="result_project", verbose_name="审核项目") 58 + project = models.ForeignKey(Project, on_delete=models.CASCADE, related_name="result_project", verbose_name="审核项目")
52 is_accept = models.CharField(max_length=10, null=True, choices=ACCEPT_CHOICES, verbose_name="审核项目") 59 is_accept = models.CharField(max_length=10, null=True, choices=ACCEPT_CHOICES, verbose_name="审核项目")
53 memo = models.CharField(max_length=300, blank=True, default="", verbose_name="审核结果陈述") 60 memo = models.CharField(max_length=300, blank=True, default="", verbose_name="审核结果陈述")
54 create_time = models.DateTimeField(auto_now_add=True) 61 create_time = models.DateTimeField(auto_now_add=True)
@@ -3,7 +3,7 @@ @@ -3,7 +3,7 @@
3 # @Author vanwhebin 3 # @Author vanwhebin
4 from rest_framework import serializers 4 from rest_framework import serializers
5 5
6 -from .models import Auditor, Project 6 +from .models import Auditor, Project, Result
7 from usercenter.serializers import UserSerializer 7 from usercenter.serializers import UserSerializer
8 8
9 9
@@ -16,9 +16,18 @@ class AuditorSerializer(serializers.ModelSerializer): @@ -16,9 +16,18 @@ class AuditorSerializer(serializers.ModelSerializer):
16 depth = 1 16 depth = 1
17 17
18 18
19 -class ProgramSerializer(serializers.ModelSerializer): 19 +class ProjectSerializer(serializers.ModelSerializer):
  20 + creator = serializers.ReadOnlyField(source='project_creator.id')
  21 + # teacher = serializers.ReadOnlyField(source='teacher.username') # 外键字段 只读
  22 +
20 class Meta: 23 class Meta:
21 model = Project 24 model = Project
22 fields = '__all__' 25 fields = '__all__'
  26 + depth = 1
  27 +
23 28
  29 +class ResultSerializer(serializers.ModelSerializer):
  30 + class Meta:
  31 + model = Result
  32 + fields = '__all__'
24 33
@@ -2,19 +2,25 @@ @@ -2,19 +2,25 @@
2 # @Time : 2020/9/30 11:18 2 # @Time : 2020/9/30 11:18
3 # @Author vanwhebin 3 # @Author vanwhebin
4 4
5 -from django.urls import re_path, include 5 +from django.urls import path, include
6 from rest_framework import routers 6 from rest_framework import routers
7 7
8 -from .views import AuditorViewSet, ProgramViewSet, AuditorListView 8 +from .views import AuditorViewSet, ProgramViewSet, AuditorListView, ResultViewSet
  9 +from .views_cbv import CreateProject, ProjectDetail, AuditProjectsList, AuditProject
9 10
10 router = routers.DefaultRouter() 11 router = routers.DefaultRouter()
11 router.register(r'auditor', AuditorViewSet, basename="auditor") 12 router.register(r'auditor', AuditorViewSet, basename="auditor")
12 router.register(r'program', ProgramViewSet, basename="program") 13 router.register(r'program', ProgramViewSet, basename="program")
  14 +router.register(r'result', ResultViewSet, basename="result")
13 15
14 app_name = "project" 16 app_name = "project"
15 17
16 urlpatterns = [ 18 urlpatterns = [
17 - re_path('', include(router.urls)), 19 + path('', include(router.urls)),
18 # re_path(r'auditor', AuditorListView.as_view()), 20 # re_path(r'auditor', AuditorListView.as_view()),
19 # re_path(r'auditor/<int:pk>', AuditorListView.as_view()), 21 # re_path(r'auditor/<int:pk>', AuditorListView.as_view()),
  22 + path('create', CreateProject.as_view(), name="create_project"),
  23 + path('audit/<int:pk>', AuditProject.as_view(), name="audit_project"),
  24 + path('list', AuditProjectsList.as_view(), name="list_projects"),
  25 + path('<int:pk>', ProjectDetail.as_view(), name="retrieve_project"),
20 ] 26 ]
@@ -2,8 +2,8 @@ @@ -2,8 +2,8 @@
2 # @Time : 2020/9/30 11:02 2 # @Time : 2020/9/30 11:02
3 # @Author vanwhebin 3 # @Author vanwhebin
4 from rest_framework import viewsets, generics, views 4 from rest_framework import viewsets, generics, views
5 -from .models import Auditor, Project  
6 -from .serializers import AuditorSerializer, ProgramSerializer 5 +from .models import Auditor, Project, Result
  6 +from .serializers import AuditorSerializer, ProjectSerializer, ResultSerializer
7 from rest_framework.permissions import IsAuthenticated, IsAuthenticatedOrReadOnly 7 from rest_framework.permissions import IsAuthenticated, IsAuthenticatedOrReadOnly
8 8
9 9
@@ -35,6 +35,11 @@ class AuditorDetailView(generics.RetrieveDestroyAPIView): @@ -35,6 +35,11 @@ class AuditorDetailView(generics.RetrieveDestroyAPIView):
35 35
36 class ProgramViewSet(viewsets.ModelViewSet): 36 class ProgramViewSet(viewsets.ModelViewSet):
37 queryset = Project.objects.all() 37 queryset = Project.objects.all()
38 - serializer_class = ProgramSerializer 38 + serializer_class = ProjectSerializer
39 permission_classes = (IsAuthenticated, ) 39 permission_classes = (IsAuthenticated, )
40 40
  41 +
  42 +class ResultViewSet(viewsets.ModelViewSet):
  43 + queryset = Result.objects.all()
  44 + serializer_class = ResultSerializer
  45 + permission_classes = (IsAuthenticated, )
  1 +# _*_ coding: utf-8 _*_
  2 +# @Time : 2020/10/7 10:40
  3 +# @Author vanwhebin
  4 +from rest_framework.generics import CreateAPIView, RetrieveAPIView, UpdateAPIView, ListAPIView
  5 +from rest_framework.permissions import IsAuthenticated, IsAdminUser
  6 +from rest_framework.exceptions import ValidationError
  7 +from django.urls import reverse
  8 +from rest_framework.response import Response
  9 +
  10 +from .serializers import ProjectSerializer
  11 +from .models import Auditor, Project, Result
  12 +from utils.helpers import WxPushHelper
  13 +from utils.pagination import MyPageNumberPagination
  14 +
  15 +
  16 +class CreateProject(CreateAPIView):
  17 + serializer_class = ProjectSerializer
  18 + permission_classes = (IsAuthenticated,)
  19 +
  20 + def post(self, request, *args, **kwargs):
  21 + super(CreateProject, self).__init__()
  22 + wx_client = WxPushHelper()
  23 + serializer = ProjectSerializer(data=request.data)
  24 + if not serializer.is_valid():
  25 + raise ValidationError
  26 + else:
  27 + serializer.save(creator=request.user, auditor=Auditor.objects.order_by('-order').all())
  28 + # 企业微信推送
  29 + obj_dict = serializer.data
  30 + wx_client.push_card(request.user.wx_token,
  31 + reverse("project:retrieve_project", kwargs={"pk": obj_dict['id']}), u"流程创建成功")
  32 + return Response(obj_dict)
  33 +
  34 +
  35 +class ProjectDetail(RetrieveAPIView):
  36 + queryset = Project.objects.all()
  37 + serializer_class = ProjectSerializer
  38 + permission_classes = (IsAuthenticated,)
  39 +
  40 +
  41 +class AuditProject(UpdateAPIView):
  42 + queryset = Project.objects.all()
  43 + serializer_class = ProjectSerializer
  44 + permission_classes = (IsAuthenticated, IsAdminUser)
  45 +
  46 + @staticmethod
  47 + def _check_audit(project_obj):
  48 + # 查看是否已经全部审核完毕 进行更新project表
  49 + # 所有的审核人员, 是否有result记录
  50 + aud_result_len = Result.objects.filter(project=project_obj).count()
  51 + auditor_len = Project.objects.filter(pk=project_obj.id).values('auditor__user_id').count()
  52 + return aud_result_len == auditor_len
  53 +
  54 + def partial_update(self, request, *args, **kwargs):
  55 + # 对项目进行更新
  56 + obj = self.get_object()
  57 + accept_choices = (
  58 + ('accept', '通过'),
  59 + ('reject', '否决')
  60 + )
  61 + accept_param = accept_choices[0][0] if request.data.get('is_accept') else accept_choices[1][0]
  62 + target = Project.objects.filter(auditor__user_id=request.user.id, pk=obj.id)
  63 + if not target:
  64 + raise PermissionError
  65 + else:
  66 + auditor = Auditor.objects.get(user=request.user)
  67 + result = Result.objects.filter(auditor=auditor, project=obj).first()
  68 + if not result:
  69 + Result.objects.create(
  70 + auditor=auditor,
  71 + project=obj,
  72 + is_accept=accept_param,
  73 + memo=request.data.get('memo', '')
  74 + )
  75 + else:
  76 + result.is_accept = accept_param
  77 + result.save()
  78 +
  79 + wx_client = WxPushHelper()
  80 + full_audit_done = self._check_audit(obj)
  81 + desc = "产品立项流程所有审批已完成" if full_audit_done else f"{request.user.username}已审批完成"
  82 + if full_audit_done:
  83 + obj.is_done = True
  84 + obj.is_pass = request.data.get('is_accept')
  85 + else:
  86 + if not accept_param:
  87 + obj.is_done = True
  88 + obj.is_pass = False
  89 + obj.save()
  90 + wx_client.push_card(obj.creator.wx_token, reverse("project:retrieve_project", kwargs={"pk": obj.id}), desc)
  91 +
  92 + return Response(ProjectSerializer(obj).data)
  93 +
  94 +
  95 +class AuditProjectsList(ListAPIView):
  96 + serializer_class = ProjectSerializer
  97 + pagination_class = MyPageNumberPagination
  98 + permission_classes = (IsAuthenticated, IsAdminUser)
  99 +
  100 + def get_queryset(self, **kwargs):
  101 + return Project.objects.filter(auditor__user_id=kwargs['user'].id)
  102 +
  103 + def get(self, request, *args, **kwargs):
  104 + qs = self.get_queryset(user=request.user)
  105 + return Response(ProjectSerializer(qs, many=True).data)
@@ -21,8 +21,12 @@ class MyTokenObtainPairSerializer(TokenObtainPairSerializer): @@ -21,8 +21,12 @@ class MyTokenObtainPairSerializer(TokenObtainPairSerializer):
21 class UserSerializer(serializers.ModelSerializer): 21 class UserSerializer(serializers.ModelSerializer):
22 class Meta: 22 class Meta:
23 model = User 23 model = User
24 - fields = ('username', 'email', 'is_superuser', 'password')  
25 - extra_kwargs = {"password": {"write_only": True}} 24 + fields = ('username', 'email', 'is_superuser', 'password', 'wx_token', 'dd_token')
  25 + extra_kwargs = {
  26 + "password": {"write_only": True},
  27 + # "wx_token": {"write_only": True},
  28 + "dd_token": {"write_only": True}
  29 + }
26 30
27 def create(self, validated_data): 31 def create(self, validated_data):
28 user = User( 32 user = User(
@@ -2,7 +2,7 @@ @@ -2,7 +2,7 @@
2 # @Time : 2020/9/8 14:27 2 # @Time : 2020/9/8 14:27
3 # @Author vanwhebin 3 # @Author vanwhebin
4 4
5 -from django.urls import re_path, include 5 +from django.urls import path, include
6 from rest_framework import routers 6 from rest_framework import routers
7 7
8 from .views import UserViewSet, GroupViewSet, PermissionViewSet 8 from .views import UserViewSet, GroupViewSet, PermissionViewSet
@@ -15,5 +15,5 @@ router.register(r'permission', PermissionViewSet, basename="permission") @@ -15,5 +15,5 @@ router.register(r'permission', PermissionViewSet, basename="permission")
15 app_name = "usercenter" 15 app_name = "usercenter"
16 16
17 urlpatterns = [ 17 urlpatterns = [
18 - re_path('', include(router.urls)), 18 + path('', include(router.urls)),
19 ] 19 ]
@@ -36,3 +36,5 @@ class PermissionViewSet(viewsets.ModelViewSet): @@ -36,3 +36,5 @@ class PermissionViewSet(viewsets.ModelViewSet):
36 serializer_class = PermissionSerializer 36 serializer_class = PermissionSerializer
37 permission_classes = (IsAdminUser,) 37 permission_classes = (IsAdminUser,)
38 38
  39 +
  40 +
  1 +# _*_ coding: utf-8 _*_
  2 +# @Time : 2020/10/7 11:26
  3 +# @Author vanwhebin
  4 +
  5 +from django.views.generic import View
  6 +from django.core.exceptions import PermissionDenied
  7 +from rest_framework.views import APIView
  8 +from rest_framework.permissions import AllowAny
  9 +from rest_framework_simplejwt.serializers import TokenObtainSerializer, TokenObtainPairSerializer
  10 +
  11 +from libs.qywx.conf import Conf
  12 +from libs.qywx.CorpApi import CorpApi, CORP_API_TYPE
  13 +from usercenter.models import UserManager, User
  14 +from utils.util import response
  15 +
  16 +
  17 +class WxRequiredMixin(View):
  18 + """ 验证是否企业微信登录成功;"""
  19 +
  20 + def dispatch(self, request, *args, **kwargs):
  21 + # 状态和文章实例有user属性
  22 + if self.get_object().user.username != self.request.user.username:
  23 + raise PermissionDenied
  24 +
  25 + return super(WxRequiredMixin, self).dispatch(request, *args, **kwargs)
  26 +
  27 +
  28 +class WxPushHelper:
  29 + api = None
  30 +
  31 + def __init__(self):
  32 + self.api = CorpApi(Conf['CORP_ID'], Conf['APP_SECRET'])
  33 +
  34 + def get_corp_user_id_by_code(self, code):
  35 + return self.api.httpCall(CORP_API_TYPE['GET_USER_INFO_BY_CODE'], code)
  36 +
  37 + def get_user_info_by_corp_user_id(self, corp_user_id):
  38 + return self.api.httpCall(CORP_API_TYPE['USER_GET'], corp_user_id)
  39 +
  40 + def push_text(self, user_wx_id, content):
  41 + data = {
  42 + "agentid": Conf['APP_ID'], # 企业应用ID
  43 + "msgtype": 'text', # 消息类型为文本
  44 + "touser": user_wx_id, # 接受消息的对象
  45 + "text": {
  46 + "content": content # 消息文本
  47 + }
  48 + }
  49 + return self.api.httpCall(CORP_API_TYPE['MESSAGE_SEND'], data)
  50 +
  51 + def push_card(self, user_wx_id, url, description):
  52 + data = {
  53 + "touser": str(user_wx_id),
  54 + "msgtype": "textcard",
  55 + "agentid": Conf['APP_ID'], # 企业应用ID
  56 + "textcard": {
  57 + "title": "产品立项流程通知",
  58 + "description": description,
  59 + "url": url,
  60 + "btntxt": "点击查看"
  61 + },
  62 + "enable_id_trans": 0,
  63 + "enable_duplicate_check": 0,
  64 + "duplicate_check_interval": 1800
  65 + }
  66 +
  67 + return self.api.httpCall(CORP_API_TYPE['MESSAGE_SEND'], data)
  68 +
  69 +
  70 +class WxUserlogin(APIView):
  71 + allowed_methods = ['post']
  72 + permission_classes = (AllowAny, )
  73 +
  74 + @staticmethod
  75 + def _get_user_info(code):
  76 + client = WxPushHelper()
  77 + corp_user = client.get_corp_user_id_by_code(code)
  78 + if "errcode" not in corp_user and "UserId" in corp_user:
  79 + return client.get_user_info_by_corp_user_id(corp_user['UserId'])
  80 + else:
  81 + raise RuntimeError(u"获取企业微信用户信息错误")
  82 +
  83 + def post(self, request, *args, **kwargs):
  84 + code = request.data.get('code', '').strip()
  85 + if not code:
  86 + raise PermissionDenied
  87 + else:
  88 + user = User.objects.filter(wx_token=code).first()
  89 + if not user:
  90 + info = self._get_user_info(code)
  91 + user = UserManager.create_user(
  92 + info['name'], "123456", info['email'], {"wx_token": info['userid']})
  93 + # 获取token
  94 + token_obj = TokenObtainPairSerializer.get_token(user)
  95 + return response({"access_token": str(token_obj.access_token)})
  96 +
  97 +
@@ -10,13 +10,12 @@ from rest_framework.generics import CreateAPIView @@ -10,13 +10,12 @@ from rest_framework.generics import CreateAPIView
10 from usercenter.serializers import MediaSerializer 10 from usercenter.serializers import MediaSerializer
11 from usercenter.models import Media 11 from usercenter.models import Media
12 from wxProject.settings import MEDIA_ROOT, UPLOAD_MEDIA_CHOICES 12 from wxProject.settings import MEDIA_ROOT, UPLOAD_MEDIA_CHOICES
13 -from utils.util import uuid, get_md5_hash 13 +from utils.util import uuid, get_md5_hash, response as res
14 from rest_framework.permissions import IsAuthenticated, AllowAny 14 from rest_framework.permissions import IsAuthenticated, AllowAny
15 from rest_framework import response, status 15 from rest_framework import response, status
16 16
17 17
18 class UploadMedia(CreateAPIView): 18 class UploadMedia(CreateAPIView):
19 - # queryset = Media  
20 serializer_class = MediaSerializer 19 serializer_class = MediaSerializer
21 permission_classes = (IsAuthenticated, ) 20 permission_classes = (IsAuthenticated, )
22 21
@@ -53,7 +52,8 @@ class UploadMedia(CreateAPIView): @@ -53,7 +52,8 @@ class UploadMedia(CreateAPIView):
53 extension=uploaded_file_ext 52 extension=uploaded_file_ext
54 ) 53 )
55 os.remove(os.path.join(MEDIA_ROOT, file_path)) 54 os.remove(os.path.join(MEDIA_ROOT, file_path))
56 - return response.Response(MediaSerializer(file, context={'request': request}).data) 55 + # return response.Response(MediaSerializer(file, context={'request': request}).data)
  56 + return res(MediaSerializer(file, context={'request': request}).data)
57 57
58 @staticmethod 58 @staticmethod
59 def allowed_extension(ext): 59 def allowed_extension(ext):
@@ -7,5 +7,5 @@ from rest_framework.pagination import PageNumberPagination @@ -7,5 +7,5 @@ from rest_framework.pagination import PageNumberPagination
7 7
8 class MyPageNumberPagination(PageNumberPagination): 8 class MyPageNumberPagination(PageNumberPagination):
9 page_size = 10 9 page_size = 10
10 - page_size_query_param = "size" 10 + page_size_query_param = "num"
11 page_query_param = "page" 11 page_query_param = "page"
@@ -106,7 +106,7 @@ AUTH_PASSWORD_VALIDATORS = [ @@ -106,7 +106,7 @@ AUTH_PASSWORD_VALIDATORS = [
106 106
107 LANGUAGE_CODE = 'zh-Hans' 107 LANGUAGE_CODE = 'zh-Hans'
108 108
109 -TIME_ZONE = 'UTC' 109 +TIME_ZONE = 'Asia/Shanghai'
110 110
111 USE_I18N = True 111 USE_I18N = True
112 112
@@ -129,9 +129,9 @@ RUNTIME_DIR = os.path.join(BASE_DIR, 'runtime') @@ -129,9 +129,9 @@ RUNTIME_DIR = os.path.join(BASE_DIR, 'runtime')
129 BASE_LOG_DIR = os.path.join(RUNTIME_DIR, "log") 129 BASE_LOG_DIR = os.path.join(RUNTIME_DIR, "log")
130 130
131 CORS_ORIGIN_WHITELIST = ( 131 CORS_ORIGIN_WHITELIST = (
132 - 'http://127.0.0.1:8000', 132 + 'http://127.0.0.1:8080',
133 'http://project.tacklifetools.com', 133 'http://project.tacklifetools.com',
134 - 'http://localhost:8000', # 凡是出现在白名单中的域名,都可以访问后端接口 134 + 'http://localhost:8080', # 凡是出现在白名单中的域名,都可以访问后端接口
135 ) 135 )
136 136
137 137
@@ -153,6 +153,7 @@ REST_FRAMEWORK = { @@ -153,6 +153,7 @@ REST_FRAMEWORK = {
153 'rest_framework.renderers.BrowsableAPIRenderer', 153 'rest_framework.renderers.BrowsableAPIRenderer',
154 # 'rest_framework_csv.renderers.CSVRenderer', 154 # 'rest_framework_csv.renderers.CSVRenderer',
155 ), 155 ),
  156 + 'DATETIME_FORMAT': "%Y-%m-%d %H:%M:%S"
156 157
157 } 158 }
158 159
1 -"""wxProject URL Configuration  
2 -  
3 -The `urlpatterns` list routes URLs to views. For more information please see:  
4 - https://docs.djangoproject.com/en/3.1/topics/http/urls/  
5 -Examples:  
6 -Function views  
7 - 1. Add an import: from my_app import views  
8 - 2. Add a URL to urlpatterns: path('', views.home, name='home')  
9 -Class-based views  
10 - 1. Add an import: from other_app.views import Home  
11 - 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')  
12 -Including another URLconf  
13 - 1. Import the include() function: from django.urls import include, path  
14 - 2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))  
15 -""" 1 +# _*_ coding: utf-8 _*_
  2 +# @Time : 2020/9/30 11:18
  3 +# @Author vanwhebin
16 from django.urls import path, re_path, include 4 from django.urls import path, re_path, include
17 from django.contrib import admin 5 from django.contrib import admin
18 from django.conf.urls.static import static 6 from django.conf.urls.static import static
19 from rest_framework_simplejwt.views import TokenRefreshView, TokenObtainPairView 7 from rest_framework_simplejwt.views import TokenRefreshView, TokenObtainPairView
20 from usercenter.views import MyTokenObtainPairView 8 from usercenter.views import MyTokenObtainPairView
21 from utils.media import UploadMedia 9 from utils.media import UploadMedia
  10 +from utils.helpers import WxUserlogin
22 from wxProject.settings import MEDIA_URL, MEDIA_ROOT 11 from wxProject.settings import MEDIA_URL, MEDIA_ROOT
23 12
24 13
@@ -32,4 +21,5 @@ urlpatterns = [ @@ -32,4 +21,5 @@ urlpatterns = [
32 path('api-auth/', include('rest_framework.urls', namespace='rest_framework')), 21 path('api-auth/', include('rest_framework.urls', namespace='rest_framework')),
33 re_path(r'api/token/refresh/', TokenRefreshView.as_view(), name='token_refresh'), 22 re_path(r'api/token/refresh/', TokenRefreshView.as_view(), name='token_refresh'),
34 re_path(r'api/token/', MyTokenObtainPairView.as_view(), name='token_obtain_pair'), 23 re_path(r'api/token/', MyTokenObtainPairView.as_view(), name='token_obtain_pair'),
  24 + re_path(r'api/login/', WxUserlogin.as_view(), name='wx_login'),
35 ] + static(MEDIA_URL, document_root=MEDIA_ROOT) 25 ] + static(MEDIA_URL, document_root=MEDIA_ROOT)