feat: 调整

This commit is contained in:
Czw996 2021-12-13 02:14:00 +08:00
parent 0708ad2c29
commit 519e25a682
21 changed files with 234 additions and 144 deletions

View file

@ -15,6 +15,8 @@ pillow = "*"
pendulum = "*"
number-precision = "*"
openpyxl = "*"
uvicorn = "*"
gunicorn = "*"
[dev-packages]
autopep8 = "*"

65
Pipfile.lock generated
View file

@ -1,7 +1,7 @@
{
"_meta": {
"hash": {
"sha256": "b2ec2d879fcb44eef57c547812ae911702fc346b27259c3eba01f17e1f5d7765"
"sha256": "b9799aee9ec6f6b223f92a087a9cdd7143d93c31b9effc4996968bcd9976d9cf"
},
"pipfile-spec": 6,
"requires": {
@ -32,13 +32,21 @@
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
"version": "==21.2.0"
},
"click": {
"hashes": [
"sha256:353f466495adaeb40b6b5f592f9f91cb22372351c84caeb068132442a4518ef3",
"sha256:410e932b050f5eed773c4cda94de75971c89cdb3155a72a0831139a79e5ecb5b"
],
"markers": "python_version >= '3.6'",
"version": "==8.0.3"
},
"django": {
"hashes": [
"sha256:51284300f1522ffcdb07ccbdf676a307c6678659e1284f0618e5a774127a6a08",
"sha256:e22c9266da3eec7827737cde57694d7db801fedac938d252bf27377cec06ed1b"
"sha256:59304646ebc6a77b9b6a59adc67d51ecb03c5e3d63ed1f14c909cdfda84e8010",
"sha256:d5a8a14da819a8b9237ee4d8c78dfe056ff6e8a7511987be627192225113ee75"
],
"index": "pypi",
"version": "==3.2.9"
"version": "==4.0"
},
"django-debug-toolbar": {
"hashes": [
@ -50,11 +58,11 @@
},
"django-extensions": {
"hashes": [
"sha256:50de8977794a66a91575dd40f87d5053608f679561731845edbd325ceeb387e3",
"sha256:5f0fea7bf131ca303090352577a9e7f8bfbf5489bd9d9c8aea9401db28db34a0"
"sha256:28e1e1bf49f0e00307ba574d645b0af3564c981a6dfc87209d48cb98f77d0b1a",
"sha256:9238b9e016bb0009d621e05cf56ea8ce5cce9b32e91ad2026996a7377ca28069"
],
"index": "pypi",
"version": "==3.1.3"
"version": "==3.1.5"
},
"django-filter": {
"hashes": [
@ -82,11 +90,11 @@
},
"drf-spectacular": {
"hashes": [
"sha256:af8a0c7c46e82c68aa70c474e3b23fa23bb16e4600270184af8230f5bd76aabb",
"sha256:cbc43c8b67bd52a4ff31c4c950419be5257b8a4718cb966e7d2876371692edc1"
"sha256:08357eefd71359ac522f3006c979f845ba09ec53638b84b8f9e920fa208dca92",
"sha256:72bf0c122a51f1431d44570b8a68e5da1bc0e961d8a895ab3a83861fef65565d"
],
"index": "pypi",
"version": "==0.20.2"
"version": "==0.21.0"
},
"et-xmlfile": {
"hashes": [
@ -96,6 +104,22 @@
"markers": "python_version >= '3.6'",
"version": "==1.1.0"
},
"gunicorn": {
"hashes": [
"sha256:9dcc4547dbb1cb284accfb15ab5667a0e5d1881cc443e0677b4882a4067a807e",
"sha256:e0a968b5ba15f8a328fdfd7ab1fcb5af4470c28aaf7e55df02a99bc13138e6e8"
],
"index": "pypi",
"version": "==20.1.0"
},
"h11": {
"hashes": [
"sha256:36a3cb8c0a032f56e2da7084577878a035d3b61d104230d4bd49c0c6b555a9c6",
"sha256:47222cb6067e4a307d535814917cd98fd0a57b6788ce715755fa2b6c28b56042"
],
"markers": "python_version >= '3.6'",
"version": "==0.12.0"
},
"inflection": {
"hashes": [
"sha256:1a29730d366e996aaacffb2f1f1cb9593dc38e2ddd30c91250c6dde09ea9b417",
@ -106,11 +130,11 @@
},
"jsonschema": {
"hashes": [
"sha256:2b563117f3659a7f433dffe1371c88f52115b79133493f376f15724b9caa7efa",
"sha256:e2d3601321ac74d38214e2853300ae740cd07e53d919a15862b8c71f9d840574"
"sha256:2a0f162822a64d95287990481b45d82f096e99721c86534f48201b64ebca6e8c",
"sha256:390713469ae64b8a58698bb3cbc3859abe6925b565a973f87323ef21b09a27a8"
],
"markers": "python_version >= '3.7'",
"version": "==4.2.0"
"version": "==4.2.1"
},
"number-precision": {
"hashes": [
@ -245,13 +269,6 @@
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==2.8.2"
},
"pytz": {
"hashes": [
"sha256:3672058bc3453457b622aab7a1c3bfd5ab0bdae451512f6cf25f64ed37f5b87c",
"sha256:acad2d8b20a1af07d4e4c9d2e9285c5ed9104354062f275f3fcd88dcef4f1326"
],
"version": "==2021.3"
},
"pytzdata": {
"hashes": [
"sha256:3efa13b335a00a8de1d345ae41ec78dd11c9f8807f522d39850f2dd828681540",
@ -322,6 +339,14 @@
],
"markers": "python_version >= '3.6'",
"version": "==4.1.1"
},
"uvicorn": {
"hashes": [
"sha256:d8c839231f270adaa6d338d525e2652a0b4a5f4c2430b5c4ef6ae4d11776b0d2",
"sha256:eacb66afa65e0648fcbce5e746b135d09722231ffffc61883d4fac2b62fbea8d"
],
"index": "pypi",
"version": "==0.16.0"
}
},
"develop": {

View file

@ -9,7 +9,7 @@ from apps.data.models import *
from apps.goods.models import *
class WarehouseViewSet(BaseViewSet, ReadWriteMixin):
class WarehouseViewSet(ModelViewSet):
"""仓库"""
serializer_class = WarehouseSerializer
@ -68,7 +68,7 @@ class WarehouseViewSet(BaseViewSet, ReadWriteMixin):
return Response(data=serializer.data, status=status.HTTP_200_OK)
class ClientViewSet(BaseViewSet, ReadWriteMixin):
class ClientViewSet(ModelViewSet):
"""客户"""
serializer_class = ClientSerializer
@ -95,7 +95,7 @@ class ClientViewSet(BaseViewSet, ReadWriteMixin):
return Response(data={'number': number}, status=status.HTTP_200_OK)
class SupplierViewSet(BaseViewSet, ReadWriteMixin):
class SupplierViewSet(ModelViewSet):
"""供应商"""
serializer_class = SupplierSerializer
@ -122,7 +122,7 @@ class SupplierViewSet(BaseViewSet, ReadWriteMixin):
return Response(data={'number': number}, status=status.HTTP_200_OK)
class AccountViewSet(BaseViewSet, ReadWriteMixin):
class AccountViewSet(ModelViewSet):
"""结算账户"""
serializer_class = AccountSerializer
@ -148,7 +148,7 @@ class AccountViewSet(BaseViewSet, ReadWriteMixin):
return Response(data={'number': number}, status=status.HTTP_200_OK)
class ChargeItemViewSet(BaseViewSet, ReadWriteMixin):
class ChargeItemViewSet(ModelViewSet):
"""收支项目"""
serializer_class = ChargeItemSerializer
@ -159,7 +159,7 @@ class ChargeItemViewSet(BaseViewSet, ReadWriteMixin):
queryset = ChargeItem.objects.all()
class ClientCategoryViewSet(BaseViewSet, ReadWriteMixin):
class ClientCategoryViewSet(ModelViewSet):
"""客户分类"""
serializer_class = ClientCategorySerializer
@ -169,7 +169,7 @@ class ClientCategoryViewSet(BaseViewSet, ReadWriteMixin):
queryset = ClientCategory.objects.all()
class SupplierCategoryViewSet(BaseViewSet, ReadWriteMixin):
class SupplierCategoryViewSet(ModelViewSet):
"""供应商分类"""
serializer_class = SupplierCategorySerializer
@ -179,7 +179,7 @@ class SupplierCategoryViewSet(BaseViewSet, ReadWriteMixin):
queryset = SupplierCategory.objects.all()
class GoodsCategoryViewSet(BaseViewSet, ReadWriteMixin):
class GoodsCategoryViewSet(ModelViewSet):
"""商品分类"""
serializer_class = GoodsCategorySerializer
@ -189,7 +189,7 @@ class GoodsCategoryViewSet(BaseViewSet, ReadWriteMixin):
queryset = GoodsCategory.objects.all()
class GoodsUnitViewSet(BaseViewSet, ReadWriteMixin):
class GoodsUnitViewSet(ModelViewSet):
"""商品单位"""
serializer_class = GoodsUnitSerializer

View file

@ -9,7 +9,7 @@ from apps.goods.models import *
from apps.data.models import *
class GoodsViewSet(BaseViewSet, ReadWriteMixin):
class GoodsViewSet(ModelViewSet):
"""商品"""
serializer_class = GoodsSerializer

View file

@ -2,4 +2,4 @@ from django.contrib import admin
from apps.system.models import *
admin.site.register([Team, PermissionType, Permission, Role, User])
admin.site.register([Team, PermissionGroup, Permission, Role, User])

View file

@ -52,7 +52,7 @@ class UserSerializer(BaseSerializer):
return instances
def create(self, validated_data):
validated_data['password'] = make_password(self.team.number)
validated_data['password'] = make_password(validated_data['username'])
return super().create(validated_data)
def save(self, **kwargs):

View file

@ -3,7 +3,7 @@ from apps.system.views import *
router = BaseRouter()
router.register('permission_types', PermissionTypeViewSet, 'permission_type')
router.register('permission_groups', PermissionGroupViewSet, 'permission_group')
router.register('roles', RoleViewSet, 'role')
router.register('users', UserViewSet, 'user')
router.register('user', UserActionViewSet, 'user_action')

View file

@ -1,6 +1,8 @@
from django.contrib.auth.hashers import make_password, check_password
from rest_framework_simplejwt.exceptions import TokenError
from rest_framework_simplejwt.tokens import RefreshToken
from extensions.common.base import *
from extensions.common.schema import *
from extensions.permissions import *
from extensions.exceptions import *
from extensions.viewsets import *
@ -9,29 +11,54 @@ from apps.system.schemas import *
from apps.system.models import *
class PermissionTypeViewSet(GenericViewSet, ListModelMixin):
"""权限类型"""
class PermissionGroupViewSet(BaseViewSet, ListModelMixin):
"""权限分组"""
serializer_class = PermissionTypeSerializer
serializer_class = PermissionGroupSerializer
permission_classes = [IsAuthenticated]
pagination_class = None
ordering = ['id']
queryset = PermissionType.objects.all()
queryset = PermissionGroup.objects.all()
def get_queryset(self):
return super().get_queryset().prefetch_related('permissions')
class RoleViewSet(BaseViewSet, ReadWriteMixin):
class RoleViewSet(ModelViewSet):
"""角色"""
serializer_class = RoleSerializer
permission_classes = [IsAuthenticated, IsManagerPermission]
search_fields = ['name', 'remark']
search_fields = ['name']
queryset = Role.objects.all()
@transaction.atomic
def perform_update(self, serializer):
role = serializer.save()
class UserViewSet(BaseViewSet, ReadWriteMixin):
# 同步用户权限
users = role.users.prefetch_related('roles', 'roles__permissions').all()
for user in users:
permissions = {permission.code for role in user.roles.all()
for permission in role.permissions.all()}
user.permissions = list(permissions)
else:
User.objects.bulk_update(users, ['permissions'])
@transaction.atomic
def perform_destroy(self, instance):
users = instance.users.all()
instance.delete()
# 同步用户权限
for user in users.prefetch_related('roles', 'roles__permissions').all():
permissions = {permission.code for role in user.roles.all()
for permission in role.permissions.all()}
user.permissions = list(permissions)
else:
User.objects.bulk_update(users, ['permissions'])
class UserViewSet(ModelViewSet):
"""用户"""
serializer_class = UserSerializer
@ -56,7 +83,7 @@ class UserViewSet(BaseViewSet, ReadWriteMixin):
"""重置密码"""
instance = self.get_object()
instance.password = make_password(self.team.number)
instance.password = make_password(self.user.username)
instance.save(update_fields=['password'])
return Response(status=status.HTTP_200_OK)
@ -131,5 +158,5 @@ class UserActionViewSet(FunctionViewSet):
__all__ = [
'PermissionTypeViewSet', 'RoleViewSet', 'UserViewSet', 'UserActionViewSet',
'PermissionGroupViewSet', 'RoleViewSet', 'UserViewSet', 'UserActionViewSet',
]

View file

@ -1,7 +1,14 @@
from pathlib import Path
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True
# Database
# https://docs.djangoproject.com/en/3.2/ref/settings/#databases
BASE_DIR = Path(__file__).resolve().parent.parent
DATABASES = {
'default': {

6
configs/gunicorn.py Normal file
View file

@ -0,0 +1,6 @@
import multiprocessing
bind = '0.0.0.0:8000'
workers = multiprocessing.cpu_count() * 2 + 1
reload = True

23
configs/nginx.conf Normal file
View file

@ -0,0 +1,23 @@
server {
listen 12222;
charset utf-8;
gzip_static on;
location / {
root /home/oms/frontend/dist/;
index index.html index.html;
try_files $uri $uri/ /index.html;
}
location /api/ {
proxy_pass http://localhost:12223/api/;
proxy_set_header Host $http_host;
proxy_set_header X-Forwarded-Proto $scheme;
}
location /media/ {
proxy_pass http://localhost:12223/media/;
proxy_set_header Host $http_host;
proxy_set_header X-Forwarded-Proto $scheme;
}
}

View file

@ -27,4 +27,5 @@ python manage.py runscript init_permission
python manage.py runscript create_user
python manage.py runscript create_test_data
python manage.py runserver
gunicorn project.asgi:application -c configs/gunicorn.py -k uvicorn.workers.UvicornWorker
```

View file

@ -1,7 +1,7 @@
# 角色权限
- 权限列表:
[/api/permission_types]
[/api/permission_groups]
- 查询创建角色:
[/api/roles/]

View file

@ -8,8 +8,8 @@ from django.conf import settings
class BaseAuthentication(JWTAuthentication):
def authenticate(self, request):
# if settings.DEBUG:
# return User.objects.all().first(), {}
if settings.DEBUG:
return User.objects.all().first(), {}
if (header := self.get_header(request)) is None:
return None

View file

View file

@ -11,10 +11,7 @@ https://docs.djangoproject.com/en/3.2/ref/settings/
"""
from datetime import timedelta
from pathlib import Path
# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent
from configs.django import *
# Quick-start development settings - unsuitable for production
@ -23,9 +20,6 @@ BASE_DIR = Path(__file__).resolve().parent.parent
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = 'django-insecure-v0ujuj#$uwo-cv&4@2=-0g3fi@av13=*g9%+jryd@5m568+93+'
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True
ALLOWED_HOSTS = ['*']
@ -46,18 +40,18 @@ INSTALLED_APPS = [
'debug_toolbar',
'apps.system',
'apps.data',
'apps.goods',
'apps.purchase',
'apps.sales',
'apps.stock_in',
'apps.stock_out',
'apps.stock_check',
'apps.stock_transfer',
'apps.flow',
'apps.finance',
'apps.statistic',
'apps.option',
# 'apps.data',
# 'apps.goods',
# 'apps.purchase',
# 'apps.sales',
# 'apps.stock_in',
# 'apps.stock_out',
# 'apps.stock_check',
# 'apps.stock_transfer',
# 'apps.flow',
# 'apps.finance',
# 'apps.statistic',
# 'apps.option',
]
MIDDLEWARE = [
@ -92,19 +86,6 @@ TEMPLATES = [
WSGI_APPLICATION = 'project.wsgi.application'
# Database
# https://docs.djangoproject.com/en/3.2/ref/settings/#databases
from configs.database import DATABASES
# DATABASES = {
# 'default': {
# 'ENGINE': 'django.db.backends.sqlite3',
# 'NAME': BASE_DIR / 'db.sqlite3',
# }
# }
# Password validation
# https://docs.djangoproject.com/en/3.2/ref/settings/#auth-password-validators

View file

@ -32,16 +32,16 @@ urlpatterns = [
*static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT),
path('api/', include('apps.system.urls')),
path('api/', include('apps.data.urls')),
path('api/', include('apps.goods.urls')),
path('api/', include('apps.purchase.urls')),
path('api/', include('apps.sales.urls')),
path('api/', include('apps.stock_in.urls')),
path('api/', include('apps.stock_out.urls')),
path('api/', include('apps.stock_check.urls')),
path('api/', include('apps.stock_transfer.urls')),
path('api/', include('apps.flow.urls')),
path('api/', include('apps.finance.urls')),
path('api/', include('apps.statistic.urls')),
path('api/', include('apps.option.urls')),
# path('api/', include('apps.data.urls')),
# path('api/', include('apps.goods.urls')),
# path('api/', include('apps.purchase.urls')),
# path('api/', include('apps.sales.urls')),
# path('api/', include('apps.stock_in.urls')),
# path('api/', include('apps.stock_out.urls')),
# path('api/', include('apps.stock_check.urls')),
# path('api/', include('apps.stock_transfer.urls')),
# path('api/', include('apps.flow.urls')),
# path('api/', include('apps.finance.urls')),
# path('api/', include('apps.statistic.urls')),
# path('api/', include('apps.option.urls')),
]

View file

@ -1,16 +1,19 @@
asgiref==3.4.1
attrs==21.2.0
autopep8==1.6.0
Django==3.2.9
click==8.0.3
Django==4.0
django-debug-toolbar==3.2.2
django-extensions==3.1.3
django-extensions==3.1.5
django-filter==21.1
djangorestframework==3.12.4
djangorestframework-simplejwt==5.0.0
drf-spectacular==0.20.2
drf-spectacular==0.21.0
et-xmlfile==1.1.0
gunicorn==20.1.0
h11==0.12.0
inflection==0.5.1
jsonschema==4.2.0
jsonschema==4.2.1
number-precision==2021.1.30
openpyxl==3.0.9
pendulum==2.1.2
@ -26,3 +29,4 @@ six==1.16.0
sqlparse==0.4.2
toml==0.10.2
uritemplate==4.1.1
uvicorn==0.16.0

View file

@ -1,4 +1,4 @@
from apps.system.models import PermissionType, Permission
from apps.system.models import PermissionGroup, Permission
PERMISSIONS = [
@ -12,9 +12,9 @@ PERMISSIONS = [
def run(*args):
PermissionType.objects.all().delete()
PermissionGroup.objects.all().delete()
for permission_type_item in PERMISSIONS:
permission_type = PermissionType.objects.create(name=permission_type_item['name'])
Permission.objects.bulk_create([Permission(type=permission_type, name=item['name'], code=item['code'])
for item in permission_type_item['permissions']])
for permission_group_item in PERMISSIONS:
permission_group = PermissionGroup.objects.create(name=permission_group_item['name'])
Permission.objects.bulk_create([Permission(group=permission_group, name=item['name'], code=item['code'])
for item in permission_group_item['permissions']])

View file

@ -5,8 +5,8 @@ BASE_DIR = Path.cwd()
def run():
create_nginx_config()
create_database_config()
create_uwsgi_config()
create_django_config()
create_gunicorn_config()
def create_nginx_config():
@ -16,8 +16,8 @@ def create_nginx_config():
server_port = input('请输入 Django 启动端口:\n')
static_path = BASE_DIR / 'frontend/dist/'
with open('/etc/nginx/sites-enabled/default', 'w') as file:
file.write(f"""
with open('configs/nginx.conf', 'w') as file:
file.write(f"""\
server {{
listen {listen_port};
charset utf-8;
@ -44,17 +44,40 @@ server {{
""")
def create_database_config():
is_need_create = input('是否需要创建 数据库 配置文件吗? (y/n)\n')
if is_need_create == 'y':
database_type = input('配置数据库: (sqlite: 0, mysql: 1)\n')
if database_type == '1':
host = input('请输入 host:\n')
user = input('请输入 user:\n')
passowrd = input('请输入 passowrd:\n')
database_name = input('请输入 数据库名称:\n')
def create_django_config():
is_production_environment = input('是否为生产环境? (y/n)\n')
if is_production_environment == 'y':
file_content = """\
from pathlib import Path
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = False
"""
else:
file_content = """\
from pathlib import Path
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True
"""
database_type = input('配置数据库: (sqlite: 0, mysql: 1)\n')
if database_type == '1':
host = input('请输入 host:\n')
user = input('请输入 user:\n')
passowrd = input('请输入 passowrd:\n')
database_name = input('请输入 数据库名称:\n')
file_content += f"""
# Database
# https://docs.djangoproject.com/en/3.2/ref/settings/#databases
text = f"""
DATABASES = {{
'default': {{
'ENGINE': 'django.db.backends.mysql',
@ -67,10 +90,10 @@ DATABASES = {{
}}
}}
"""
else:
text = f"""
from pathlib import Path
else:
file_content += f"""
# Database
# https://docs.djangoproject.com/en/3.2/ref/settings/#databases
BASE_DIR = Path(__file__).resolve().parent.parent
DATABASES = {{
@ -80,34 +103,23 @@ DATABASES = {{
}}
}}
"""
with open(BASE_DIR / 'configs/database.py', 'w') as file:
file.write(text)
with open(BASE_DIR / 'configs/django.py', 'w') as file:
file.write(file_content)
def create_uwsgi_config():
is_need_create = input('是否需要创建 uwsgi 配置文件吗? (y/n)\n')
def create_gunicorn_config():
is_need_create = input('是否需要创建 Gunicorn 配置文件吗? (y/n)\n')
if is_need_create == 'y':
http_port = input('请输入项目端口:\n')
pidfile_path = BASE_DIR / 'logs/master.pid'
daemonize_path = BASE_DIR / 'logs/worker.log'
pidfile_path.touch()
daemonize_path.touch()
bind_address = input('请输入 Django 启动地址:\n')
with open(BASE_DIR / 'configs/uwsgi.ini', 'w') as file:
file.write(f"""
[uwsgi]
chdir = {BASE_DIR}
module = project.wsgi:application
master = True
processes = 8
max-requests = 5000
harakiri = 60
http = :{http_port}
uid = root
gid = root
pidfile = {pidfile_path}
daemonize = {daemonize_path}
vacuum = True
with open('configs/gunicorn.py', 'w') as file:
file.write(f"""\
import multiprocessing
bind = '{bind_address}'
workers = multiprocessing.cpu_count() * 2 + 1
reload = True
""")

View file

@ -14,10 +14,12 @@ for app in (project_path / 'apps').iterdir():
if file.is_file() and file.name != '__init__.py':
file.unlink()
# Python3 manage.py
# Python3 manage.py
print('构建数据库')
os.chdir(project_path)
os.system('python manage.py reset_db --noinput')
os.system('python manage.py makemigrations')
os.system('python manage.py migrate')
print('初始化权限')
os.system('python manage.py runscript init_permission')