正在显示
33 个修改的文件
包含
420 行增加
和
48 行删除
.idea/dataSources.xml
0 → 100644
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 |
不能预览此文件类型
libs/qywx/__pycache__/CorpApi.cpython-38.pyc
0 → 100644
不能预览此文件类型
libs/qywx/__pycache__/conf.cpython-38.pyc
0 → 100644
不能预览此文件类型
@@ -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, ) |
project/views_cbv.py
0 → 100644
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) |
static/media/2020-10-07/php多进程模型.pdf
0 → 100644
不能预览此文件类型
不能预览此文件类型
不能预览此文件类型
static/media/2020-10-07/深入理解ES6翻译完整版.pdf
0 → 100644
不能预览此文件类型
@@ -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 | + |
utils/helpers.py
0 → 100644
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) |
-
请 注册 或 登录 后发表评论