first commit
This commit is contained in:
99
code/.gitignore
vendored
Normal file
99
code/.gitignore
vendored
Normal file
@@ -0,0 +1,99 @@
|
||||
# Byte-compiled / optimized / DLL files
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
__pycache__/
|
||||
# C extensions
|
||||
*.so
|
||||
|
||||
# Distribution / packaging
|
||||
.Python
|
||||
env/
|
||||
develop-eggs/
|
||||
dist/
|
||||
downloads/
|
||||
eggs/
|
||||
.eggs/
|
||||
lib/
|
||||
lib64/
|
||||
parts/
|
||||
sdist/
|
||||
var/
|
||||
*.egg-info/
|
||||
.installed.cfg
|
||||
*.egg
|
||||
|
||||
# PyInstaller
|
||||
# Usually these files are written by a python script from a template
|
||||
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
||||
*.manifest
|
||||
*.spec
|
||||
|
||||
# Installer logs
|
||||
pip-log.txt
|
||||
pip-delete-this-directory.txt
|
||||
|
||||
# Unit test / coverage reports
|
||||
htmlcov/
|
||||
.tox/
|
||||
.coverage
|
||||
.coverage.*
|
||||
.cache
|
||||
nosetests.xml
|
||||
coverage.xml
|
||||
*,cover
|
||||
.hypothesis/
|
||||
|
||||
# Translations
|
||||
*.mo
|
||||
*.pot
|
||||
|
||||
# Django stuff:
|
||||
*.log
|
||||
local_settings.py
|
||||
|
||||
# Flask stuff:
|
||||
instance/
|
||||
.webassets-cache
|
||||
|
||||
# Scrapy stuff:
|
||||
.scrapy
|
||||
|
||||
# Sphinx documentation
|
||||
docs/_build/
|
||||
|
||||
# PyBuilder
|
||||
target/
|
||||
|
||||
# IPython Notebook
|
||||
.ipynb_checkpoints
|
||||
|
||||
# pyenv
|
||||
.python-version
|
||||
|
||||
# celery beat schedule file
|
||||
celerybeat-schedule
|
||||
|
||||
# dotenv
|
||||
.env
|
||||
|
||||
# virtualenv
|
||||
venv/
|
||||
ENV/
|
||||
|
||||
# Spyder project settings
|
||||
.spyderproject
|
||||
|
||||
# Rope project settings
|
||||
.ropeproject
|
||||
.idea/
|
||||
*.db
|
||||
.DS_Store
|
||||
__pycache__
|
||||
**/migrations
|
||||
!**/migrations/__init__.py
|
||||
*.pyc
|
||||
|
||||
!conf/env.example.py
|
||||
db.sqlite3
|
||||
media/
|
||||
static/
|
||||
18
code/.vscode/c_cpp_properties.json
vendored
Normal file
18
code/.vscode/c_cpp_properties.json
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"configurations": [
|
||||
{
|
||||
"name": "windows-gcc-x64",
|
||||
"includePath": [
|
||||
"${workspaceFolder}/**"
|
||||
],
|
||||
"compilerPath": "E:/software/mingw/mingw64/bin/gcc.exe",
|
||||
"cStandard": "${default}",
|
||||
"cppStandard": "${default}",
|
||||
"intelliSenseMode": "windows-gcc-x64",
|
||||
"compilerArgs": [
|
||||
""
|
||||
]
|
||||
}
|
||||
],
|
||||
"version": 4
|
||||
}
|
||||
24
code/.vscode/launch.json
vendored
Normal file
24
code/.vscode/launch.json
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "C/C++ Runner: Debug Session",
|
||||
"type": "cppdbg",
|
||||
"request": "launch",
|
||||
"args": [],
|
||||
"stopAtEntry": false,
|
||||
"externalConsole": true,
|
||||
"cwd": "e:/projects/wxwx/projects_job/VDES_Projects/C_project",
|
||||
"program": "e:/projects/wxwx/projects_job/VDES_Projects/C_project/build/Debug/outDebug",
|
||||
"MIMode": "gdb",
|
||||
"miDebuggerPath": "gdb",
|
||||
"setupCommands": [
|
||||
{
|
||||
"description": "Enable pretty-printing for gdb",
|
||||
"text": "-enable-pretty-printing",
|
||||
"ignoreFailures": true
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
64
code/.vscode/settings.json
vendored
Normal file
64
code/.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,64 @@
|
||||
{
|
||||
"C_Cpp_Runner.cCompilerPath": "gcc",
|
||||
"C_Cpp_Runner.cppCompilerPath": "g++",
|
||||
"C_Cpp_Runner.debuggerPath": "gdb",
|
||||
"C_Cpp_Runner.cStandard": "",
|
||||
"C_Cpp_Runner.cppStandard": "",
|
||||
"C_Cpp_Runner.msvcBatchPath": "C:/Program Files/Microsoft Visual Studio/VR_NR/Community/VC/Auxiliary/Build/vcvarsall.bat",
|
||||
"C_Cpp_Runner.useMsvc": false,
|
||||
"C_Cpp_Runner.warnings": [
|
||||
"-Wall",
|
||||
"-Wextra",
|
||||
"-Wpedantic",
|
||||
"-Wshadow",
|
||||
"-Wformat=2",
|
||||
"-Wcast-align",
|
||||
"-Wconversion",
|
||||
"-Wsign-conversion",
|
||||
"-Wnull-dereference"
|
||||
],
|
||||
"C_Cpp_Runner.msvcWarnings": [
|
||||
"/W4",
|
||||
"/permissive-",
|
||||
"/w14242",
|
||||
"/w14287",
|
||||
"/w14296",
|
||||
"/w14311",
|
||||
"/w14826",
|
||||
"/w44062",
|
||||
"/w44242",
|
||||
"/w14905",
|
||||
"/w14906",
|
||||
"/w14263",
|
||||
"/w44265",
|
||||
"/w14928"
|
||||
],
|
||||
"C_Cpp_Runner.enableWarnings": true,
|
||||
"C_Cpp_Runner.warningsAsError": false,
|
||||
"C_Cpp_Runner.compilerArgs": [],
|
||||
"C_Cpp_Runner.linkerArgs": [],
|
||||
"C_Cpp_Runner.includePaths": [],
|
||||
"C_Cpp_Runner.includeSearch": [
|
||||
"*",
|
||||
"**/*"
|
||||
],
|
||||
"C_Cpp_Runner.excludeSearch": [
|
||||
"**/build",
|
||||
"**/build/**",
|
||||
"**/.*",
|
||||
"**/.*/**",
|
||||
"**/.vscode",
|
||||
"**/.vscode/**"
|
||||
],
|
||||
"C_Cpp_Runner.useAddressSanitizer": false,
|
||||
"C_Cpp_Runner.useUndefinedSanitizer": false,
|
||||
"C_Cpp_Runner.useLeakSanitizer": false,
|
||||
"C_Cpp_Runner.showCompilationTime": false,
|
||||
"C_Cpp_Runner.useLinkTimeOptimization": false,
|
||||
"C_Cpp_Runner.msvcSecureNoWarnings": false,
|
||||
"files.associations": {
|
||||
"math.h": "c",
|
||||
"windows.h": "c",
|
||||
"winsock2.h": "c"
|
||||
}
|
||||
}
|
||||
146
code/README.md
Normal file
146
code/README.md
Normal file
@@ -0,0 +1,146 @@
|
||||
# FuAdmin
|
||||
|
||||
[](https://gitee.com/fuadmin/fu-admin/blob/master/LICENSE) [](https://python.org/) [](https://docs.djangoproject.com/zh-hans/4.0.4/) [](https://nodejs.org/zh-cn/) [](https://gitee.com/fuadmin/fu-admin) [](https://github.com/FuAdmin/FuAdmin)
|
||||
|
||||
## 为什么使用 Django-Ninja 和 Vue3
|
||||
|
||||
💡 **「Django Ninja 相比于 Django Rest Framework 」**
|
||||
|
||||
- 简单:旨在易于使用和直观,直接使用@装饰器(java好像叫注解)开发api
|
||||
|
||||
- 快速执行:由于 Pydantic 和异步的支持,性能非常高。
|
||||
|
||||
- 快速编码:输入提示和自动文档让您只关注业务逻辑。
|
||||
|
||||
- 基于标准:基于 API 的开放标准:OpenAPI(以前称为 Swagger)和 JSON Schema。
|
||||
|
||||
- Django 集成:与 Django 自身的功能和 ORM 有很好的集成。
|
||||
|
||||

|
||||
|
||||
💡 **「Vue3 」**
|
||||
|
||||
- 最火的框架,国内最火的前端框架之一。
|
||||
- 性能提升,运行速度是 vue2 的 1.5 倍。
|
||||
- 体积更小,按需编译体积 vue2 要更小。
|
||||
- 类型推断,更好的支持 ts 这个也是趋势。
|
||||
- 高级给予,暴露了更底层的 API 和提供更先进的内置组件。
|
||||
- 组合 API,能够更好的组织逻辑,封装逻辑,复用逻辑
|
||||
|
||||
## 平台简介
|
||||
|
||||
💡 [fu-admin-web](https://gitee.com/fuadmin/fu-admin-web) 采用 VUE3,TS 开发。 💡 [fu-admin-backend](https://gitee.com/fuadmin/fu-admin-backend) 采用 Python,Django 和 Django-Ninija 开发。数据库支持MySql,SqlServer,Sqlite。
|
||||
|
||||
- 🧑🤝🧑 前端采用[VbenAdmin](https://vvbin.cn/doc-next) 、[Vue3](https://cn.vuejs.org/)、[Ant Design Vue](https://www.antdv.com/docs/vue/getting-started-cn)。
|
||||
- 👭 后端采用 Python 语言 Django 框架以及强大的 [Django Ninja](https://django-ninja.rest-framework.com/)。
|
||||
- 👬 支持加载动态权限菜单,多方式轻松权限控制。
|
||||
- 💡 Vue2 项目移步[Dvadmin](https://gitee.com/liqianglog/django-vue-admin)
|
||||
- 💏 特别鸣谢:[VbenAdmin](https://github.com/vbenjs/vue-vben-admin) 、[Ant Design Vue](https://github.com/vueComponent/ant-design-vue)。
|
||||
- 💡 特别感谢[jetbrains](https://www.jetbrains.com/) 为本开源项目提供免费的 IntelliJ IDEA 授权。
|
||||
|
||||
## 在线体验
|
||||
|
||||
👩👧👦 演示地址:[http://175.24.184.165:8080](http://175.24.184.165:8080)
|
||||
|
||||
- 账号:superadmin
|
||||
|
||||
- 密码:123456
|
||||
|
||||
|
||||
## 交流
|
||||
|
||||
- FuAdmin 交流群:726947525
|
||||
|
||||
<img src="backend/utils/picture/qq.png" alt="qq 群" style="zoom: 25%;" />
|
||||
|
||||
## 源码地址
|
||||
|
||||
| | 项目地址 |
|
||||
|--------|-------------------------------------|
|
||||
| github | https://github.com/FuAdmin/fu-admin |
|
||||
| 码云 | https://gitee.com/fuadmin/fu-admin |
|
||||
|
||||
## 内置功能
|
||||
|
||||
1. 👨⚕️ 菜单管理:配置系统菜单,操作权限,按钮权限标识、后端接口权限等。
|
||||
2. 🧑⚕️ 部门管理:配置系统组织机构(公司、部门、角色)。
|
||||
3. 👩⚕️ 角色管理:角色菜单权限分配、数据权限分配、设置角色按部门进行数据范围权限划分。
|
||||
4. 🧑🎓 权限权限:授权角色的权限范围。
|
||||
5. 👨🎓 用户管理:用户是系统操作者,该功能主要完成系统用户配置。
|
||||
6. 🧑🔧 数据字典:对系统中经常使用的一些较为固定的数据进行维护。
|
||||
7. 🧑🔧 分类字典:对系统中经常使用的一些树形数据进行维护。
|
||||
8. 📁 附件管理:对平台上所有文件、图片等进行统一管理。
|
||||
9. 🗓️ 操作日志:系统正常操作日志记录和查询;系统异常信息日志记录和查询。
|
||||
10. 定时任务:系统设置定时任务。
|
||||
|
||||
## 准备工作
|
||||
|
||||
```
|
||||
Python >= 3.8.0 (推荐3.8+版本)
|
||||
nodejs >= 16.0 (推荐最新)
|
||||
Mysql >= 5.7.0 (可选,默认数据库sqlite3,推荐8.0版本)
|
||||
Redis(可选,最新版)
|
||||
```
|
||||
|
||||
## 前端 ♝
|
||||
|
||||
```bash
|
||||
# 克隆项目
|
||||
git https://gitee.com/fuadmin/fu-admin.git
|
||||
|
||||
# 进入项目目录
|
||||
cd web
|
||||
|
||||
# 安装依赖
|
||||
yarn install --registry=https://registry.npm.taobao.org
|
||||
|
||||
# 启动服务
|
||||
yarn run dev
|
||||
# 浏览器访问 http://localhost:8080
|
||||
# .env 文件中可配置启动端口等参数
|
||||
# 构建生产环境
|
||||
# yarn run build
|
||||
```
|
||||
|
||||
## 后端 💈
|
||||
|
||||
```bash
|
||||
# 克隆项目
|
||||
git https://gitee.com/fuadmin/fu-admin.git
|
||||
# 进入项目目录
|
||||
cd backend
|
||||
# 在 `env.py` 中配置数据库信息
|
||||
# 默认是Mysql,如果使用SqlServer,qing在requirements.txt中打开
|
||||
mssql-django==1.1.2
|
||||
pyodbc==4.0.32
|
||||
# 安装依赖环境
|
||||
pip3 install -r requirements.txt
|
||||
# 执行迁移命令:
|
||||
python3 manage.py makemigrations system
|
||||
python3 manage.py migrate
|
||||
# 初始化数据
|
||||
python3 manage.py init
|
||||
# 初始化省市县数据:
|
||||
python3 manage.py init_area
|
||||
# 启动项目
|
||||
python3 manage.py runserver 0.0.0.0:8000
|
||||
# 或使用 daphne :
|
||||
daphne -b 0.0.0.0 -p 8000 application.asgi:application
|
||||
```
|
||||
|
||||
### 访问项目
|
||||
|
||||
- 文档访问地址:[http://localhost:8080/api/docs](http://localhost:8080/api/docs) (默认为此地址,如有修改请按照配置文件)
|
||||
- 账号:`superadmin` 密码:`123456`
|
||||
|
||||
## 演示图 ✅
|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||
318
code/bk/settings.txt
Normal file
318
code/bk/settings.txt
Normal file
@@ -0,0 +1,318 @@
|
||||
"""
|
||||
Django settings for fuadmin project.
|
||||
|
||||
Generated by 'django-admin startproject' using Django 4.0.4.
|
||||
|
||||
For more information on this file, see
|
||||
https://docs.djangoproject.com/en/4.0/topics/settings/
|
||||
|
||||
For the full list of settings and their values, see
|
||||
https://docs.djangoproject.com/en/4.0/ref/settings/
|
||||
"""
|
||||
import os
|
||||
from datetime import timedelta
|
||||
from pathlib import Path
|
||||
from conf.env import *
|
||||
|
||||
# Build paths inside the project like this: BASE_DIR / 'subdir'.
|
||||
BASE_DIR = Path(__file__).resolve().parent.parent
|
||||
|
||||
# Quick-start development settings - unsuitable for production
|
||||
# See https://docs.djangoproject.com/en/4.0/howto/deployment/checklist/
|
||||
|
||||
# SECURITY WARNING: keep the secret key used in production secret!
|
||||
SECRET_KEY = 'django-insecure-x4k4^#6wovi1aep8%ow!5fr%(9o#1u=+0+nzi($_j=^d*ui6g3'
|
||||
|
||||
# SECURITY WARNING: don't run with debug turned on in production!
|
||||
DEBUG = locals().get('DEBUG', True)
|
||||
ALLOWED_HOSTS = locals().get('ALLOWED_HOSTS', ['*'])
|
||||
DEMO = locals().get('DEMO', False)
|
||||
|
||||
# Application definition
|
||||
|
||||
INSTALLED_APPS = [
|
||||
'django.contrib.admin',
|
||||
'django.contrib.auth',
|
||||
'django.contrib.contenttypes',
|
||||
'django.contrib.sessions',
|
||||
'django.contrib.messages',
|
||||
'django.contrib.staticfiles',
|
||||
'django_celery_beat',
|
||||
'django_celery_results',
|
||||
'system',
|
||||
'demo',
|
||||
'generator',
|
||||
]
|
||||
|
||||
MIDDLEWARE = [
|
||||
'django.middleware.security.SecurityMiddleware',
|
||||
'django.contrib.sessions.middleware.SessionMiddleware',
|
||||
'django.middleware.common.CommonMiddleware',
|
||||
'django.middleware.csrf.CsrfViewMiddleware',
|
||||
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
||||
'django.contrib.messages.middleware.MessageMiddleware',
|
||||
'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
||||
'utils.middleware.ApiLoggingMiddleware',
|
||||
|
||||
]
|
||||
|
||||
ROOT_URLCONF = 'fuadmin.urls'
|
||||
|
||||
TEMPLATES = [
|
||||
{
|
||||
'BACKEND': 'django.template.backends.django.DjangoTemplates',
|
||||
'DIRS': [BASE_DIR / 'templates']
|
||||
,
|
||||
'APP_DIRS': True,
|
||||
'OPTIONS': {
|
||||
'context_processors': [
|
||||
'django.template.context_processors.debug',
|
||||
'django.template.context_processors.request',
|
||||
'django.contrib.auth.context_processors.auth',
|
||||
'django.contrib.messages.context_processors.messages',
|
||||
],
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
WSGI_APPLICATION = 'fuadmin.wsgi.application'
|
||||
|
||||
# Password validation
|
||||
# https://docs.djangoproject.com/en/4.0/ref/settings/#auth-password-validators
|
||||
|
||||
AUTH_PASSWORD_VALIDATORS = [
|
||||
{
|
||||
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
|
||||
},
|
||||
{
|
||||
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
|
||||
},
|
||||
{
|
||||
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
|
||||
},
|
||||
{
|
||||
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
|
||||
},
|
||||
]
|
||||
|
||||
# Internationalization
|
||||
# https://docs.djangoproject.com/en/4.0/topics/i18n/
|
||||
|
||||
LANGUAGE_CODE = 'zh-hans'
|
||||
|
||||
TIME_ZONE = 'Asia/Shanghai'
|
||||
|
||||
USE_I18N = True
|
||||
|
||||
USE_L10N = True
|
||||
|
||||
USE_TZ = False
|
||||
|
||||
# Static files (CSS, JavaScript, Images)
|
||||
# https://docs.djangoproject.com/en/4.0/howto/static-files/
|
||||
|
||||
STATIC_URL = 'static/'
|
||||
|
||||
# Default primary key field type
|
||||
# https://docs.djangoproject.com/en/4.0/ref/settings/#default-auto-field
|
||||
|
||||
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
|
||||
|
||||
AUTH_USER_MODEL = 'system.Users'
|
||||
USERNAME_FIELD = 'username'
|
||||
ALL_MODELS_OBJECTS = [] # 所有app models 对象
|
||||
|
||||
# Database
|
||||
# https://docs.djangoproject.com/en/4.0/ref/settings/#databases
|
||||
# 数据库配置
|
||||
if DATABASE_TYPE == "MYSQL":
|
||||
# Mysql数据库
|
||||
DATABASES = {
|
||||
"default": {
|
||||
"ENGINE": "django.db.backends.mysql",
|
||||
"HOST": DATABASE_HOST,
|
||||
"PORT": DATABASE_PORT,
|
||||
"USER": DATABASE_USER,
|
||||
"PASSWORD": DATABASE_PASSWORD,
|
||||
"NAME": DATABASE_NAME,
|
||||
}
|
||||
}
|
||||
elif DATABASE_TYPE == "SQLSERVER":
|
||||
# SqlServer数据库
|
||||
DATABASES = {
|
||||
"default": {
|
||||
"ENGINE": "mssql",
|
||||
"HOST": DATABASE_HOST,
|
||||
"PORT": DATABASE_PORT,
|
||||
"USER": DATABASE_USER,
|
||||
"PASSWORD": DATABASE_PASSWORD,
|
||||
"NAME": DATABASE_NAME,
|
||||
# 全局开启事务,绑定的是http请求响应整个过程
|
||||
'ATOMIC_REQUESTS': True,
|
||||
'OPTIONS': {
|
||||
'driver': 'ODBC Driver 17 for SQL Server',
|
||||
},
|
||||
}
|
||||
}
|
||||
elif DATABASE_TYPE == "POSTGRESQL":
|
||||
# POSTGRESQL
|
||||
DATABASES = {
|
||||
"default": {
|
||||
'ENGINE': 'django.db.backends.postgresql_psycopg2',
|
||||
"HOST": DATABASE_HOST,
|
||||
"PORT": DATABASE_PORT,
|
||||
"USER": DATABASE_USER,
|
||||
"PASSWORD": DATABASE_PASSWORD,
|
||||
"NAME": DATABASE_NAME,
|
||||
# 全局开启事务,绑定的是http请求响应整个过程
|
||||
'ATOMIC_REQUESTS': True,
|
||||
}
|
||||
}
|
||||
else:
|
||||
# sqlite3 数据库
|
||||
DATABASES = {
|
||||
'default': {
|
||||
'ENGINE': 'django.db.backends.sqlite3',
|
||||
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
|
||||
'OPTIONS': {
|
||||
'timeout': 20,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
# 缓存配置
|
||||
CACHES = {
|
||||
"default": {
|
||||
"BACKEND": "django_redis.cache.RedisCache",
|
||||
"LOCATION": f'{REDIS_URL}/1',
|
||||
"TIMEOUT": None,
|
||||
"OPTIONS": {
|
||||
"CLIENT_CLASS": "django_redis.client.DefaultClient",
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
# # ================================================= #
|
||||
# # ********************* 日志配置 ******************* #
|
||||
# # ================================================= #
|
||||
# # log 配置部分BEGIN #
|
||||
SERVER_LOGS_FILE = os.path.join(BASE_DIR, "logs", "server.log")
|
||||
ERROR_LOGS_FILE = os.path.join(BASE_DIR, "logs", "error.log")
|
||||
LOGS_FILE = os.path.join(BASE_DIR, "logs")
|
||||
if not os.path.exists(os.path.join(BASE_DIR, "logs")):
|
||||
os.makedirs(os.path.join(BASE_DIR, "logs"))
|
||||
|
||||
# 格式:[2020-04-22 23:33:01][micoservice.apps.ready():16] [INFO] 这是一条日志:
|
||||
# 格式:[日期][模块.函数名称():行号] [级别] 信息
|
||||
STANDARD_LOG_FORMAT = (
|
||||
"[%(asctime)s][%(name)s.%(funcName)s():%(lineno)d] [%(levelname)s] %(message)s"
|
||||
)
|
||||
CONSOLE_LOG_FORMAT = (
|
||||
"[%(asctime)s][%(name)s.%(funcName)s():%(lineno)d] [%(levelname)s] %(message)s"
|
||||
)
|
||||
LOGGING = {
|
||||
"version": 1,
|
||||
"disable_existing_loggers": False,
|
||||
"formatters": {
|
||||
"standard": {"format": STANDARD_LOG_FORMAT},
|
||||
"console": {
|
||||
"format": CONSOLE_LOG_FORMAT,
|
||||
"datefmt": "%Y-%m-%d %H:%M:%S",
|
||||
},
|
||||
"file": {
|
||||
"format": CONSOLE_LOG_FORMAT,
|
||||
"datefmt": "%Y-%m-%d %H:%M:%S",
|
||||
},
|
||||
},
|
||||
"handlers": {
|
||||
"file": {
|
||||
"level": "INFO",
|
||||
"class": "logging.handlers.RotatingFileHandler",
|
||||
"filename": SERVER_LOGS_FILE,
|
||||
"maxBytes": 1024 * 1024 * 100, # 100 MB
|
||||
"backupCount": 5, # 最多备份5个
|
||||
"formatter": "standard",
|
||||
"encoding": "utf-8",
|
||||
},
|
||||
"error": {
|
||||
"level": "ERROR",
|
||||
"class": "logging.handlers.RotatingFileHandler",
|
||||
"filename": ERROR_LOGS_FILE,
|
||||
"maxBytes": 1024 * 1024 * 100, # 100 MB
|
||||
"backupCount": 3, # 最多备份3个
|
||||
"formatter": "standard",
|
||||
"encoding": "utf-8",
|
||||
},
|
||||
"console": {
|
||||
"level": "INFO",
|
||||
"class": "logging.StreamHandler",
|
||||
"formatter": "console",
|
||||
},
|
||||
|
||||
},
|
||||
"loggers": {
|
||||
"": {
|
||||
"handlers": ["console", "error", "file"],
|
||||
"level": "INFO",
|
||||
},
|
||||
"django": {
|
||||
"handlers": ["console", "error", "file"],
|
||||
"level": "INFO",
|
||||
"propagate": False,
|
||||
},
|
||||
'django.db.backends': {
|
||||
'handlers': ["console", "error", "file"],
|
||||
'propagate': False,
|
||||
'level': "INFO"
|
||||
},
|
||||
"uvicorn.error": {
|
||||
"level": "INFO",
|
||||
"handlers": ["console", "error", "file"],
|
||||
},
|
||||
"uvicorn.access": {
|
||||
"handlers": ["console", "error", "file"],
|
||||
"level": "INFO"
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
# celery 配置
|
||||
CELERY_BROKER_URL = f'{REDIS_URL}/2'
|
||||
DJANGO_CELERY_BEAT_TZ_AWARE = False
|
||||
CELERY_ENABLE_UTC = False
|
||||
CELERY_WORKER_CONCURRENCY = 2 # 并发数
|
||||
CELERY_MAX_TASKS_PER_CHILD = 5 # 每个worker最多执行5个任务便自我销毁释放内存
|
||||
CELERY_TIMEZONE = TIME_ZONE # celery 时区问题
|
||||
CELERY_RESULT_BACKEND = 'django-db' # celery结果存储到数据库中
|
||||
CELERY_BEAT_SCHEDULER = 'django_celery_beat.schedulers:DatabaseScheduler' # Backend数据库
|
||||
|
||||
# token 有效时间 时 分 秒
|
||||
TOKEN_LIFETIME = 12 * 60 * 60
|
||||
# TOKEN_LIFETIME = 50
|
||||
|
||||
# 接口白名单,不需要授权直接访问
|
||||
WHITE_LIST = ['/api/system/userinfo', '/api/system/permCode', '/api/system/menu/route/tree', '/api/system/user/*',
|
||||
'/api/system/user/set/repassword']
|
||||
|
||||
# 接口日志记录
|
||||
API_LOG_ENABLE = True
|
||||
API_LOG_METHODS = ['POST', 'GET', 'DELETE', 'PUT']
|
||||
API_MODEL_MAP = {}
|
||||
|
||||
# 初始化需要执行的列表,用来初始化后执行
|
||||
INITIALIZE_RESET_LIST = []
|
||||
|
||||
|
||||
|
||||
# vdes监听器配置
|
||||
VDES_MONITOR_HOST = 'localhost'
|
||||
VDES_MONITOR_PORT = 8082
|
||||
VDES_BUFFER_SIZE = 65536 # 接收缓冲区大小
|
||||
|
||||
# ais船只信息配置
|
||||
OWN_SHIP_MMSI = '413323670'
|
||||
OWN_SHIP_NAME = '湃星一号'
|
||||
OWN_SHIP_TYPE = '货船'
|
||||
RADIUS_NM = 50.0 #显示周边多少海里范围的船只
|
||||
23
code/bk/urls.txt
Normal file
23
code/bk/urls.txt
Normal file
@@ -0,0 +1,23 @@
|
||||
"""fuadmin URL Configuration
|
||||
|
||||
The `urlpatterns` list routes URLs to views. For more information please see:
|
||||
https://docs.djangoproject.com/en/4.0/topics/http/urls/
|
||||
Examples:
|
||||
Function views
|
||||
1. Add an import: from my_app import views
|
||||
2. Add a URL to urlpatterns: path('', views.home, name='home')
|
||||
Class-based views
|
||||
1. Add an import: from other_app.views import Home
|
||||
2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')
|
||||
Including another URLconf
|
||||
1. Import the include() function: from django.urls import include, path
|
||||
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
|
||||
"""
|
||||
from django.contrib import admin
|
||||
from django.urls import path
|
||||
from .api import api
|
||||
|
||||
urlpatterns = [
|
||||
path('admin/', admin.site.urls),
|
||||
path('api/', api.urls),
|
||||
]
|
||||
34
code/conf/docker.py
Normal file
34
code/conf/docker.py
Normal file
@@ -0,0 +1,34 @@
|
||||
import os
|
||||
|
||||
# ================================================= #
|
||||
# ************** mysql数据库 配置 ************** #
|
||||
# ================================================= #
|
||||
# 数据库类型 MYSQL/SQLSERVER/SQLITE3
|
||||
DATABASE_TYPE = "MYSQL"
|
||||
# 数据库地址
|
||||
DATABASE_HOST = "fuadmin-mysql"
|
||||
# 数据库端口
|
||||
DATABASE_PORT = 3306
|
||||
# 数据库用户名
|
||||
DATABASE_USER = "root"
|
||||
# 数据库密码
|
||||
DATABASE_PASSWORD = "root"
|
||||
# 数据库名
|
||||
DATABASE_NAME = "fuadmin"
|
||||
|
||||
# ================================================= #
|
||||
# ************** redis配置,无redis 可不进行配置 ************** #
|
||||
# ================================================= #
|
||||
REDIS_PASSWORD = ""
|
||||
REDIS_HOST = "fuadmin-redis"
|
||||
REDIS_URL = f'redis://:{REDIS_PASSWORD or ""}@{REDIS_HOST}:6379'
|
||||
# ================================================= #
|
||||
# ************** 其他 配置 ************** #
|
||||
# ================================================= #
|
||||
DEBUG = False # 线上环境请设置为False
|
||||
ALLOWED_HOSTS = ["*"]
|
||||
LOGIN_NO_CAPTCHA_AUTH = True # 登录接口 /api/token/ 是否需要验证码认证,用于测试,正式环境建议取消
|
||||
ENABLE_LOGIN_ANALYSIS_LOG = True # 启动登录详细概略获取(通过调用api获取ip详细地址)
|
||||
|
||||
# 是否开启演示环境,开启后增 删 改功能失效
|
||||
DEMO = False
|
||||
32
code/conf/env.py
Normal file
32
code/conf/env.py
Normal file
@@ -0,0 +1,32 @@
|
||||
# ================================================= #
|
||||
# ************** mysql数据库 配置 ************** #
|
||||
# ================================================= #
|
||||
# 数据库类型 MYSQL/SQLSERVER/SQLITE3/POSTGRESQL
|
||||
DATABASE_TYPE = "MYSQL"
|
||||
# 数据库地址
|
||||
DATABASE_HOST = "127.0.0.1"
|
||||
# 数据库端口
|
||||
DATABASE_PORT = 3306
|
||||
# 数据库用户名
|
||||
DATABASE_USER = "root"
|
||||
# 数据库密码
|
||||
DATABASE_PASSWORD = "010704"
|
||||
# 数据库名
|
||||
DATABASE_NAME = "vdes_data"
|
||||
|
||||
# ================================================= #
|
||||
# ************** redis配置,无redis 可不进行配置 ************** #
|
||||
# ================================================= #
|
||||
REDIS_PASSWORD = ''
|
||||
REDIS_HOST = '127.0.0.1'
|
||||
REDIS_URL = f'redis://:{REDIS_PASSWORD or ""}@{REDIS_HOST}:6379'
|
||||
# ================================================= #
|
||||
# ************** 其他 配置 ************** #
|
||||
# ================================================= #
|
||||
DEBUG = True # 线上环境请设置为False
|
||||
ALLOWED_HOSTS = ["*"]
|
||||
LOGIN_NO_CAPTCHA_AUTH = True # 登录接口 /api/token/ 是否需要验证码认证,用于测试,正式环境建议取消
|
||||
ENABLE_LOGIN_ANALYSIS_LOG = True # 启动登录详细概略获取(通过调用api获取ip详细地址)
|
||||
|
||||
# 是否开启演示环境,开启后增 删 改功能失效
|
||||
DEMO = False
|
||||
0
code/demo/__init__.py
Normal file
0
code/demo/__init__.py
Normal file
3
code/demo/admin.py
Normal file
3
code/demo/admin.py
Normal file
@@ -0,0 +1,3 @@
|
||||
from django.contrib import admin
|
||||
|
||||
# Register your models here.
|
||||
94
code/demo/api.py
Normal file
94
code/demo/api.py
Normal file
@@ -0,0 +1,94 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
# time: 2022/7/21 14:01
|
||||
# file: api.py
|
||||
# author: 臧成龙
|
||||
# QQ: 939589097
|
||||
|
||||
from typing import List
|
||||
|
||||
from demo.models import Demo
|
||||
from ninja import Field, ModelSchema, Query, Router
|
||||
from ninja.pagination import paginate
|
||||
from utils.fu_crud import (
|
||||
ImportSchema,
|
||||
create,
|
||||
delete,
|
||||
export_data,
|
||||
import_data,
|
||||
retrieve,
|
||||
update,
|
||||
)
|
||||
from utils.fu_ninja import FuFilters, MyPagination
|
||||
|
||||
router = Router()
|
||||
|
||||
|
||||
# 设置过滤字段
|
||||
class Filters(FuFilters):
|
||||
name: str = Field(None, alias="name")
|
||||
code: str = Field(None, alias="code")
|
||||
status: int = Field(None, alias="status")
|
||||
id: str = Field(None, alias="demo_id")
|
||||
|
||||
|
||||
# 设置请求接收字段
|
||||
class DemoSchemaIn(ModelSchema):
|
||||
remark: list
|
||||
|
||||
class Config:
|
||||
model = Demo
|
||||
model_fields = ['name', 'code', 'sort', 'status']
|
||||
|
||||
|
||||
# 设置响应字段
|
||||
class DemoSchemaOut(ModelSchema):
|
||||
|
||||
class Config:
|
||||
model = Demo
|
||||
model_fields = ['id', 'name', 'code', 'sort', 'status', 'remark', 'create_datetime']
|
||||
|
||||
|
||||
# 创建Demo
|
||||
@router.post("/demo", response=DemoSchemaOut)
|
||||
def create_demo(request, data: DemoSchemaIn):
|
||||
data.remark = ','.join(data.remark)
|
||||
demo = create(request, data, Demo)
|
||||
return demo
|
||||
|
||||
|
||||
# 删除Demo
|
||||
@router.delete("/demo/{demo_id}")
|
||||
def delete_demo(request, demo_id: int):
|
||||
delete(demo_id, Demo)
|
||||
return {"success": True}
|
||||
|
||||
|
||||
# 更新Demo
|
||||
@router.put("/demo/{demo_id}", response=DemoSchemaOut)
|
||||
def update_demo(request, demo_id: int, data: DemoSchemaIn):
|
||||
data.remark = ','.join(data.remark)
|
||||
demo = update(request, demo_id, data, Demo)
|
||||
return demo
|
||||
|
||||
|
||||
# 获取Demo
|
||||
@router.get("/demo", response=List[DemoSchemaOut])
|
||||
@paginate(MyPagination)
|
||||
def list_demo(request, filters: Filters = Query(...)):
|
||||
qs = retrieve(request, Demo, filters)
|
||||
return qs
|
||||
|
||||
|
||||
# 导入
|
||||
@router.get("/demo/all/export")
|
||||
def export_demo(request):
|
||||
export_fields = ['name', 'code', 'status']
|
||||
return export_data(request, Demo, DemoSchemaOut, export_fields)
|
||||
|
||||
|
||||
# 导出
|
||||
@router.post("/demo/all/import")
|
||||
def import_demo(request, data: ImportSchema):
|
||||
import_fields = ['name', 'code', 'status']
|
||||
return import_data(request, Demo, DemoSchemaIn, data, import_fields)
|
||||
6
code/demo/apps.py
Normal file
6
code/demo/apps.py
Normal file
@@ -0,0 +1,6 @@
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class DemoConfig(AppConfig):
|
||||
default_auto_field = 'django.db.models.BigAutoField'
|
||||
name = 'demo'
|
||||
14
code/demo/models.py
Normal file
14
code/demo/models.py
Normal file
@@ -0,0 +1,14 @@
|
||||
from django.db import models
|
||||
from utils.models import CoreModel
|
||||
|
||||
|
||||
class Demo(CoreModel):
|
||||
name = models.CharField(null=False, max_length=64, verbose_name="项目名称", help_text="项目名称")
|
||||
code = models.CharField(max_length=32, verbose_name="项目编码", help_text="项目编码")
|
||||
status = models.CharField(max_length=64, verbose_name="项目状态", help_text="项目状态")
|
||||
|
||||
class Meta:
|
||||
db_table = "Demo"
|
||||
verbose_name = '项目演示'
|
||||
verbose_name_plural = verbose_name
|
||||
ordering = ('-create_datetime',)
|
||||
11
code/demo/router.py
Normal file
11
code/demo/router.py
Normal file
@@ -0,0 +1,11 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
# time: 2022/7/21 14:10
|
||||
# file: router.py
|
||||
# author: 臧成龙
|
||||
# QQ: 939589097
|
||||
from ninja import Router
|
||||
from demo.api import router
|
||||
|
||||
demo_router = Router()
|
||||
demo_router.add_router('/', router, tags=["Demo"])
|
||||
3
code/demo/tests.py
Normal file
3
code/demo/tests.py
Normal file
@@ -0,0 +1,3 @@
|
||||
from django.test import TestCase
|
||||
|
||||
# Create your tests here.
|
||||
3
code/demo/views.py
Normal file
3
code/demo/views.py
Normal file
@@ -0,0 +1,3 @@
|
||||
from django.shortcuts import render
|
||||
|
||||
# Create your views here.
|
||||
0
code/fuadmin/__init__.py
Normal file
0
code/fuadmin/__init__.py
Normal file
29
code/fuadmin/api.py
Normal file
29
code/fuadmin/api.py
Normal file
@@ -0,0 +1,29 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# @Time : 2022/5/9 23:15
|
||||
# @Author : 臧成龙
|
||||
# @FileName: api.py
|
||||
# @Software: PyCharm
|
||||
from demo.router import demo_router
|
||||
from system.router import system_router
|
||||
from utils.fu_auth import GlobalAuth
|
||||
from utils.fu_ninja import FuNinjaAPI
|
||||
from generator.router import generator_router
|
||||
|
||||
|
||||
# api = FuNinjaAPI(auth=GlobalAuth())
|
||||
api = FuNinjaAPI() # 移除auth参数
|
||||
|
||||
|
||||
# 统一处理server异常
|
||||
@api.exception_handler(Exception)
|
||||
def a(request, exc):
|
||||
|
||||
if hasattr(exc, 'errno'):
|
||||
return api.create_response(request, data=[], msg=str(exc), code=exc.errno)
|
||||
else:
|
||||
return api.create_response(request, data=[], msg=str(exc), code=500)
|
||||
|
||||
|
||||
api.add_router('/system/', system_router)
|
||||
api.add_router('/demo/', demo_router)
|
||||
api.add_router('/generator/', generator_router)
|
||||
16
code/fuadmin/asgi.py
Normal file
16
code/fuadmin/asgi.py
Normal file
@@ -0,0 +1,16 @@
|
||||
"""
|
||||
ASGI config for fuadmin project.
|
||||
|
||||
It exposes the ASGI callable as a module-level variable named ``application``.
|
||||
|
||||
For more information on this file, see
|
||||
https://docs.djangoproject.com/en/4.0/howto/deployment/asgi/
|
||||
"""
|
||||
|
||||
import os
|
||||
|
||||
from django.core.asgi import get_asgi_application
|
||||
|
||||
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'fuadmin.settings')
|
||||
|
||||
application = get_asgi_application()
|
||||
14
code/fuadmin/celery.py
Normal file
14
code/fuadmin/celery.py
Normal file
@@ -0,0 +1,14 @@
|
||||
import os
|
||||
|
||||
from celery import Celery, platforms
|
||||
from django.conf import settings
|
||||
|
||||
os.environ.setdefault('DJANGO_SETTINGS_MODULE', "fuadmin.settings")
|
||||
|
||||
# app = Celery(f"application")
|
||||
app = Celery(f"system")
|
||||
|
||||
app.config_from_object('django.conf:settings', namespace='CELERY')
|
||||
app.autodiscover_tasks(lambda: settings.INSTALLED_APPS)
|
||||
# app.autodiscover_tasks()
|
||||
platforms.C_FORCE_ROOT = True
|
||||
326
code/fuadmin/settings.py
Normal file
326
code/fuadmin/settings.py
Normal file
@@ -0,0 +1,326 @@
|
||||
"""
|
||||
Django settings for fuadmin project.
|
||||
|
||||
Generated by 'django-admin startproject' using Django 4.0.4.
|
||||
|
||||
For more information on this file, see
|
||||
https://docs.djangoproject.com/en/4.0/topics/settings/
|
||||
|
||||
For the full list of settings and their values, see
|
||||
https://docs.djangoproject.com/en/4.0/ref/settings/
|
||||
"""
|
||||
import os
|
||||
from datetime import timedelta
|
||||
from pathlib import Path
|
||||
from conf.env import *
|
||||
|
||||
# Build paths inside the project like this: BASE_DIR / 'subdir'.
|
||||
BASE_DIR = Path(__file__).resolve().parent.parent
|
||||
|
||||
# Quick-start development settings - unsuitable for production
|
||||
# See https://docs.djangoproject.com/en/4.0/howto/deployment/checklist/
|
||||
|
||||
# SECURITY WARNING: keep the secret key used in production secret!
|
||||
SECRET_KEY = 'django-insecure-x4k4^#6wovi1aep8%ow!5fr%(9o#1u=+0+nzi($_j=^d*ui6g3'
|
||||
|
||||
# SECURITY WARNING: don't run with debug turned on in production!
|
||||
DEBUG = locals().get('DEBUG', True)
|
||||
ALLOWED_HOSTS = locals().get('ALLOWED_HOSTS', ['*'])
|
||||
DEMO = locals().get('DEMO', False)
|
||||
|
||||
# Application definition
|
||||
|
||||
INSTALLED_APPS = [
|
||||
'django.contrib.admin',
|
||||
'django.contrib.auth',
|
||||
'django.contrib.contenttypes',
|
||||
'django.contrib.sessions',
|
||||
'django.contrib.messages',
|
||||
'django.contrib.staticfiles',
|
||||
'django_celery_beat',
|
||||
'django_celery_results',
|
||||
'system',
|
||||
'demo',
|
||||
'generator',
|
||||
]
|
||||
|
||||
MIDDLEWARE = [
|
||||
'django.middleware.security.SecurityMiddleware',
|
||||
'django.contrib.sessions.middleware.SessionMiddleware',
|
||||
'django.middleware.common.CommonMiddleware',
|
||||
'django.middleware.csrf.CsrfViewMiddleware',
|
||||
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
||||
'django.contrib.messages.middleware.MessageMiddleware',
|
||||
'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
||||
'utils.middleware.ApiLoggingMiddleware',
|
||||
|
||||
]
|
||||
|
||||
ROOT_URLCONF = 'fuadmin.urls'
|
||||
|
||||
TEMPLATES = [
|
||||
{
|
||||
'BACKEND': 'django.template.backends.django.DjangoTemplates',
|
||||
'DIRS': [BASE_DIR / 'templates']
|
||||
,
|
||||
'APP_DIRS': True,
|
||||
'OPTIONS': {
|
||||
'context_processors': [
|
||||
'django.template.context_processors.debug',
|
||||
'django.template.context_processors.request',
|
||||
'django.contrib.auth.context_processors.auth',
|
||||
'django.contrib.messages.context_processors.messages',
|
||||
],
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
WSGI_APPLICATION = 'fuadmin.wsgi.application'
|
||||
|
||||
# Password validation
|
||||
# https://docs.djangoproject.com/en/4.0/ref/settings/#auth-password-validators
|
||||
|
||||
AUTH_PASSWORD_VALIDATORS = [
|
||||
{
|
||||
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
|
||||
},
|
||||
{
|
||||
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
|
||||
},
|
||||
{
|
||||
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
|
||||
},
|
||||
{
|
||||
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
|
||||
},
|
||||
]
|
||||
|
||||
# Internationalization
|
||||
# https://docs.djangoproject.com/en/4.0/topics/i18n/
|
||||
|
||||
LANGUAGE_CODE = 'zh-hans'
|
||||
|
||||
TIME_ZONE = 'Asia/Shanghai'
|
||||
|
||||
USE_I18N = True
|
||||
|
||||
USE_L10N = True
|
||||
|
||||
USE_TZ = False
|
||||
|
||||
# Static files (CSS, JavaScript, Images)
|
||||
# https://docs.djangoproject.com/en/4.0/howto/static-files/
|
||||
|
||||
STATIC_URL = '/static/'
|
||||
STATICFILES_DIRS = [
|
||||
os.path.join(BASE_DIR, 'static'),
|
||||
]
|
||||
# Default primary key field type
|
||||
# https://docs.djangoproject.com/en/4.0/ref/settings/#default-auto-field
|
||||
|
||||
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
|
||||
|
||||
AUTH_USER_MODEL = 'system.Users'
|
||||
USERNAME_FIELD = 'username'
|
||||
ALL_MODELS_OBJECTS = [] # 所有app models 对象
|
||||
|
||||
# Database
|
||||
# https://docs.djangoproject.com/en/4.0/ref/settings/#databases
|
||||
# 数据库配置
|
||||
if DATABASE_TYPE == "MYSQL":
|
||||
# Mysql数据库
|
||||
DATABASES = {
|
||||
"default": {
|
||||
"ENGINE": "django.db.backends.mysql",
|
||||
"HOST": DATABASE_HOST,
|
||||
"PORT": DATABASE_PORT,
|
||||
"USER": DATABASE_USER,
|
||||
"PASSWORD": DATABASE_PASSWORD,
|
||||
"NAME": DATABASE_NAME,
|
||||
}
|
||||
}
|
||||
elif DATABASE_TYPE == "SQLSERVER":
|
||||
# SqlServer数据库
|
||||
DATABASES = {
|
||||
"default": {
|
||||
"ENGINE": "mssql",
|
||||
"HOST": DATABASE_HOST,
|
||||
"PORT": DATABASE_PORT,
|
||||
"USER": DATABASE_USER,
|
||||
"PASSWORD": DATABASE_PASSWORD,
|
||||
"NAME": DATABASE_NAME,
|
||||
# 全局开启事务,绑定的是http请求响应整个过程
|
||||
'ATOMIC_REQUESTS': True,
|
||||
'OPTIONS': {
|
||||
'driver': 'ODBC Driver 17 for SQL Server',
|
||||
},
|
||||
}
|
||||
}
|
||||
elif DATABASE_TYPE == "POSTGRESQL":
|
||||
# POSTGRESQL
|
||||
DATABASES = {
|
||||
"default": {
|
||||
'ENGINE': 'django.db.backends.postgresql_psycopg2',
|
||||
"HOST": DATABASE_HOST,
|
||||
"PORT": DATABASE_PORT,
|
||||
"USER": DATABASE_USER,
|
||||
"PASSWORD": DATABASE_PASSWORD,
|
||||
"NAME": DATABASE_NAME,
|
||||
# 全局开启事务,绑定的是http请求响应整个过程
|
||||
'ATOMIC_REQUESTS': True,
|
||||
}
|
||||
}
|
||||
else:
|
||||
# sqlite3 数据库
|
||||
DATABASES = {
|
||||
'default': {
|
||||
'ENGINE': 'django.db.backends.sqlite3',
|
||||
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
|
||||
'OPTIONS': {
|
||||
'timeout': 20,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
# 缓存配置
|
||||
CACHES = {
|
||||
"default": {
|
||||
"BACKEND": "django_redis.cache.RedisCache",
|
||||
"LOCATION": f'{REDIS_URL}/1',
|
||||
"TIMEOUT": None,
|
||||
"OPTIONS": {
|
||||
"CLIENT_CLASS": "django_redis.client.DefaultClient",
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
# # ================================================= #
|
||||
# # ********************* 日志配置 ******************* #
|
||||
# # ================================================= #
|
||||
# # log 配置部分BEGIN #
|
||||
SERVER_LOGS_FILE = os.path.join(BASE_DIR, "logs", "server.log")
|
||||
ERROR_LOGS_FILE = os.path.join(BASE_DIR, "logs", "error.log")
|
||||
LOGS_FILE = os.path.join(BASE_DIR, "logs")
|
||||
if not os.path.exists(os.path.join(BASE_DIR, "logs")):
|
||||
os.makedirs(os.path.join(BASE_DIR, "logs"))
|
||||
|
||||
# 格式:[2020-04-22 23:33:01][micoservice.apps.ready():16] [INFO] 这是一条日志:
|
||||
# 格式:[日期][模块.函数名称():行号] [级别] 信息
|
||||
STANDARD_LOG_FORMAT = (
|
||||
"[%(asctime)s][%(name)s.%(funcName)s():%(lineno)d] [%(levelname)s] %(message)s"
|
||||
)
|
||||
CONSOLE_LOG_FORMAT = (
|
||||
"[%(asctime)s][%(name)s.%(funcName)s():%(lineno)d] [%(levelname)s] %(message)s"
|
||||
)
|
||||
LOGGING = {
|
||||
"version": 1,
|
||||
"disable_existing_loggers": False,
|
||||
"formatters": {
|
||||
"standard": {"format": STANDARD_LOG_FORMAT},
|
||||
"console": {
|
||||
"format": CONSOLE_LOG_FORMAT,
|
||||
"datefmt": "%Y-%m-%d %H:%M:%S",
|
||||
},
|
||||
"file": {
|
||||
"format": CONSOLE_LOG_FORMAT,
|
||||
"datefmt": "%Y-%m-%d %H:%M:%S",
|
||||
},
|
||||
},
|
||||
"handlers": {
|
||||
"file": {
|
||||
"level": "INFO",
|
||||
"class": "logging.handlers.RotatingFileHandler",
|
||||
"filename": SERVER_LOGS_FILE,
|
||||
"maxBytes": 1024 * 1024 * 100, # 100 MB
|
||||
"backupCount": 5, # 最多备份5个
|
||||
"formatter": "standard",
|
||||
"encoding": "utf-8",
|
||||
},
|
||||
"error": {
|
||||
"level": "ERROR",
|
||||
"class": "logging.handlers.RotatingFileHandler",
|
||||
"filename": ERROR_LOGS_FILE,
|
||||
"maxBytes": 1024 * 1024 * 100, # 100 MB
|
||||
"backupCount": 3, # 最多备份3个
|
||||
"formatter": "standard",
|
||||
"encoding": "utf-8",
|
||||
},
|
||||
"console": {
|
||||
"level": "INFO",
|
||||
"class": "logging.StreamHandler",
|
||||
"formatter": "console",
|
||||
},
|
||||
|
||||
},
|
||||
"loggers": {
|
||||
"": {
|
||||
"handlers": ["console", "error", "file"],
|
||||
"level": "INFO",
|
||||
},
|
||||
"django": {
|
||||
"handlers": ["console", "error", "file"],
|
||||
"level": "INFO",
|
||||
"propagate": False,
|
||||
},
|
||||
'django.db.backends': {
|
||||
'handlers': ["console", "error", "file"],
|
||||
'propagate': False,
|
||||
'level': "INFO"
|
||||
},
|
||||
"uvicorn.error": {
|
||||
"level": "INFO",
|
||||
"handlers": ["console", "error", "file"],
|
||||
},
|
||||
"uvicorn.access": {
|
||||
"handlers": ["console", "error", "file"],
|
||||
"level": "INFO"
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
# celery 配置
|
||||
CELERY_BROKER_URL = f'{REDIS_URL}/2'
|
||||
DJANGO_CELERY_BEAT_TZ_AWARE = False
|
||||
CELERY_ENABLE_UTC = False
|
||||
CELERY_WORKER_CONCURRENCY = 2 # 并发数
|
||||
CELERY_MAX_TASKS_PER_CHILD = 5 # 每个worker最多执行5个任务便自我销毁释放内存
|
||||
CELERY_TIMEZONE = TIME_ZONE # celery 时区问题
|
||||
CELERY_RESULT_BACKEND = 'django-db' # celery结果存储到数据库中
|
||||
CELERY_BEAT_SCHEDULER = 'django_celery_beat.schedulers:DatabaseScheduler' # Backend数据库
|
||||
|
||||
# token 有效时间 时 分 秒
|
||||
TOKEN_LIFETIME = 12 * 60 * 60
|
||||
# TOKEN_LIFETIME = 50
|
||||
|
||||
# 接口白名单,不需要授权直接访问
|
||||
WHITE_LIST = ['/api/system/userinfo', '/api/system/permCode', '/api/system/menu/route/tree', '/api/system/user/*',
|
||||
'/api/system/user/set/repassword']
|
||||
|
||||
# 接口日志记录
|
||||
API_LOG_ENABLE = True
|
||||
API_LOG_METHODS = ['POST', 'GET', 'DELETE', 'PUT']
|
||||
API_MODEL_MAP = {}
|
||||
|
||||
# 初始化需要执行的列表,用来初始化后执行
|
||||
INITIALIZE_RESET_LIST = []
|
||||
|
||||
|
||||
|
||||
# vdes监听器配置
|
||||
# VDES_MONITOR_HOST = '192.168.2.183'
|
||||
# VDES_MONITOR_PORT = 10
|
||||
# VDES_MONITOR_HOST = '172.16.1.59'
|
||||
# VDES_MONITOR_PORT = 8081
|
||||
VDES_MONITOR_HOST = 'localhost'
|
||||
VDES_MONITOR_PORT = 10
|
||||
|
||||
VDES_BUFFER_SIZE = 65536 # 接收缓冲区大小
|
||||
|
||||
# ais船只信息配置
|
||||
OWN_SHIP_MMSI = '413323670'
|
||||
OWN_SHIP_NAME = '湃星一号'
|
||||
OWN_SHIP_TYPE = '货船'
|
||||
RADIUS_NM = 150.0 #显示周边多少海里范围的船只
|
||||
|
||||
29
code/fuadmin/urls.py
Normal file
29
code/fuadmin/urls.py
Normal file
@@ -0,0 +1,29 @@
|
||||
"""fuadmin URL Configuration
|
||||
|
||||
The `urlpatterns` list routes URLs to views. For more information please see:
|
||||
https://docs.djangoproject.com/en/4.0/topics/http/urls/
|
||||
Examples:
|
||||
Function views
|
||||
1. Add an import: from my_app import views
|
||||
2. Add a URL to urlpatterns: path('', views.home, name='home')
|
||||
Class-based views
|
||||
1. Add an import: from other_app.views import Home
|
||||
2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')
|
||||
Including another URLconf
|
||||
1. Import the include() function: from django.urls import include, path
|
||||
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
|
||||
"""
|
||||
from django.contrib import admin
|
||||
from django.urls import path
|
||||
from django.conf import settings
|
||||
from django.conf.urls.static import static
|
||||
from .api import api
|
||||
|
||||
urlpatterns = [
|
||||
path('admin/', admin.site.urls),
|
||||
path('api/', api.urls),
|
||||
]
|
||||
|
||||
# 在开发环境中添加静态文件 URL 配置
|
||||
if settings.DEBUG:
|
||||
urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
|
||||
16
code/fuadmin/wsgi.py
Normal file
16
code/fuadmin/wsgi.py
Normal file
@@ -0,0 +1,16 @@
|
||||
"""
|
||||
WSGI config for fuadmin project.
|
||||
|
||||
It exposes the WSGI callable as a module-level variable named ``application``.
|
||||
|
||||
For more information on this file, see
|
||||
https://docs.djangoproject.com/en/4.0/howto/deployment/wsgi/
|
||||
"""
|
||||
|
||||
import os
|
||||
|
||||
from django.core.wsgi import get_wsgi_application
|
||||
|
||||
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'fuadmin.settings')
|
||||
|
||||
application = get_wsgi_application()
|
||||
0
code/generator/__init__.py
Normal file
0
code/generator/__init__.py
Normal file
6
code/generator/apps.py
Normal file
6
code/generator/apps.py
Normal file
@@ -0,0 +1,6 @@
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class GeneratorConfig(AppConfig):
|
||||
default_auto_field = 'django.db.models.BigAutoField'
|
||||
name = 'generator'
|
||||
19
code/generator/router.py
Normal file
19
code/generator/router.py
Normal file
@@ -0,0 +1,19 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
# time: 2022/7/21 14:10
|
||||
# file: router.py
|
||||
# author: 臧成龙
|
||||
# QQ: 939589097
|
||||
from ninja import Router
|
||||
from .test_demo.api import router as test_demo_router
|
||||
# from .template_test.api import router as template_test_router
|
||||
from .test.api import router as test_router
|
||||
|
||||
generator_router = Router()
|
||||
generator_router.add_router('/', test_demo_router, tags=['测试案例'])
|
||||
# generator_router.add_router('/', template_test_router, tags=['测试1'])
|
||||
generator_router.add_router('/', test_router, tags=['模板测试'])
|
||||
|
||||
|
||||
|
||||
|
||||
83
code/generator/template_test/api.py
Normal file
83
code/generator/template_test/api.py
Normal file
@@ -0,0 +1,83 @@
|
||||
from typing import List
|
||||
from .model import TemplateTest
|
||||
from ninja import Field, ModelSchema, Query, Router
|
||||
from ninja.pagination import paginate
|
||||
from utils.fu_crud import (
|
||||
ImportSchema,
|
||||
create,
|
||||
delete,
|
||||
export_data,
|
||||
import_data,
|
||||
retrieve,
|
||||
update,
|
||||
)
|
||||
from utils.fu_ninja import FuFilters, MyPagination
|
||||
|
||||
router = Router()
|
||||
|
||||
|
||||
# 设置过滤字段
|
||||
class Filters(FuFilters):
|
||||
|
||||
input_text_area_2: str = Field(None, alias='input_text_area_2')
|
||||
input_1: str = Field(None, alias='input_1')
|
||||
|
||||
|
||||
# 设置请求接收字段
|
||||
class TemplateTestSchemaIn(ModelSchema):
|
||||
|
||||
class Config:
|
||||
model = TemplateTest
|
||||
model_exclude = ['create_datetime', 'update_datetime', 'belong_dept', 'modifier', 'creator', 'creator', 'id']
|
||||
|
||||
|
||||
# 设置响应字段
|
||||
class TemplateTestSchemaOut(ModelSchema):
|
||||
|
||||
class Config:
|
||||
model = TemplateTest
|
||||
model_fields = '__all__'
|
||||
|
||||
|
||||
# 创建TemplateTest
|
||||
@router.post('/template_test', response=TemplateTestSchemaOut)
|
||||
def create_template_test(request, data: TemplateTestSchemaIn):
|
||||
template_test = create(request, data, TemplateTest)
|
||||
return template_test
|
||||
|
||||
|
||||
# 删除TemplateTest
|
||||
@router.delete('/template_test/{id}')
|
||||
def delete_template_test(request, id: int):
|
||||
delete(id, TemplateTest)
|
||||
return {'success': True}
|
||||
|
||||
|
||||
# 更新TemplateTest
|
||||
@router.put('/template_test/{id}', response=TemplateTestSchemaOut)
|
||||
def update_template_test(request, id: int, data: TemplateTestSchemaIn):
|
||||
template_test = update(request, id, data, TemplateTest)
|
||||
return template_test
|
||||
|
||||
|
||||
# 获取TemplateTest
|
||||
@router.get('/template_test', response=List[TemplateTestSchemaOut])
|
||||
@paginate(MyPagination)
|
||||
def list_template_test(request, filters: Filters = Query(...)):
|
||||
qs = retrieve(request, TemplateTest, filters)
|
||||
return qs
|
||||
|
||||
|
||||
# 导入
|
||||
@router.get('/template_test/all/export')
|
||||
def export_template_test(request):
|
||||
export_fields = ('input_text_area_2', 'input_1', )
|
||||
return export_data(request, TemplateTest, TemplateTestSchemaOut, export_fields)
|
||||
|
||||
|
||||
# 导出
|
||||
@router.post('/template_test/all/import')
|
||||
def import_template_test(request, data: ImportSchema):
|
||||
import_fields = ('input_text_area_2', 'input_1', )
|
||||
return import_data(request, TemplateTest, TemplateTestSchemaIn, data, import_fields)
|
||||
|
||||
15
code/generator/template_test/model.py
Normal file
15
code/generator/template_test/model.py
Normal file
@@ -0,0 +1,15 @@
|
||||
from django.db import models
|
||||
from utils.models import CoreModel
|
||||
|
||||
|
||||
class TemplateTest(CoreModel):
|
||||
|
||||
input_text_area_2 = models.TextField(null=True, blank=True, verbose_name='文本域', help_text='文本域')
|
||||
input_1 = models.CharField(null=True, blank=True, max_length=255, verbose_name='输入框', help_text='输入框')
|
||||
|
||||
class Meta:
|
||||
db_table = 'generator_template_test'
|
||||
verbose_name = '测试1'
|
||||
verbose_name_plural = verbose_name
|
||||
ordering = ('-create_datetime',)
|
||||
|
||||
6
code/generator/template_test/router.py
Normal file
6
code/generator/template_test/router.py
Normal file
@@ -0,0 +1,6 @@
|
||||
from ninja import Router
|
||||
from .api import router
|
||||
|
||||
template_test_router = Router()
|
||||
template_test_router.add_router('/', router, tags=['测试1'])
|
||||
|
||||
85
code/generator/test/api.py
Normal file
85
code/generator/test/api.py
Normal file
@@ -0,0 +1,85 @@
|
||||
from typing import List
|
||||
from .model import Test
|
||||
from ninja import Field, ModelSchema, Query, Router
|
||||
from ninja.pagination import paginate
|
||||
from utils.fu_crud import (
|
||||
ImportSchema,
|
||||
create,
|
||||
delete,
|
||||
export_data,
|
||||
import_data,
|
||||
retrieve,
|
||||
update,
|
||||
)
|
||||
from utils.fu_ninja import FuFilters, MyPagination
|
||||
|
||||
router = Router()
|
||||
|
||||
|
||||
# 设置过滤字段
|
||||
class Filters(FuFilters):
|
||||
|
||||
icon: str = Field(None, alias='icon')
|
||||
sequence: int = Field(None, alias='sequence')
|
||||
code: str = Field(None, alias='code')
|
||||
name: str = Field(None, alias='name')
|
||||
|
||||
|
||||
# 设置请求接收字段
|
||||
class TestSchemaIn(ModelSchema):
|
||||
|
||||
class Config:
|
||||
model = Test
|
||||
model_exclude = ['create_datetime', 'update_datetime', 'belong_dept', 'modifier', 'creator', 'creator', 'id']
|
||||
|
||||
|
||||
# 设置响应字段
|
||||
class TestSchemaOut(ModelSchema):
|
||||
|
||||
class Config:
|
||||
model = Test
|
||||
model_fields = '__all__'
|
||||
|
||||
|
||||
# 创建Test
|
||||
@router.post('/test', response=TestSchemaOut)
|
||||
def create_test(request, data: TestSchemaIn):
|
||||
test = create(request, data, Test)
|
||||
return test
|
||||
|
||||
|
||||
# 删除Test
|
||||
@router.delete('/test/{id}')
|
||||
def delete_test(request, id: int):
|
||||
delete(id, Test)
|
||||
return {'success': True}
|
||||
|
||||
|
||||
# 更新Test
|
||||
@router.put('/test/{id}', response=TestSchemaOut)
|
||||
def update_test(request, id: int, data: TestSchemaIn):
|
||||
test = update(request, id, data, Test)
|
||||
return test
|
||||
|
||||
|
||||
# 获取Test
|
||||
@router.get('/test', response=List[TestSchemaOut])
|
||||
@paginate(MyPagination)
|
||||
def list_test(request, filters: Filters = Query(...)):
|
||||
qs = retrieve(request, Test, filters)
|
||||
return qs
|
||||
|
||||
|
||||
# 导入
|
||||
@router.get('/test/all/export')
|
||||
def export_test(request):
|
||||
export_fields = ('icon', 'sequence', 'code', 'name', )
|
||||
return export_data(request, Test, TestSchemaOut, export_fields)
|
||||
|
||||
|
||||
# 导出
|
||||
@router.post('/test/all/import')
|
||||
def import_test(request, data: ImportSchema):
|
||||
import_fields = ('icon', 'sequence', 'code', 'name', )
|
||||
return import_data(request, Test, TestSchemaIn, data, import_fields)
|
||||
|
||||
17
code/generator/test/model.py
Normal file
17
code/generator/test/model.py
Normal file
@@ -0,0 +1,17 @@
|
||||
from django.db import models
|
||||
from utils.models import CoreModel
|
||||
|
||||
|
||||
class Test(CoreModel):
|
||||
|
||||
icon = models.CharField(null=True, blank=True, max_length=255, verbose_name='图标', help_text='图标')
|
||||
sequence = models.DecimalField(null=True, blank=True, max_digits = 13, decimal_places = 4, verbose_name='排序', help_text='排序')
|
||||
code = models.CharField(null=True, blank=True, max_length=255, verbose_name='代码', help_text='代码')
|
||||
name = models.CharField(null=True, blank=True, max_length=255, verbose_name='名称', help_text='名称')
|
||||
|
||||
class Meta:
|
||||
db_table = 'generator_test'
|
||||
verbose_name = '模板测试'
|
||||
verbose_name_plural = verbose_name
|
||||
ordering = ('-create_datetime',)
|
||||
|
||||
6
code/generator/test/router.py
Normal file
6
code/generator/test/router.py
Normal file
@@ -0,0 +1,6 @@
|
||||
from ninja import Router
|
||||
from .api import router
|
||||
|
||||
test_router = Router()
|
||||
test_router.add_router('/', router, tags=['模板测试'])
|
||||
|
||||
84
code/generator/test_demo/api.py
Normal file
84
code/generator/test_demo/api.py
Normal file
@@ -0,0 +1,84 @@
|
||||
from typing import List
|
||||
from .model import TestDemo
|
||||
from ninja import Field, ModelSchema, Query, Router
|
||||
from ninja.pagination import paginate
|
||||
from utils.fu_crud import (
|
||||
ImportSchema,
|
||||
create,
|
||||
delete,
|
||||
export_data,
|
||||
import_data,
|
||||
retrieve,
|
||||
update,
|
||||
)
|
||||
from utils.fu_ninja import FuFilters, MyPagination
|
||||
|
||||
router = Router()
|
||||
|
||||
|
||||
# 设置过滤字段
|
||||
class Filters(FuFilters):
|
||||
|
||||
des: str = Field(None, alias='des')
|
||||
code: int = Field(None, alias='code')
|
||||
name: str = Field(None, alias='name')
|
||||
|
||||
|
||||
# 设置请求接收字段
|
||||
class TestDemoSchemaIn(ModelSchema):
|
||||
|
||||
class Config:
|
||||
model = TestDemo
|
||||
model_exclude = ['create_datetime', 'update_datetime', 'belong_dept', 'modifier', 'creator', 'creator', 'id']
|
||||
|
||||
|
||||
# 设置响应字段
|
||||
class TestDemoSchemaOut(ModelSchema):
|
||||
|
||||
class Config:
|
||||
model = TestDemo
|
||||
model_fields = '__all__'
|
||||
|
||||
|
||||
# 创建TestDemo
|
||||
@router.post('/test_demo', response=TestDemoSchemaOut)
|
||||
def create_test_demo(request, data: TestDemoSchemaIn):
|
||||
test_demo = create(request, data, TestDemo)
|
||||
return test_demo
|
||||
|
||||
|
||||
# 删除TestDemo
|
||||
@router.delete('/test_demo/{id}')
|
||||
def delete_test_demo(request, id: int):
|
||||
delete(id, TestDemo)
|
||||
return {'success': True}
|
||||
|
||||
|
||||
# 更新TestDemo
|
||||
@router.put('/test_demo/{id}', response=TestDemoSchemaOut)
|
||||
def update_test_demo(request, id: int, data: TestDemoSchemaIn):
|
||||
test_demo = update(request, id, data, TestDemo)
|
||||
return test_demo
|
||||
|
||||
|
||||
# 获取TestDemo
|
||||
@router.get('/test_demo', response=List[TestDemoSchemaOut])
|
||||
@paginate(MyPagination)
|
||||
def list_test_demo(request, filters: Filters = Query(...)):
|
||||
qs = retrieve(request, TestDemo, filters)
|
||||
return qs
|
||||
|
||||
|
||||
# 导入
|
||||
@router.get('/test_demo/all/export')
|
||||
def export_test_demo(request):
|
||||
export_fields = ('des', 'code', 'name', )
|
||||
return export_data(request, TestDemo, TestDemoSchemaOut, export_fields)
|
||||
|
||||
|
||||
# 导出
|
||||
@router.post('/test_demo/all/import')
|
||||
def import_test_demo(request, data: ImportSchema):
|
||||
import_fields = ('des', 'code', 'name', )
|
||||
return import_data(request, TestDemo, TestDemoSchemaIn, data, import_fields)
|
||||
|
||||
16
code/generator/test_demo/model.py
Normal file
16
code/generator/test_demo/model.py
Normal file
@@ -0,0 +1,16 @@
|
||||
from django.db import models
|
||||
from utils.models import CoreModel
|
||||
|
||||
|
||||
class TestDemo(CoreModel):
|
||||
|
||||
des = models.TextField(null=True, blank=True, verbose_name='描述', help_text='描述')
|
||||
code = models.DecimalField(null=True, blank=True, max_digits = 13, decimal_places = 4, verbose_name='编码', help_text='编码')
|
||||
name = models.CharField(null=True, blank=True, max_length=255, verbose_name='名称', help_text='名称')
|
||||
|
||||
class Meta:
|
||||
db_table = 'generator_test_demo'
|
||||
verbose_name = '测试案例'
|
||||
verbose_name_plural = verbose_name
|
||||
ordering = ('-create_datetime',)
|
||||
|
||||
6
code/generator/test_demo/router.py
Normal file
6
code/generator/test_demo/router.py
Normal file
@@ -0,0 +1,6 @@
|
||||
from ninja import Router
|
||||
from .api import router
|
||||
|
||||
test_demo_router = Router()
|
||||
test_demo_router.add_router('/', router, tags=['测试案例'])
|
||||
|
||||
22
code/manage.py
Normal file
22
code/manage.py
Normal file
@@ -0,0 +1,22 @@
|
||||
#!/usr/bin/env python
|
||||
"""Django's command-line utility for administrative tasks."""
|
||||
import os
|
||||
import sys
|
||||
|
||||
|
||||
def main():
|
||||
"""Run administrative tasks."""
|
||||
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'fuadmin.settings')
|
||||
try:
|
||||
from django.core.management import execute_from_command_line
|
||||
except ImportError as exc:
|
||||
raise ImportError(
|
||||
"Couldn't import Django. Are you sure it's installed and "
|
||||
"available on your PYTHONPATH environment variable? Did you "
|
||||
"forget to activate a virtual environment?"
|
||||
) from exc
|
||||
execute_from_command_line(sys.argv)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
21
code/requirements.txt
Normal file
21
code/requirements.txt
Normal file
@@ -0,0 +1,21 @@
|
||||
django-ninja==0.19.1
|
||||
Django==4.0.8
|
||||
typing==3.7.4.3
|
||||
simplejwt==2.0.1
|
||||
pydantic==1.9.2
|
||||
requests==2.28.1
|
||||
# mysql
|
||||
mysqlclient==2.1.1
|
||||
# sqlserver
|
||||
# mssql-django==1.1.2
|
||||
# pyodbc==4.0.32
|
||||
django-celery-beat==2.3.0
|
||||
django-celery-results==2.3.1
|
||||
celery==5.2.7
|
||||
pypinyin==0.46.0
|
||||
user-agents==2.2.0
|
||||
django-redis==5.2.0
|
||||
openpyxl==3.0.10
|
||||
psutil==5.9.1
|
||||
daphne==4.0.0
|
||||
psycopg2-binary==2.9.9
|
||||
0
code/system/__init__.py
Normal file
0
code/system/__init__.py
Normal file
3
code/system/admin.py
Normal file
3
code/system/admin.py
Normal file
@@ -0,0 +1,3 @@
|
||||
from django.contrib import admin
|
||||
|
||||
# Register your models here.
|
||||
0
code/system/apis/__init__.py
Normal file
0
code/system/apis/__init__.py
Normal file
79
code/system/apis/button.py
Normal file
79
code/system/apis/button.py
Normal file
@@ -0,0 +1,79 @@
|
||||
# from application.ninja_cof import api
|
||||
# Author 臧成龙
|
||||
# coding=utf-8
|
||||
# @Time : 2022/5/21 02:36
|
||||
# @File : button.py
|
||||
# @Software: PyCharm
|
||||
# @qq: 939589097
|
||||
|
||||
from typing import List
|
||||
|
||||
from django.shortcuts import get_object_or_404
|
||||
from ninja import Field, ModelSchema, Query, Router, Schema
|
||||
from ninja.pagination import paginate
|
||||
from system.models import Button
|
||||
from utils.fu_ninja import MyPagination
|
||||
|
||||
router = Router()
|
||||
|
||||
|
||||
class Filters(Schema):
|
||||
name: str = Field(None, alias="name")
|
||||
code: str = Field(None, alias="code")
|
||||
id: str = Field(None, alias="id")
|
||||
|
||||
|
||||
class SchemaIn(ModelSchema):
|
||||
|
||||
class Config:
|
||||
model = Button
|
||||
model_exclude = ['id', 'create_datetime', 'update_datetime']
|
||||
|
||||
|
||||
class SchemaOut(ModelSchema):
|
||||
class Config:
|
||||
model = Button
|
||||
model_fields = "__all__"
|
||||
# model_fields = []
|
||||
|
||||
|
||||
@router.post("/button", response=SchemaOut)
|
||||
def create_button(request, data: SchemaIn):
|
||||
post = Button.objects.create(**data.dict())
|
||||
return post
|
||||
|
||||
|
||||
@router.get("/button/{button_id}", response=SchemaOut)
|
||||
def get_button(request, button_id: int):
|
||||
post = get_object_or_404(Button, id=button_id)
|
||||
return post
|
||||
|
||||
|
||||
@router.get("/button", response=List[SchemaOut])
|
||||
@paginate(MyPagination)
|
||||
def list_button(request, filters: Filters = Query(...)):
|
||||
qs = Button.objects.filter(**filters.dict(exclude_none=True))
|
||||
return qs
|
||||
|
||||
|
||||
@router.get("/button/all/list", response=List[SchemaOut])
|
||||
def all_list_button(request):
|
||||
qs = Button.objects.all()
|
||||
return qs
|
||||
|
||||
|
||||
@router.put("/button/{button_id}", response=SchemaOut)
|
||||
def update_button(request, button_id: int, payload: SchemaIn):
|
||||
post = get_object_or_404(Button, id=button_id)
|
||||
for attr, value in payload.dict().items():
|
||||
setattr(post, attr, value)
|
||||
post.save()
|
||||
return post
|
||||
|
||||
|
||||
@router.delete("/button/{button_id}")
|
||||
def delete_button(request, button_id: int):
|
||||
post = get_object_or_404(Button, id=button_id)
|
||||
post.delete()
|
||||
return {"success": True}
|
||||
|
||||
67
code/system/apis/celery_crontab.py
Normal file
67
code/system/apis/celery_crontab.py
Normal file
@@ -0,0 +1,67 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# @Time : 2022/6/14 00:13
|
||||
# @Author : 臧成龙
|
||||
# @FileName: crontab_schedule.py
|
||||
# @Software: PyCharm
|
||||
from typing import List
|
||||
|
||||
from django.shortcuts import get_object_or_404
|
||||
from django_celery_beat.models import CrontabSchedule
|
||||
from ninja import ModelSchema, Router
|
||||
from ninja.pagination import paginate
|
||||
from utils.fu_crud import create, delete, retrieve, update
|
||||
from utils.fu_ninja import MyPagination
|
||||
|
||||
router = Router()
|
||||
|
||||
|
||||
class SchemaIn(ModelSchema):
|
||||
class Config:
|
||||
model = CrontabSchedule
|
||||
model_fields = ['minute', 'hour', 'day_of_month', 'month_of_year', 'day_of_week']
|
||||
|
||||
|
||||
class SchemaOut(ModelSchema):
|
||||
class Config:
|
||||
model = CrontabSchedule
|
||||
model_fields = ['id', 'minute', 'hour', 'day_of_month', 'month_of_year', 'day_of_week']
|
||||
|
||||
|
||||
@router.post("/crontab_schedule", response=SchemaOut)
|
||||
def create_crontab_schedule(request, data: SchemaIn):
|
||||
qs = CrontabSchedule.objects.create(**data.dict())
|
||||
return qs
|
||||
|
||||
|
||||
@router.delete("/crontab_schedule/{crontab_schedule_id}")
|
||||
def delete_crontab_schedule(request, crontab_schedule_id: int):
|
||||
delete(crontab_schedule_id, CrontabSchedule)
|
||||
return {"success": True}
|
||||
|
||||
|
||||
@router.put("/crontab_schedule/{crontab_schedule_id}", response=SchemaOut)
|
||||
def update_crontab_schedule(request, crontab_schedule_id: int, data: SchemaIn):
|
||||
instance = get_object_or_404(CrontabSchedule, id=crontab_schedule_id)
|
||||
for attr, value in data.dict().items():
|
||||
setattr(instance, attr, value)
|
||||
instance.save()
|
||||
return instance
|
||||
|
||||
|
||||
@router.get("/crontab_schedule", response=List[SchemaOut])
|
||||
@paginate(MyPagination)
|
||||
def list_crontab_schedule(request):
|
||||
qs = retrieve(request, CrontabSchedule)
|
||||
return qs
|
||||
|
||||
|
||||
@router.get("/crontab_schedule/{crontab_schedule_id}", response=SchemaOut)
|
||||
def get_crontab_schedule(request, crontab_schedule_id: int):
|
||||
qs = get_object_or_404(CrontabSchedule, id=crontab_schedule_id)
|
||||
return qs
|
||||
|
||||
|
||||
@router.get("/crontab_schedule/all/list", response=List[SchemaOut])
|
||||
def all_list_role(request):
|
||||
qs = retrieve(request, CrontabSchedule)
|
||||
return qs
|
||||
67
code/system/apis/celery_interval.py
Normal file
67
code/system/apis/celery_interval.py
Normal file
@@ -0,0 +1,67 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# @Time : 2022/6/14 00:32
|
||||
# @Author : 臧成龙
|
||||
# @FileName: interval_schedule.py
|
||||
# @Software: PyCharm
|
||||
from typing import List
|
||||
|
||||
from django.shortcuts import get_object_or_404
|
||||
from django_celery_beat.models import IntervalSchedule
|
||||
from ninja import ModelSchema, Router
|
||||
from ninja.pagination import paginate
|
||||
from utils.fu_crud import create, delete, retrieve, update
|
||||
from utils.fu_ninja import MyPagination
|
||||
|
||||
router = Router()
|
||||
|
||||
|
||||
class SchemaIn(ModelSchema):
|
||||
class Config:
|
||||
model = IntervalSchedule
|
||||
model_fields = ['every', 'period']
|
||||
|
||||
|
||||
class SchemaOut(ModelSchema):
|
||||
class Config:
|
||||
model = IntervalSchedule
|
||||
model_fields = ['id', 'every', 'period']
|
||||
|
||||
|
||||
@router.post("/interval_schedule", response=SchemaOut)
|
||||
def create_interval_schedule(request, data: SchemaIn):
|
||||
qs = IntervalSchedule.objects.create(**data.dict())
|
||||
return qs
|
||||
|
||||
|
||||
@router.delete("/interval_schedule/{interval_schedule_id}")
|
||||
def delete_interval_schedule(request, interval_schedule_id: int):
|
||||
delete(interval_schedule_id, IntervalSchedule)
|
||||
return {"success": True}
|
||||
|
||||
|
||||
@router.put("/interval_schedule/{interval_schedule_id}", response=SchemaOut)
|
||||
def update_interval_schedule(request, interval_schedule_id: int, data: SchemaIn):
|
||||
instance = get_object_or_404(IntervalSchedule, id=interval_schedule_id)
|
||||
for attr, value in data.dict().items():
|
||||
setattr(instance, attr, value)
|
||||
instance.save()
|
||||
return instance
|
||||
|
||||
|
||||
@router.get("/interval_schedule", response=List[SchemaOut])
|
||||
@paginate(MyPagination)
|
||||
def list_interval_schedule(request):
|
||||
qs = retrieve(request, IntervalSchedule)
|
||||
return qs
|
||||
|
||||
|
||||
@router.get("/interval_schedule/{interval_schedule_id}", response=SchemaOut)
|
||||
def get_interval_schedule(request, interval_schedule_id: int):
|
||||
qs = get_object_or_404(IntervalSchedule, id=interval_schedule_id)
|
||||
return qs
|
||||
|
||||
|
||||
@router.get("/interval_schedule/all/list", response=List[SchemaOut])
|
||||
def all_list_role(request):
|
||||
qs = retrieve(request, IntervalSchedule)
|
||||
return qs
|
||||
96
code/system/apis/celery_periodic.py
Normal file
96
code/system/apis/celery_periodic.py
Normal file
@@ -0,0 +1,96 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# @Time : 2022/6/14 01:50
|
||||
# @Author : 臧成龙
|
||||
# @FileName: periodic_task.py
|
||||
# @Software: PyCharm
|
||||
from typing import List
|
||||
|
||||
from django.forms import model_to_dict
|
||||
from django.shortcuts import get_object_or_404
|
||||
from django_celery_beat.models import CrontabSchedule, IntervalSchedule, PeriodicTask
|
||||
from ninja import ModelSchema, Router, Schema
|
||||
from ninja.pagination import paginate
|
||||
from pydantic import Field
|
||||
from system.apis import celery_crontab, celery_interval
|
||||
from utils.fu_crud import create, delete, retrieve, update
|
||||
from utils.fu_ninja import MyPagination
|
||||
from utils.fu_response import FuResponse
|
||||
|
||||
router = Router()
|
||||
|
||||
|
||||
class SchemaIn(ModelSchema):
|
||||
interval_id: int = Field(None, alias="interval")
|
||||
crontab_id: int = Field(None, alias="crontab")
|
||||
|
||||
class Config:
|
||||
model = PeriodicTask
|
||||
model_fields = ['task', 'name', 'enabled']
|
||||
|
||||
|
||||
class SchemaOut(ModelSchema):
|
||||
interval: celery_interval.SchemaOut = None
|
||||
crontab: celery_crontab.SchemaOut = None
|
||||
|
||||
class Config:
|
||||
model = PeriodicTask
|
||||
model_fields = ['id', 'task', 'name', 'enabled']
|
||||
|
||||
|
||||
@router.post("/periodic_task", response=SchemaOut)
|
||||
def create_periodic_task(request, data: SchemaIn):
|
||||
qs = PeriodicTask.objects.create(**data.dict())
|
||||
return qs
|
||||
|
||||
|
||||
@router.delete("/periodic_task/{periodic_task_id}")
|
||||
def delete_periodic_task(request, periodic_task_id: int):
|
||||
delete(periodic_task_id, PeriodicTask)
|
||||
return {"success": True}
|
||||
|
||||
|
||||
@router.put("/periodic_task/{periodic_task_id}", response=SchemaOut)
|
||||
def update_periodic_task(request, periodic_task_id: int, data: SchemaIn):
|
||||
instance = get_object_or_404(PeriodicTask, id=periodic_task_id)
|
||||
for attr, value in data.dict().items():
|
||||
setattr(instance, attr, value)
|
||||
instance.save()
|
||||
return instance
|
||||
|
||||
|
||||
@router.get("/periodic_task", response=List[SchemaOut])
|
||||
@paginate(MyPagination)
|
||||
def list_periodic_task(request):
|
||||
qs = retrieve(request, PeriodicTask)
|
||||
return qs
|
||||
|
||||
|
||||
@router.get("/periodic_task/{periodic_task_id}", response=SchemaOut)
|
||||
def get_periodic_task(request, periodic_task_id: int):
|
||||
qs = get_object_or_404(PeriodicTask, id=periodic_task_id)
|
||||
return qs
|
||||
|
||||
|
||||
@router.get("/periodic_task/all/list", response=List[SchemaOut])
|
||||
def all_list_role(request):
|
||||
qs = retrieve(request, PeriodicTask)
|
||||
return qs
|
||||
|
||||
|
||||
class SchemaExecIn(Schema):
|
||||
task: str = None
|
||||
|
||||
|
||||
@router.post("/periodic_task/immediate/exec")
|
||||
def immediate_exec_task(request, data: SchemaExecIn):
|
||||
task_name = data.task
|
||||
data = {
|
||||
'task': None
|
||||
}
|
||||
test = f"""
|
||||
from {'.'.join(task_name.split('.')[:-1])} import {task_name.split('.')[-1]}
|
||||
task = {task_name.split('.')[-1]}.delay()
|
||||
"""
|
||||
exec(test)
|
||||
|
||||
return FuResponse()
|
||||
241
code/system/apis/code_generator.py
Normal file
241
code/system/apis/code_generator.py
Normal file
@@ -0,0 +1,241 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# @Time : 2022/5/10 21:56
|
||||
# @Author : 臧成龙
|
||||
# @FileName: generator_template.py
|
||||
# @Software: PyCharm
|
||||
import json
|
||||
import os
|
||||
import subprocess
|
||||
from typing import List
|
||||
|
||||
from django.core import management
|
||||
from django.shortcuts import get_object_or_404
|
||||
from ninja import Field, ModelSchema, Query, Router
|
||||
from ninja.pagination import paginate
|
||||
|
||||
from system.code_template.backend.api import generator_backend_api
|
||||
from system.code_template.backend.model import generator_backend_model
|
||||
from system.code_template.backend.router import generator_router
|
||||
from system.code_template.web.api_template import generator_api
|
||||
from system.code_template.web.data_template import generator_data
|
||||
from system.code_template.web.drawer_template import generator_drawer
|
||||
from system.code_template.web.index_template import generator_index
|
||||
from system.models import GeneratorTemplate, Menu, MenuButton, MenuColumnField
|
||||
from utils.fu_crud import (
|
||||
ImportSchema,
|
||||
create,
|
||||
delete,
|
||||
export_data,
|
||||
import_data,
|
||||
retrieve,
|
||||
update, batch_create, )
|
||||
from utils.fu_ninja import FuFilters, MyPagination
|
||||
from utils.fu_response import FuResponse
|
||||
from utils.usual import insert_content_after_line
|
||||
|
||||
router = Router()
|
||||
|
||||
|
||||
class Filters(FuFilters):
|
||||
name: str = Field(None, alias="name")
|
||||
code: str = Field(None, alias="code")
|
||||
id: str = Field(None, alias="id")
|
||||
|
||||
|
||||
class GeneratorTemplateSchemaIn(ModelSchema):
|
||||
class Config:
|
||||
model = GeneratorTemplate
|
||||
model_fields = ['name', 'code', 'form_info', 'table_info', 'remark']
|
||||
|
||||
|
||||
class GeneratorTemplateSchemaOut(ModelSchema):
|
||||
class Config:
|
||||
model = GeneratorTemplate
|
||||
model_fields = "__all__"
|
||||
|
||||
|
||||
@router.post("/generator_template", response=GeneratorTemplateSchemaOut)
|
||||
def create_generator_template(request, data: GeneratorTemplateSchemaIn):
|
||||
generator_template = create(request, data, GeneratorTemplate)
|
||||
return generator_template
|
||||
|
||||
|
||||
@router.delete("/generator_template/{generator_template_id}")
|
||||
def delete_generator_template(request, generator_template_id: int):
|
||||
delete(generator_template_id, GeneratorTemplate)
|
||||
return {"success": True}
|
||||
|
||||
|
||||
@router.put("/generator_template/{generator_template_id}", response=GeneratorTemplateSchemaOut)
|
||||
def update_generator_template(request, generator_template_id: int, data: GeneratorTemplateSchemaIn):
|
||||
generator_template = update(request, generator_template_id, data, GeneratorTemplate)
|
||||
return generator_template
|
||||
|
||||
|
||||
@router.get("/generator_template", response=List[GeneratorTemplateSchemaOut])
|
||||
@paginate(MyPagination)
|
||||
def list_generator_template(request, filters: Filters = Query(...)):
|
||||
qs = retrieve(request, GeneratorTemplate, filters)
|
||||
return qs
|
||||
|
||||
|
||||
@router.get("/generator_template/{generator_template_id}", response=GeneratorTemplateSchemaOut)
|
||||
def get_generator_template(request, generator_template_id: int):
|
||||
generator_template = get_object_or_404(GeneratorTemplate, id=generator_template_id)
|
||||
return generator_template
|
||||
|
||||
|
||||
@router.get("/generator_template/all/list", response=List[GeneratorTemplateSchemaOut])
|
||||
def all_list_generator_template(request):
|
||||
qs = retrieve(request, GeneratorTemplate)
|
||||
return qs
|
||||
|
||||
|
||||
@router.get("/generator_template/all/export")
|
||||
def export_generator_template(request):
|
||||
export_fields = ['name', 'code', 'status', 'sort']
|
||||
return export_data(request, GeneratorTemplate, GeneratorTemplateSchemaOut, export_fields)
|
||||
|
||||
|
||||
@router.post("/generator_template/all/import")
|
||||
def import_generator_template(request, data: ImportSchema):
|
||||
import_fields = ['name', 'code', 'status', 'sort']
|
||||
return import_data(request, GeneratorTemplate, GeneratorTemplateSchemaIn, data, import_fields)
|
||||
|
||||
|
||||
@router.put("/generator_template/code/generate/{generator_template_id}")
|
||||
def generate_code(request, generator_template_id: int):
|
||||
instance = get_object_or_404(GeneratorTemplate, id=generator_template_id)
|
||||
web_index_txt = generator_index(instance)
|
||||
web_data_txt = generator_data(instance)
|
||||
web_drawer_txt = generator_drawer(instance)
|
||||
web_api_txt = generator_api(instance)
|
||||
web_target_path = os.path.abspath(
|
||||
os.path.join(os.getcwd(), "..", 'web', 'src', 'views', 'generator', instance.code))
|
||||
# 判断当前路径是否存在,没有则创建文件夹
|
||||
if not os.path.exists(web_target_path):
|
||||
os.makedirs(web_target_path)
|
||||
|
||||
web_index_path = os.path.join(web_target_path, 'index.vue')
|
||||
with open(web_index_path, 'w', encoding='utf-8') as file:
|
||||
file.write(web_index_txt)
|
||||
|
||||
web_data_path = os.path.join(web_target_path, 'data.ts')
|
||||
with open(web_data_path, 'w', encoding='utf-8') as file:
|
||||
file.write(web_data_txt)
|
||||
|
||||
web_api_path = os.path.join(web_target_path, 'api.ts')
|
||||
with open(web_api_path, 'w', encoding='utf-8') as file:
|
||||
file.write(web_api_txt)
|
||||
|
||||
web_drawer_path = os.path.join(web_target_path, 'drawer.vue')
|
||||
with open(web_drawer_path, 'w', encoding='utf-8') as file:
|
||||
file.write(web_drawer_txt)
|
||||
# 添加列表字段,菜单和菜单按钮
|
||||
if not instance.has_menu:
|
||||
# 添加菜单
|
||||
menu_dic = {
|
||||
"type": 1,
|
||||
"title": instance.name,
|
||||
"parent_id": 33,
|
||||
"sort": 1,
|
||||
"icon": "ant-design:book-outlined",
|
||||
"path": instance.code,
|
||||
"status": True,
|
||||
"hide_menu": False,
|
||||
"component": f"/generator/{instance.code}/index.vue",
|
||||
"name": instance.code,
|
||||
"is_ext": False,
|
||||
"keepalive": False
|
||||
}
|
||||
menu_qr = create(request, menu_dic, Menu)
|
||||
# 添加菜单按钮
|
||||
button_list = [
|
||||
{
|
||||
"name": "新增",
|
||||
"code": f"{instance.code}:add",
|
||||
"method": 1,
|
||||
"api": f"/api/generator/{instance.code}",
|
||||
"sort": 1,
|
||||
"menu_id": menu_qr.id
|
||||
},
|
||||
{
|
||||
"name": "删除",
|
||||
"code": f"{instance.code}:delete",
|
||||
"method": 3,
|
||||
"api": f"/api/generator/{instance.code}/{instance.code}_id",
|
||||
"sort": 2,
|
||||
"menu_id": menu_qr.id
|
||||
},
|
||||
{
|
||||
"name": "修改",
|
||||
"code": f"{instance.code}:update",
|
||||
"method": 2,
|
||||
"api": f"/api/generator/{instance.code}/{instance.code}_id",
|
||||
"sort": 3,
|
||||
"menu_id": menu_qr.id
|
||||
},
|
||||
{
|
||||
"name": "查询",
|
||||
"code": f"{instance.code}:search",
|
||||
"method": 0,
|
||||
"api": f"/api/generator/{instance.code}",
|
||||
"sort": 4,
|
||||
"menu_id": menu_qr.id
|
||||
}
|
||||
]
|
||||
batch_create(request, button_list, MenuButton)
|
||||
|
||||
# 添加列表字段
|
||||
table_info = json.loads(instance.table_info)
|
||||
column_info = table_info.get('columnInfo')
|
||||
column_list = []
|
||||
for item in column_info:
|
||||
column_list.append({
|
||||
'code': instance.code + ':' + item['field_name'],
|
||||
'name': item['column_name'],
|
||||
'menu_id': menu_qr.id,
|
||||
})
|
||||
batch_create(request, column_list, MenuColumnField)
|
||||
|
||||
# 生成后端代码
|
||||
backend_model_txt = generator_backend_model(instance)
|
||||
backend_api_txt = generator_backend_api(instance)
|
||||
backend_router_txt = generator_router(instance)
|
||||
|
||||
# 更新generator router
|
||||
generator_router_path = os.path.abspath(os.path.join(os.getcwd(), 'generator', 'router.py'))
|
||||
|
||||
if not instance.has_menu:
|
||||
insert_from_txt = f'''from .{instance.code}.api import router as {instance.code}_router'''
|
||||
insert_content_after_line(generator_router_path, 'from ninja import Router', insert_from_txt)
|
||||
insert_router_txt = f'''generator_router.add_router('/', {instance.code}_router, tags=['{instance.name}'])'''
|
||||
insert_content_after_line(generator_router_path, 'generator_router = Router()', insert_router_txt)
|
||||
instance.has_menu = True
|
||||
instance.save()
|
||||
|
||||
backend_target_path = os.path.abspath(os.path.join(os.getcwd(), 'generator', instance.code))
|
||||
|
||||
# 判断当前路径是否存在,没有则创建文件夹
|
||||
if not os.path.exists(backend_target_path):
|
||||
os.makedirs(backend_target_path)
|
||||
backend_model_path = os.path.join(backend_target_path, 'model.py')
|
||||
with open(backend_model_path, 'w', encoding='utf-8') as file:
|
||||
file.write(backend_model_txt)
|
||||
|
||||
backend_api_path = os.path.join(backend_target_path, 'api.py')
|
||||
with open(backend_api_path, 'w', encoding='utf-8') as file:
|
||||
file.write(backend_api_txt)
|
||||
|
||||
backend_router_path = os.path.join(backend_target_path, 'router.py')
|
||||
with open(backend_router_path, 'w', encoding='utf-8') as file:
|
||||
file.write(backend_router_txt)
|
||||
|
||||
return FuResponse(msg='代码生成成功')
|
||||
|
||||
|
||||
@router.put("/generator_template/code/generate_db/{generator_template_id}")
|
||||
def generate_db(request, generator_template_id: int):
|
||||
management.call_command('makemigrations','generator')
|
||||
management.call_command('migrate','generator')
|
||||
return FuResponse(msg='数据库生成成功')
|
||||
6
code/system/apis/data_dict/__init__.py
Normal file
6
code/system/apis/data_dict/__init__.py
Normal file
@@ -0,0 +1,6 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
# time: 2022/6/28 16:58
|
||||
# file: __init__.py.py
|
||||
# author: 臧成龙
|
||||
# QQ: 939589097
|
||||
79
code/system/apis/data_dict/category_dict.py
Normal file
79
code/system/apis/data_dict/category_dict.py
Normal file
@@ -0,0 +1,79 @@
|
||||
# from application.ninja_cof import api
|
||||
# Author 臧成龙
|
||||
# coding=utf-8
|
||||
# @Time : 2022/5/15 21:47
|
||||
# @File : category_dict.py
|
||||
# @Software: PyCharm
|
||||
# @qq: 939589097
|
||||
|
||||
from typing import List
|
||||
|
||||
from django.shortcuts import get_object_or_404
|
||||
from ninja import Field, ModelSchema, Query, Router, Schema
|
||||
from ninja.pagination import paginate
|
||||
from system.models import CategoryDict
|
||||
from utils.fu_crud import create, delete, retrieve, update
|
||||
from utils.fu_ninja import FuFilters, MyPagination
|
||||
from utils.fu_response import FuResponse
|
||||
from utils.list_to_tree import list_to_tree
|
||||
|
||||
router = Router()
|
||||
|
||||
|
||||
class Filters(FuFilters):
|
||||
label: str = Field(None, alias="label")
|
||||
value: str = Field(None, alias="value")
|
||||
code: str = Field(None, alias="code")
|
||||
|
||||
|
||||
class SchemaIn(ModelSchema):
|
||||
parent_id: int = None
|
||||
|
||||
class Config:
|
||||
model = CategoryDict
|
||||
model_exclude = ['id', 'parent', 'create_datetime', 'update_datetime']
|
||||
|
||||
|
||||
class SchemaOut(ModelSchema):
|
||||
class Config:
|
||||
model = CategoryDict
|
||||
model_fields = "__all__"
|
||||
# model_fields = []
|
||||
|
||||
|
||||
@router.post("/category_dict", response=SchemaOut)
|
||||
def create_category_dict(request, data: SchemaIn):
|
||||
category_dict = create(request, data, CategoryDict)
|
||||
return category_dict
|
||||
|
||||
|
||||
@router.delete("/category_dict/{category_dict_id}")
|
||||
def delete_category_dict(request, category_dict_id: int):
|
||||
delete(category_dict_id, CategoryDict)
|
||||
return {"success": True}
|
||||
|
||||
|
||||
@router.put("/category_dict/{category_dict_id}", response=SchemaOut)
|
||||
def update_category_dict(request, category_dict_id: int, data: SchemaIn):
|
||||
category_dict = update(request, category_dict_id, data, CategoryDict)
|
||||
return category_dict
|
||||
|
||||
|
||||
@router.get("/category_dict", response=List[SchemaOut])
|
||||
@paginate(MyPagination)
|
||||
def list_category_dict(request, filters: Filters = Query(...)):
|
||||
qs = retrieve(request, CategoryDict, filters)
|
||||
return qs
|
||||
|
||||
|
||||
@router.get("/category_dict/{category_dict_id}", response=SchemaOut)
|
||||
def get_category_dict(request, category_dict_id: int):
|
||||
category_dict = get_object_or_404(CategoryDict, id=category_dict_id)
|
||||
return category_dict
|
||||
|
||||
|
||||
@router.get("/category_dict/list/tree")
|
||||
def list_category_dict_tree(request, filters: Filters = Query(...)):
|
||||
qs = retrieve(request, CategoryDict, filters).values()
|
||||
category_dict_tree = list_to_tree(list(qs))
|
||||
return FuResponse(data=category_dict_tree)
|
||||
70
code/system/apis/data_dict/dict.py
Normal file
70
code/system/apis/data_dict/dict.py
Normal file
@@ -0,0 +1,70 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# @Time : 2022/6/07 00:56
|
||||
# @Author : 臧成龙
|
||||
# @FileName: dict.py
|
||||
# @Software: PyCharm
|
||||
from typing import List
|
||||
|
||||
from django.shortcuts import get_object_or_404
|
||||
from ninja import Field, ModelSchema, Query, Router, Schema
|
||||
from ninja.pagination import paginate
|
||||
from system.models import Dict
|
||||
from utils.fu_crud import create, delete, retrieve, update
|
||||
from utils.fu_ninja import FuFilters, MyPagination
|
||||
|
||||
router = Router()
|
||||
|
||||
|
||||
class Filters(FuFilters):
|
||||
name: str = Field(None, alias="name")
|
||||
status: bool = Field(None, alias="status")
|
||||
id: str = Field(None, alias="dict_id")
|
||||
|
||||
|
||||
class SchemaIn(ModelSchema):
|
||||
class Config:
|
||||
model = Dict
|
||||
model_fields = ['name', 'code', 'sort', 'status']
|
||||
|
||||
|
||||
class SchemaOut(ModelSchema):
|
||||
class Config:
|
||||
model = Dict
|
||||
model_fields = ['id', 'name', 'code', 'sort', 'status']
|
||||
|
||||
|
||||
@router.post("/dict", response=SchemaOut)
|
||||
def create_dict(request, data: SchemaIn):
|
||||
qs = create(request, data, Dict)
|
||||
return qs
|
||||
|
||||
|
||||
@router.delete("/dict/{dict_id}")
|
||||
def delete_dict(request, dict_id: int):
|
||||
delete(dict_id, Dict)
|
||||
return {"success": True}
|
||||
|
||||
|
||||
@router.put("/dict/{dict_id}", response=SchemaOut)
|
||||
def update_dict(request, dict_id: int, data: SchemaIn):
|
||||
qs = update(request, dict_id, data, Dict)
|
||||
return qs
|
||||
|
||||
|
||||
@router.get("/dict", response=List[SchemaOut])
|
||||
@paginate(MyPagination)
|
||||
def list_dict(request, filters: Filters = Query(...)):
|
||||
qs = retrieve(request, Dict, filters)
|
||||
return qs
|
||||
|
||||
|
||||
@router.get("/dict/{dict_id}", response=SchemaOut)
|
||||
def get_dict(request, dict_id: int):
|
||||
qs = get_object_or_404(Dict, id=dict_id)
|
||||
return qs
|
||||
|
||||
|
||||
@router.get("/dict/all/list", response=List[SchemaOut])
|
||||
def all_list_role(request):
|
||||
qs = retrieve(request, Dict)
|
||||
return qs
|
||||
85
code/system/apis/data_dict/dict_item.py
Normal file
85
code/system/apis/data_dict/dict_item.py
Normal file
@@ -0,0 +1,85 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# @Time : 2022/6/07 00:56
|
||||
# @Author : 臧成龙
|
||||
# @FileName: dict_item.py
|
||||
# @Software: PyCharm
|
||||
from typing import List
|
||||
|
||||
from django.shortcuts import get_object_or_404
|
||||
from ninja import Field, ModelSchema, Query, Router, Schema
|
||||
from ninja.pagination import paginate
|
||||
from system.models import Dict, DictItem
|
||||
from utils.fu_crud import create, delete, retrieve, update
|
||||
from utils.fu_ninja import FuFilters, MyPagination
|
||||
|
||||
router = Router()
|
||||
|
||||
|
||||
class Filters(FuFilters):
|
||||
label: str = Field(None, alias="label")
|
||||
value: str = Field(None, alias="value")
|
||||
dict_id: str = Field(None, alias="dict_id")
|
||||
code: str = Field(None, alias="code")
|
||||
status: bool = Field(None, alias="status")
|
||||
|
||||
|
||||
class SchemaIn(ModelSchema):
|
||||
dict_id: int = Field(None, alias="dict_id")
|
||||
|
||||
class Config:
|
||||
model = DictItem
|
||||
model_fields = ['label', 'value', 'sort', 'icon', 'status']
|
||||
|
||||
|
||||
class SchemaOut(ModelSchema):
|
||||
class Config:
|
||||
model = DictItem
|
||||
model_fields = ['id', 'label', 'value', 'sort', 'icon', 'status']
|
||||
|
||||
|
||||
@router.post("/dict_item", response=SchemaOut)
|
||||
def create_dict_item(request, data: SchemaIn):
|
||||
qs = create(request, data, DictItem)
|
||||
return qs
|
||||
|
||||
|
||||
@router.delete("/dict_item/{dict_item_id}")
|
||||
def delete_dict_item(request, dict_item_id: int):
|
||||
delete(dict_item_id, DictItem)
|
||||
return {"success": True}
|
||||
|
||||
|
||||
@router.put("/dict_item/{dict_item_id}", response=SchemaOut)
|
||||
def update_dict_item(request, dict_item_id: int, data: SchemaIn):
|
||||
qs = update(request, dict_item_id, data, DictItem)
|
||||
return qs
|
||||
|
||||
|
||||
@router.get("/dict_item", response=List[SchemaOut])
|
||||
@paginate(MyPagination)
|
||||
def list_dict_item(request, filters: Filters = Query(...)):
|
||||
qs = retrieve(request, DictItem, filters)
|
||||
return qs
|
||||
|
||||
|
||||
@router.get("/dict_item/{dict_item_id}", response=SchemaOut)
|
||||
def get_dict_item(request, dict_item_id: int):
|
||||
qs = get_object_or_404(DictItem, id=dict_item_id)
|
||||
return qs
|
||||
|
||||
|
||||
@router.get("/dict_item/all/list", response=List[SchemaOut])
|
||||
def all_list_role(request):
|
||||
qs = retrieve(request, DictItem)
|
||||
return qs
|
||||
|
||||
|
||||
@router.get("/dict_item/by/code", response=List[SchemaOut])
|
||||
def list_dict_item_by_code(request, filters: Filters = Query(...)):
|
||||
filters.status = True
|
||||
dict_qs = retrieve(request, Dict, filters).first()
|
||||
if dict_qs is None:
|
||||
return []
|
||||
else:
|
||||
item_qs = dict_qs.dictItem.filter(status=True)
|
||||
return item_qs
|
||||
79
code/system/apis/dept.py
Normal file
79
code/system/apis/dept.py
Normal file
@@ -0,0 +1,79 @@
|
||||
# from application.ninja_cof import api
|
||||
# Author 臧成龙
|
||||
# coding=utf-8
|
||||
# @Time : 2022/5/15 21:47
|
||||
# @File : dept.py
|
||||
# @Software: PyCharm
|
||||
# @qq: 939589097
|
||||
|
||||
from typing import List
|
||||
|
||||
from django.shortcuts import get_object_or_404
|
||||
from ninja import Field, ModelSchema, Query, Router, Schema
|
||||
from ninja.pagination import paginate
|
||||
from system.models import Dept
|
||||
from utils.fu_crud import create, delete, retrieve, update
|
||||
from utils.fu_ninja import FuFilters, MyPagination
|
||||
from utils.fu_response import FuResponse
|
||||
from utils.list_to_tree import list_to_route, list_to_tree
|
||||
|
||||
router = Router()
|
||||
|
||||
|
||||
class Filters(FuFilters):
|
||||
name: str = Field(None, alias="name")
|
||||
status: bool = Field(None, alias="status")
|
||||
id: str = Field(None, alias="id")
|
||||
|
||||
|
||||
class SchemaIn(ModelSchema):
|
||||
parent_id: int = None
|
||||
|
||||
class Config:
|
||||
model = Dept
|
||||
model_exclude = ['id', 'parent', 'create_datetime', 'update_datetime']
|
||||
|
||||
|
||||
class SchemaOut(ModelSchema):
|
||||
class Config:
|
||||
model = Dept
|
||||
model_fields = "__all__"
|
||||
# model_fields = []
|
||||
|
||||
|
||||
@router.post("/dept", response=SchemaOut)
|
||||
def create_dept(request, data: SchemaIn):
|
||||
dept = create(request, data, Dept)
|
||||
return dept
|
||||
|
||||
|
||||
@router.delete("/dept/{dept_id}")
|
||||
def delete_dept(request, dept_id: int):
|
||||
delete(dept_id, Dept)
|
||||
return {"success": True}
|
||||
|
||||
|
||||
@router.put("/dept/{dept_id}", response=SchemaOut)
|
||||
def update_dept(request, dept_id: int, data: SchemaIn):
|
||||
dept = update(request, dept_id, data, Dept)
|
||||
return dept
|
||||
|
||||
|
||||
@router.get("/dept", response=List[SchemaOut])
|
||||
@paginate(MyPagination)
|
||||
def list_dept(request, filters: Filters = Query(...)):
|
||||
qs = retrieve(request, Dept, filters)
|
||||
return qs
|
||||
|
||||
|
||||
@router.get("/dept/{dept_id}", response=SchemaOut)
|
||||
def get_dept(request, dept_id: int):
|
||||
dept = get_object_or_404(Dept, id=dept_id)
|
||||
return dept
|
||||
|
||||
|
||||
@router.get("/dept/list/tree")
|
||||
def list_dept_tree(request, filters: Filters = Query(...)):
|
||||
qs = retrieve(request, Dept, filters).values()
|
||||
dept_tree = list_to_tree(list(qs))
|
||||
return FuResponse(data=dept_tree)
|
||||
99
code/system/apis/file.py
Normal file
99
code/system/apis/file.py
Normal file
@@ -0,0 +1,99 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# @Time : 2022/6/07 00:56
|
||||
# @Author : 臧成龙
|
||||
# @FileName: file.py
|
||||
# @Software: PyCharm
|
||||
import os
|
||||
from datetime import datetime
|
||||
from typing import List
|
||||
from urllib.parse import unquote
|
||||
|
||||
from django.http import FileResponse, StreamingHttpResponse, HttpResponse
|
||||
from django.shortcuts import get_object_or_404
|
||||
from fuadmin.settings import BASE_DIR, STATIC_URL
|
||||
from ninja import Field
|
||||
from ninja import File as NinjaFile
|
||||
from ninja import ModelSchema, Query, Router, Schema
|
||||
from ninja.files import UploadedFile
|
||||
from ninja.pagination import paginate
|
||||
from system.models import File
|
||||
from utils.fu_crud import create, delete, retrieve, update
|
||||
from utils.fu_ninja import FuFilters, MyPagination
|
||||
|
||||
router = Router()
|
||||
|
||||
|
||||
class Filters(FuFilters):
|
||||
name: str = Field(None, alias="name")
|
||||
|
||||
|
||||
class SchemaIn(Schema):
|
||||
name: str = Field(None, alias="name")
|
||||
url: str = Field(None, alias="url")
|
||||
|
||||
|
||||
class SchemaOut(ModelSchema):
|
||||
class Config:
|
||||
model = File
|
||||
model_fields = "__all__"
|
||||
|
||||
|
||||
@router.delete("/file/{file_id}")
|
||||
def delete_file(request, file_id: int):
|
||||
delete(file_id, File)
|
||||
return {"success": True}
|
||||
|
||||
|
||||
@router.get("/file", response=List[SchemaOut])
|
||||
@paginate(MyPagination)
|
||||
def list_file(request, filters: Filters = Query(...)):
|
||||
qs = retrieve(request, File, filters)
|
||||
return qs
|
||||
|
||||
|
||||
@router.get("/file/{file_id}", response=SchemaOut)
|
||||
def get_file(request, file_id: int):
|
||||
qs = get_object_or_404(File, id=file_id)
|
||||
return qs
|
||||
|
||||
|
||||
@router.get("/file/all/list", response=List[SchemaOut])
|
||||
def all_list_role(request):
|
||||
qs = retrieve(request, File)
|
||||
return qs
|
||||
|
||||
|
||||
@router.post("/upload", response=SchemaOut)
|
||||
def upload(request, file: UploadedFile = NinjaFile(...)):
|
||||
binary_data = file.read()
|
||||
current_date = datetime.now().strftime('%Y%m%d%H%M%S%f')
|
||||
current_ymd = datetime.now().strftime('%Y%m%d')
|
||||
file_name = current_date + '_' + file.name.replace(' ', '_')
|
||||
file_path = os.path.join(STATIC_URL, current_ymd)
|
||||
if not os.path.exists(file_path):
|
||||
os.makedirs(file_path)
|
||||
file_url = os.path.join(file_path, file_name)
|
||||
with open(file_url, 'wb') as f:
|
||||
f.write(binary_data)
|
||||
data = {
|
||||
'name': file.name,
|
||||
'size': file.size,
|
||||
'save_name': file_name,
|
||||
'url': file_url,
|
||||
}
|
||||
qs = create(request, data, File)
|
||||
return qs
|
||||
|
||||
|
||||
@router.post("/download")
|
||||
def create_post(request, data: SchemaIn):
|
||||
filePath = str(BASE_DIR) + unquote(data.url)
|
||||
r = FileResponse(open(filePath, "rb"), as_attachment=True)
|
||||
return r
|
||||
|
||||
|
||||
@router.get("/image/{image_id}", auth=None)
|
||||
def get_file(request, image_id: int):
|
||||
qs = get_object_or_404(File, id=image_id)
|
||||
|
||||
return HttpResponse(open(os.path.join(str(BASE_DIR), str(qs.url)), "rb"), content_type='image/png')
|
||||
172
code/system/apis/home.py
Normal file
172
code/system/apis/home.py
Normal file
@@ -0,0 +1,172 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# @Time : 2022/5/14 15:05
|
||||
# @Author : 臧成龙
|
||||
# @FileName: login.py
|
||||
# @Software: PyCharm
|
||||
import json
|
||||
from datetime import datetime
|
||||
# from django.core.cache import cache
|
||||
|
||||
from django.contrib import auth
|
||||
from django.forms import model_to_dict
|
||||
from django.shortcuts import get_object_or_404
|
||||
from django.db.models.expressions import RawSQL
|
||||
from ninja import Router, ModelSchema, Query, Schema, Field
|
||||
|
||||
from fuadmin.settings import RADIUS_NM, OWN_SHIP_MMSI, OWN_SHIP_NAME, OWN_SHIP_TYPE
|
||||
from system.models import VDESData, ShipLatestStatus, GpsStatus
|
||||
from utils.fu_jwt import FuJwt
|
||||
from utils.fu_response import FuResponse
|
||||
from utils.request_util import save_login_log
|
||||
from utils.vdes_curd import safe_bulk_upsert, get_nearby_ships
|
||||
|
||||
from random import uniform, randint
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
router = Router()
|
||||
|
||||
class SchemaOut(ModelSchema):
|
||||
class Config:
|
||||
model = VDESData
|
||||
model_exclude = ['id'] #排除字段
|
||||
|
||||
# @router.get("/home", response=SchemaOut)
|
||||
@router.get("/home")
|
||||
def get_vdesinfo(request):
|
||||
# 初始化4-5艘船的固定MMSI
|
||||
ship_mmsis = ['413323670', '413323671', '413323672', '413323673', '413323674', '423324323', '423343245']
|
||||
ship_data = []
|
||||
|
||||
# 检查是否是第一次调用,如果是则初始化位置
|
||||
if not hasattr(get_vdesinfo, 'ship_positions'):
|
||||
# 初始位置在(31.0320, 122.1240)周围30公里范围内
|
||||
base_lat = 31.0320
|
||||
base_lng = 122.2240
|
||||
delta = 0.18 # 约20公里
|
||||
|
||||
get_vdesinfo.ship_positions = []
|
||||
for i, mmsi in enumerate(ship_mmsis):
|
||||
# 初始位置随机分布在基准点周围
|
||||
initial_lat = round(base_lat + uniform(-delta, delta), 6)
|
||||
initial_lng = round(base_lng + uniform(-delta, delta), 6)
|
||||
initial_course = randint(0, 359)
|
||||
#固定本船初始位置
|
||||
if mmsi == '413323670':
|
||||
initial_lat = 30.9918
|
||||
initial_lng = 122.2788
|
||||
get_vdesinfo.ship_positions.append({
|
||||
'mmsi': mmsi,
|
||||
'lat': initial_lat,
|
||||
'lng': initial_lng,
|
||||
'course': initial_course,
|
||||
'speed': round(uniform(5, 15), 1) # 初始速度5-15节
|
||||
})
|
||||
|
||||
# 更新每艘船的位置
|
||||
for ship in get_vdesinfo.ship_positions:
|
||||
# 微调位置和航向
|
||||
ship['lat'] += (uniform(0, 1) + 0.3) * 0.001
|
||||
ship['lng'] += (uniform(0, 1) + 0.3) * 0.001
|
||||
ship['course'] = (ship['course'] + uniform(-5, 5)) % 360
|
||||
|
||||
# 创建模拟数据
|
||||
mock_data = {
|
||||
'mmsi': ship['mmsi'],
|
||||
'latitude': round(ship['lat'], 6),
|
||||
'longitude': round(ship['lng'], 6),
|
||||
'speed': ship['speed'],
|
||||
'course': ship['course'],
|
||||
'heading': ship['course'],
|
||||
'nav_status': 0,
|
||||
'timestamp': datetime.now(),
|
||||
'ship_name': f'船舶_{ship["mmsi"][-4:]}',
|
||||
'ship_type': randint(1, 99),
|
||||
'draught': round(uniform(5.0, 10.0), 1),
|
||||
'destination': '上海港',
|
||||
'eta': datetime.now() + timedelta(hours=randint(1, 24)),
|
||||
'is_ownship': ship['mmsi'] == '413323670' # 新增字段标识本船
|
||||
}
|
||||
ship_data.append(mock_data)
|
||||
# print(ship_data)
|
||||
# 批量创建并返回所有船的数据
|
||||
# VDESData.objects.bulk_create(ship_data)
|
||||
return ship_data # 返回所有船舶数据
|
||||
|
||||
def create_vdes_data_batch(count):
|
||||
print('开始模拟')
|
||||
data_list = []
|
||||
for _ in range(count):
|
||||
mock_data = {
|
||||
'mmsi': str(randint(336295939, 336295969)),
|
||||
'latitude': round(uniform(30.5, 31.0), 6),
|
||||
'longitude': round(uniform(122.0, 123.0), 6),
|
||||
'speed': round(uniform(0, 30), 1),
|
||||
'course': randint(0, 359),
|
||||
'heading': randint(0, 359),
|
||||
'nav_status': randint(0, 8),
|
||||
'timestamp': datetime.now(),
|
||||
'ship_name': f'船舶_{randint(1000, 9999)}',
|
||||
'ship_type': randint(1, 99),
|
||||
'draught': round(uniform(1.0, 15.0), 1),
|
||||
'destination': f'港口_{randint(1, 100)}',
|
||||
'eta': datetime.now() + timedelta(days=randint(1, 7)),
|
||||
}
|
||||
data_list.append(mock_data)
|
||||
return data_list
|
||||
# 批量创建
|
||||
ShipLatestStatus.objects.bulk_create(data_list)
|
||||
print('模拟结束')
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@router.get("/nearby")
|
||||
# def nearby_ships(request, lat: float, lon: float, radius_nm: float = 20.0):
|
||||
def nearby_ships(request):
|
||||
# ship_data_list = create_vdes_data_batch(5)
|
||||
# created, updated = safe_bulk_upsert(ship_data_list)
|
||||
# print(f"操作结果: 新增 {created} 条, 更新 {updated} 条")
|
||||
#查询最新经纬度数据
|
||||
latest_gps = GpsStatus.objects.order_by('-utc_time').first()
|
||||
lat = latest_gps.latitude
|
||||
lon = latest_gps.longitude
|
||||
radius_nm = RADIUS_NM
|
||||
ships = list(get_nearby_ships(lat, lon, radius_nm))
|
||||
ownship = ShipLatestStatus(
|
||||
mmsi=OWN_SHIP_MMSI,
|
||||
latitude=latest_gps.latitude,
|
||||
longitude=latest_gps.longitude,
|
||||
speed=latest_gps.speed,
|
||||
course=latest_gps.direction,
|
||||
heading=latest_gps.direction,
|
||||
nav_status=randint(0, 8),
|
||||
timestamp=datetime.now(),
|
||||
ship_name=OWN_SHIP_NAME,
|
||||
ship_type=OWN_SHIP_TYPE,
|
||||
draught=round(uniform(1.0, 15.0), 1),
|
||||
destination='上海港',
|
||||
eta=datetime.now() + timedelta(days=randint(1, 7))
|
||||
)
|
||||
ships.append(ownship)
|
||||
|
||||
result = []
|
||||
for ship in ships:
|
||||
result.append({
|
||||
'mmsi': ship.mmsi,
|
||||
'latitude': ship.latitude,
|
||||
'longitude': ship.longitude,
|
||||
'speed': ship.speed,
|
||||
'course': ship.course,
|
||||
'heading': ship.heading,
|
||||
'nav_status': 0,
|
||||
'timestamp': datetime.now(),
|
||||
'ship_name': ship.ship_name,
|
||||
'ship_type': ship.ship_type,
|
||||
'draught': ship.draught,
|
||||
'destination': ship.destination,
|
||||
'eta': ship.eta,
|
||||
'is_ownship': ship.mmsi == OWN_SHIP_MMSI
|
||||
})
|
||||
return result
|
||||
6
code/system/apis/log/__init__.py
Normal file
6
code/system/apis/log/__init__.py
Normal file
@@ -0,0 +1,6 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
# time: 2022/6/28 16:57
|
||||
# file: __init__.py.py
|
||||
# author: 臧成龙
|
||||
# QQ: 939589097
|
||||
43
code/system/apis/log/celery_log.py
Normal file
43
code/system/apis/log/celery_log.py
Normal file
@@ -0,0 +1,43 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# @Time : 2022/6/14 23:
|
||||
# @Author : 臧成龙
|
||||
# @FileName: celery_log.py
|
||||
# @Software: PyCharm
|
||||
from typing import List
|
||||
|
||||
from django_celery_results.models import TaskResult
|
||||
from ninja import Field, ModelSchema, Query, Router, Schema
|
||||
from ninja.pagination import paginate
|
||||
from utils.fu_crud import delete, retrieve
|
||||
from utils.fu_ninja import FuFilters, MyPagination
|
||||
|
||||
router = Router()
|
||||
|
||||
|
||||
class Filters(FuFilters):
|
||||
periodic_task_name: str = Field(None, alias="periodic_task_name")
|
||||
|
||||
|
||||
class SchemaOut(ModelSchema):
|
||||
class Config:
|
||||
model = TaskResult
|
||||
model_fields = "__all__"
|
||||
|
||||
|
||||
@router.delete("/celery_log/{celery_log_id}")
|
||||
def delete_celery_log(request, celery_log_id: int):
|
||||
delete(celery_log_id, TaskResult)
|
||||
return {"success": True}
|
||||
|
||||
|
||||
@router.get("/celery_log", response=List[SchemaOut])
|
||||
@paginate(MyPagination)
|
||||
def list_celery_log(request, filters: Filters = Query(...)):
|
||||
qs = retrieve(request, TaskResult, filters)
|
||||
return qs
|
||||
|
||||
|
||||
@router.get("/celery_log/all/list", response=List[SchemaOut])
|
||||
def all_list_role(request):
|
||||
qs = retrieve(request, TaskResult)
|
||||
return qs
|
||||
46
code/system/apis/log/login_log.py
Normal file
46
code/system/apis/log/login_log.py
Normal file
@@ -0,0 +1,46 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# @Time : 2022/6/07 00:56
|
||||
# @Author : 臧成龙
|
||||
# @FileName: login_log.py
|
||||
# @Software: PyCharm
|
||||
from typing import List
|
||||
|
||||
from django.shortcuts import get_object_or_404
|
||||
from ninja import Field, ModelSchema, Query, Router, Schema
|
||||
from ninja.pagination import paginate
|
||||
from system.models import LoginLog
|
||||
from utils.fu_crud import create, delete, retrieve, update
|
||||
from utils.fu_ninja import FuFilters, MyPagination
|
||||
|
||||
router = Router()
|
||||
|
||||
|
||||
class Filters(FuFilters):
|
||||
name: str = Field(None, alias="name")
|
||||
code: str = Field(None, alias="code")
|
||||
id: str = Field(None, alias="login_log_id")
|
||||
|
||||
|
||||
class SchemaOut(ModelSchema):
|
||||
class Config:
|
||||
model = LoginLog
|
||||
model_fields = "__all__"
|
||||
|
||||
|
||||
@router.delete("/login_log/{login_log_id}")
|
||||
def delete_login_log(request, login_log_id: int):
|
||||
delete(login_log_id, LoginLog)
|
||||
return {"success": True}
|
||||
|
||||
|
||||
@router.get("/login_log", response=List[SchemaOut])
|
||||
@paginate(MyPagination)
|
||||
def list_login_log(request, filters: Filters = Query(...)):
|
||||
qs = retrieve(request, LoginLog, filters)
|
||||
return qs
|
||||
|
||||
|
||||
@router.get("/login_log/all/list", response=List[SchemaOut])
|
||||
def all_list_role(request):
|
||||
qs = retrieve(request, LoginLog)
|
||||
return qs
|
||||
45
code/system/apis/log/operation_log.py
Normal file
45
code/system/apis/log/operation_log.py
Normal file
@@ -0,0 +1,45 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# @Time : 2022/6/07 00:56
|
||||
# @Author : 臧成龙
|
||||
# @FileName: operation_log.py
|
||||
# @Software: PyCharm
|
||||
from typing import List
|
||||
|
||||
from django.shortcuts import get_object_or_404
|
||||
from ninja import Field, ModelSchema, Query, Router, Schema
|
||||
from ninja.pagination import paginate
|
||||
from system.models import OperationLog
|
||||
from utils.fu_crud import create, delete, retrieve, update
|
||||
from utils.fu_ninja import FuFilters, MyPagination
|
||||
|
||||
router = Router()
|
||||
|
||||
|
||||
class Filters(FuFilters):
|
||||
request_username: str = Field(None, alias="request_username")
|
||||
id: str = Field(None, alias="operation_log_id")
|
||||
|
||||
|
||||
class SchemaOut(ModelSchema):
|
||||
class Config:
|
||||
model = OperationLog
|
||||
model_fields = "__all__"
|
||||
|
||||
|
||||
@router.delete("/operation_log/{operation_log_id}")
|
||||
def delete_operation_log(request, operation_log_id: int):
|
||||
delete(operation_log_id, OperationLog)
|
||||
return {"success": True}
|
||||
|
||||
|
||||
@router.get("/operation_log", response=List[SchemaOut])
|
||||
@paginate(MyPagination)
|
||||
def list_operation_log(request, filters: Filters = Query(...)):
|
||||
qs = retrieve(request, OperationLog, filters)
|
||||
return qs
|
||||
|
||||
|
||||
@router.get("/operation_log/all/list", response=List[SchemaOut])
|
||||
def all_list_role(request):
|
||||
qs = retrieve(request, OperationLog)
|
||||
return qs
|
||||
119
code/system/apis/login.py
Normal file
119
code/system/apis/login.py
Normal file
@@ -0,0 +1,119 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# @Time : 2022/5/14 15:05
|
||||
# @Author : 臧成龙
|
||||
# @FileName: login.py
|
||||
# @Software: PyCharm
|
||||
import json
|
||||
from datetime import datetime
|
||||
# from django.core.cache import cache
|
||||
|
||||
from django.contrib import auth
|
||||
from django.forms import model_to_dict
|
||||
from django.shortcuts import get_object_or_404
|
||||
from ninja import Router, ModelSchema, Query, Schema, Field
|
||||
|
||||
from fuadmin.settings import SECRET_KEY, TOKEN_LIFETIME
|
||||
from system.models import Users, Role, MenuButton, MenuColumnField
|
||||
from utils.fu_jwt import FuJwt
|
||||
from utils.fu_response import FuResponse
|
||||
from utils.request_util import save_login_log
|
||||
from utils.usual import get_user_info_from_token
|
||||
|
||||
router = Router()
|
||||
|
||||
|
||||
class SchemaOut(ModelSchema):
|
||||
homePath: str = Field(None, alias="home_path")
|
||||
|
||||
class Config:
|
||||
model = Users
|
||||
model_exclude = ['password', 'role', 'post']
|
||||
|
||||
|
||||
class LoginSchema(Schema):
|
||||
username: str = Field(None, alias="username")
|
||||
password: str = Field(None, alias="password")
|
||||
|
||||
|
||||
class Out(Schema):
|
||||
multi_depart: str
|
||||
sysAllDictItems: str
|
||||
departs: str
|
||||
userInfo: SchemaOut
|
||||
token: str
|
||||
|
||||
|
||||
@router.post("/login", response=Out, auth=None)
|
||||
def login(request, data: LoginSchema):
|
||||
user_obj = auth.authenticate(request, **data.dict())
|
||||
if user_obj:
|
||||
request.user = user_obj
|
||||
role = user_obj.role.all().values('id')
|
||||
post = list(user_obj.post.all().values('id'))
|
||||
role_list = []
|
||||
post_list = []
|
||||
for item in role:
|
||||
role_list.append(item['id'])
|
||||
for item in post:
|
||||
post_list.append(item['id'])
|
||||
user_obj_dic = model_to_dict(user_obj)
|
||||
user_obj_dic['post'] = post_list
|
||||
user_obj_dic['role'] = role_list
|
||||
del user_obj_dic['password']
|
||||
del user_obj_dic['avatar']
|
||||
|
||||
time_now = int(datetime.now().timestamp())
|
||||
jwt = FuJwt(SECRET_KEY, user_obj_dic, valid_to=time_now + TOKEN_LIFETIME)
|
||||
# 将生成的token加入缓存
|
||||
# cache.set(user_obj.id, jwt.encode())
|
||||
token = f"bearer {jwt.encode()}"
|
||||
data = {
|
||||
"multi_depart": 1,
|
||||
"sysAllDictItems": "q",
|
||||
"departs": "e",
|
||||
'userInfo': user_obj_dic,
|
||||
'token': token
|
||||
}
|
||||
save_login_log(request=request)
|
||||
return data
|
||||
else:
|
||||
return FuResponse(code=500, msg="账号/密码错误")
|
||||
|
||||
|
||||
@router.get("/logout", auth=None)
|
||||
def get_post(request):
|
||||
# 删除缓存
|
||||
user_info = get_user_info_from_token(request)
|
||||
# cache.delete(user_info['id'])
|
||||
return FuResponse(msg="注销成功")
|
||||
|
||||
|
||||
@router.get("/userinfo", response=SchemaOut)
|
||||
def get_userinfo(request):
|
||||
user_info = get_user_info_from_token(request)
|
||||
user = get_object_or_404(Users, id=user_info['id'])
|
||||
return user
|
||||
|
||||
|
||||
@router.get("/permCode")
|
||||
def route_menu_tree(request):
|
||||
"""用于前端获取当前用户的按钮权限"""
|
||||
token_user = get_user_info_from_token(request)
|
||||
user = Users.objects.get(id=token_user['id'])
|
||||
|
||||
if not token_user['is_superuser']:
|
||||
menu_button_ids = user.role.values_list('permission__id', flat=True)
|
||||
menu_column_ids = user.role.values_list('column__id', flat=True)
|
||||
|
||||
# queryset = MenuButton.objects.filter(id__in=menuIds, status=1).values()
|
||||
queryset_button = MenuButton.objects.filter(id__in=menu_button_ids)
|
||||
queryset_column = MenuColumnField.objects.filter(id__in=menu_column_ids)
|
||||
else:
|
||||
queryset_button = MenuButton.objects.all()
|
||||
queryset_column = MenuColumnField.objects.all()
|
||||
|
||||
queryset = [*queryset_button, *queryset_column]
|
||||
code_list = []
|
||||
for item in queryset:
|
||||
code_list.append(item.code)
|
||||
return FuResponse(data=code_list)
|
||||
91
code/system/apis/menu.py
Normal file
91
code/system/apis/menu.py
Normal file
@@ -0,0 +1,91 @@
|
||||
# Author 臧成龙
|
||||
# coding=utf-8
|
||||
# @Time : 2022/5/15 21:47
|
||||
# @File : menu.py
|
||||
# @Software: PyCharm
|
||||
# @qq: 939589097
|
||||
|
||||
from typing import List
|
||||
|
||||
from django.shortcuts import get_object_or_404
|
||||
from fuadmin.settings import SECRET_KEY
|
||||
from ninja import Field, ModelSchema, Query, Router, Schema
|
||||
from ninja.pagination import paginate
|
||||
from system.models import Menu, MenuButton, Users
|
||||
from utils.fu_crud import create, delete, retrieve, update
|
||||
from utils.fu_jwt import FuJwt
|
||||
from utils.fu_ninja import FuFilters, MyPagination
|
||||
from utils.fu_response import FuResponse
|
||||
from utils.list_to_tree import list_to_route, list_to_tree
|
||||
|
||||
router = Router()
|
||||
|
||||
|
||||
class Filters(FuFilters):
|
||||
title: str = Field(None, alias="title")
|
||||
status: bool = Field(None, alias="status")
|
||||
id: str = Field(None, alias="id")
|
||||
|
||||
|
||||
class SchemaIn(ModelSchema):
|
||||
parent_id: int = None
|
||||
component: str = 'LAYOUT'
|
||||
|
||||
class Config:
|
||||
model = Menu
|
||||
model_exclude = ['id', 'parent', 'create_datetime', 'update_datetime']
|
||||
|
||||
|
||||
class SchemaOut(ModelSchema):
|
||||
class Config:
|
||||
model = Menu
|
||||
model_fields = "__all__"
|
||||
# model_fields = []
|
||||
|
||||
|
||||
@router.post("/menu", response=SchemaOut)
|
||||
def create_menu(request, data: SchemaIn):
|
||||
menu = create(request, data, Menu)
|
||||
return menu
|
||||
|
||||
|
||||
@router.delete("/menu/{menu_id}")
|
||||
def delete_menu(request, menu_id: int):
|
||||
delete(menu_id, Menu)
|
||||
return {"success": True}
|
||||
|
||||
|
||||
@router.put("/menu/{menu_id}", response=SchemaOut)
|
||||
def update_menu(request, menu_id: int, data: SchemaIn):
|
||||
post = update(request, menu_id, data, Menu)
|
||||
return post
|
||||
|
||||
|
||||
@router.get("/menu", response=List[SchemaOut])
|
||||
def list_menu_tree(request, filters: Filters = Query(...)):
|
||||
qs = retrieve(request, Menu, filters).values()
|
||||
# 将查询集转换成树形结构
|
||||
menu_tree = list_to_tree(list(qs))
|
||||
return FuResponse(data=menu_tree)
|
||||
|
||||
|
||||
@router.get("/menu/{menu_id}", response=SchemaOut)
|
||||
def get_menu(request, menu_id: int):
|
||||
post = get_object_or_404(Menu, id=menu_id)
|
||||
return post
|
||||
|
||||
|
||||
@router.get("/menu/route/tree")
|
||||
def route_menu_tree(request):
|
||||
"""用于前端获取当前角色的路由"""
|
||||
token = request.META.get("HTTP_AUTHORIZATION")
|
||||
token = token.split(" ")[1]
|
||||
token_user = FuJwt(SECRET_KEY).decode(SECRET_KEY, token).payload
|
||||
user = Users.objects.get(id=token_user['id'])
|
||||
queryset = Menu.objects.filter(status=1).values()
|
||||
if not token_user['is_superuser']:
|
||||
menuIds = user.role.values_list('menu__id', flat=True)
|
||||
queryset = Menu.objects.filter(id__in=menuIds, status=1).values()
|
||||
menu_tree = list_to_route(list(queryset))
|
||||
return FuResponse(data=menu_tree)
|
||||
|
||||
69
code/system/apis/menu_button.py
Normal file
69
code/system/apis/menu_button.py
Normal file
@@ -0,0 +1,69 @@
|
||||
# Author 臧成龙
|
||||
# coding=utf-8
|
||||
# @Time : 2022/5/21 21:26
|
||||
# @File : menu_menu_button.py
|
||||
# @Software: PyCharm
|
||||
# @qq: 939589097
|
||||
|
||||
from typing import List
|
||||
|
||||
from django.shortcuts import get_object_or_404
|
||||
from ninja import Field, ModelSchema, Query, Router, Schema
|
||||
from ninja.pagination import paginate
|
||||
from system.models import MenuButton
|
||||
from utils.fu_crud import create, delete, retrieve, update
|
||||
from utils.fu_ninja import FuFilters, MyPagination
|
||||
|
||||
router = Router()
|
||||
|
||||
|
||||
class Filters(FuFilters):
|
||||
name: str = Field(None, alias="name")
|
||||
code: str = Field(None, alias="code")
|
||||
menu_id: str = Field(None, alias="menu_id")
|
||||
|
||||
|
||||
class SchemaIn(ModelSchema):
|
||||
menu_id: int = None
|
||||
|
||||
class Config:
|
||||
model = MenuButton
|
||||
model_exclude = ['id', 'menu', 'create_datetime', 'update_datetime']
|
||||
|
||||
|
||||
class SchemaOut(ModelSchema):
|
||||
class Config:
|
||||
model = MenuButton
|
||||
model_fields = "__all__"
|
||||
# model_fields = []
|
||||
|
||||
|
||||
@router.post("/menu_button", response=SchemaOut)
|
||||
def create_menu_button(request, data: SchemaIn):
|
||||
menu_button = create(request, data, MenuButton)
|
||||
return menu_button
|
||||
|
||||
|
||||
@router.delete("/menu_button/{menu_button_id}")
|
||||
def delete_menu_button(request, menu_button_id: int):
|
||||
delete(menu_button_id, MenuButton)
|
||||
return {"success": True}
|
||||
|
||||
|
||||
@router.put("/menu_button/{menu_button_id}", response=SchemaOut)
|
||||
def update_menu_button(request, menu_button_id: int, data: SchemaIn):
|
||||
menu_button = update(request, menu_button_id, data, MenuButton)
|
||||
return menu_button
|
||||
|
||||
|
||||
@router.get("/menu_button", response=List[SchemaOut])
|
||||
@paginate(MyPagination)
|
||||
def list_menu_button(request, filters: Filters = Query(...)):
|
||||
qs = retrieve(request, MenuButton, filters)
|
||||
return qs
|
||||
|
||||
|
||||
@router.get("/menu_button/{menu_button_id}", response=SchemaOut)
|
||||
def get_menu_button(request, menu_button_id: int):
|
||||
post = get_object_or_404(MenuButton, id=menu_button_id)
|
||||
return post
|
||||
80
code/system/apis/menu_column.py
Normal file
80
code/system/apis/menu_column.py
Normal file
@@ -0,0 +1,80 @@
|
||||
# Author 臧成龙
|
||||
# coding=utf-8
|
||||
# @Time : 2023/3/27 23:26
|
||||
# @File : menu_menu_column_field.py
|
||||
# @Software: PyCharm
|
||||
# @qq: 939589097
|
||||
|
||||
from typing import List
|
||||
|
||||
from django.shortcuts import get_object_or_404
|
||||
from ninja import Field, ModelSchema, Query, Router, Schema
|
||||
from ninja.pagination import paginate
|
||||
from system.models import MenuColumnField
|
||||
from utils.fu_crud import create, delete, retrieve, update, batch_create
|
||||
from utils.fu_ninja import FuFilters, MyPagination
|
||||
from utils.fu_response import FuResponse
|
||||
|
||||
router = Router()
|
||||
|
||||
|
||||
class Filters(FuFilters):
|
||||
name: str = Field(None, alias="name")
|
||||
code: str = Field(None, alias="code")
|
||||
menu_id: str = Field(None, alias="menu_id")
|
||||
|
||||
|
||||
class SchemaIn(ModelSchema):
|
||||
menu_id: str = Field(None, alias="menu_id")
|
||||
|
||||
class Config:
|
||||
model = MenuColumnField
|
||||
model_exclude = ['id', 'menu', 'create_datetime', 'update_datetime']
|
||||
|
||||
|
||||
class BatchSchemaIn(Schema):
|
||||
batch_info: list = Field(None, alias="batch_info")
|
||||
|
||||
|
||||
class SchemaOut(ModelSchema):
|
||||
class Config:
|
||||
model = MenuColumnField
|
||||
model_fields = "__all__"
|
||||
# model_fields = []
|
||||
|
||||
|
||||
@router.post("/menu_column_field", response=SchemaOut)
|
||||
def create_menu_column_field(request, data: SchemaIn):
|
||||
menu_column_field = create(request, data, MenuColumnField)
|
||||
return menu_column_field
|
||||
|
||||
|
||||
@router.post("/menu_column_field/batch/create")
|
||||
def batch_create_menu_column_field(request, data: BatchSchemaIn):
|
||||
batch_create(request, data.batch_info, MenuColumnField)
|
||||
return FuResponse(msg="导入成功")
|
||||
|
||||
|
||||
@router.delete("/menu_column_field/{menu_column_field_id}")
|
||||
def delete_menu_column_field(request, menu_column_field_id: int):
|
||||
delete(menu_column_field_id, MenuColumnField)
|
||||
return {"success": True}
|
||||
|
||||
|
||||
@router.put("/menu_column_field/{menu_column_field_id}", response=SchemaOut)
|
||||
def update_menu_column_field(request, menu_column_field_id: int, data: SchemaIn):
|
||||
menu_column_field = update(request, menu_column_field_id, data, MenuColumnField)
|
||||
return menu_column_field
|
||||
|
||||
|
||||
@router.get("/menu_column_field", response=List[SchemaOut])
|
||||
@paginate(MyPagination)
|
||||
def list_menu_column_field(request, filters: Filters = Query(...)):
|
||||
qs = retrieve(request, MenuColumnField, filters)
|
||||
return qs
|
||||
|
||||
|
||||
@router.get("/menu_column_field/{menu_column_field_id}", response=SchemaOut)
|
||||
def get_menu_column_field(request, menu_column_field_id: int):
|
||||
post = get_object_or_404(MenuColumnField, id=menu_column_field_id)
|
||||
return post
|
||||
54
code/system/apis/monitor.py
Normal file
54
code/system/apis/monitor.py
Normal file
@@ -0,0 +1,54 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
# time: 2022/7/26 14:12
|
||||
# file: monitor.py
|
||||
# author: 臧成龙
|
||||
# QQ: 939589097
|
||||
from ninja import Router
|
||||
from django.core.cache import cache
|
||||
from utils.fu_response import FuResponse
|
||||
from utils.system import system
|
||||
|
||||
router = Router()
|
||||
|
||||
|
||||
@router.get("/monitor")
|
||||
def list_role(request):
|
||||
data = cache.get('local_inspection_data')
|
||||
qs = system().GetSystemAllInfo()
|
||||
if data and isinstance(data, dict):
|
||||
qs['local_inspection_data'] = {
|
||||
'cpu0_usage': data.get('cpu0_usage'),
|
||||
'cpu1_usage': data.get('cpu1_usage')
|
||||
}
|
||||
else:
|
||||
# 如果data为空或不是字典类型,设置默认值
|
||||
qs['local_inspection_data'] = {
|
||||
'cpu0_usage': None,
|
||||
'cpu1_usage': None
|
||||
}
|
||||
# print("从系统获取监控数据:", qs)
|
||||
return FuResponse(data=qs)
|
||||
|
||||
|
||||
@router.get("/get_sbb")
|
||||
def list_role(request):
|
||||
sbb_data = {}
|
||||
|
||||
# 循环获取sbb1-sbb6的缓存数据
|
||||
for i in range(1, 7):
|
||||
key = f'sbb{i}'
|
||||
value = cache.get(key)
|
||||
print(f"sbb{i}缓存数据:", value)
|
||||
sbb_data[key] = value
|
||||
print("从缓存获取sbb数据:", sbb_data)
|
||||
# 模拟的sbb数据字典,包含sbb1-sbb6这六个字段
|
||||
# sample_sbb_data = {
|
||||
# "sbb1": {'type': 1, 'satellite_id': 2, 'primary_network_id': 0, 'roaming_network_id': 0, 'sbb_version': 152, 'start_time': 181303439, 'validity_period': 1440, 'itu_compatibility': 0, 'reserved_bits': 0, 'sbb_spare_frequency': 1226, 'uplink_max_length': 26, 'reserved': 0, 'total_message_length': 20},
|
||||
# "sbb2": {'type': 2, 'downlink_frequency_a': 2226, 'uplink_frequency_a': 1226, 'downlink_bandwidth_a': 1, 'uplink_bandwidth_a': 1, 'channel_slot_lengths_a': [6, 6, 2, 6, 6, 6, 6, 2, 2, 12, 0, 0], 'channel_functions_a': [0, 1, 4, 4, 4, 4, 4, 4, 2, 3, 5, 5], 'downlink_frequency_b': 1226, 'uplink_frequency_b': 2226, 'downlink_bandwidth_b': 1, 'uplink_bandwidth_b': 1, 'channel_slot_lengths_b': [6, 6, 2, 6, 6, 6, 6, 2, 2, 12, 0, 0], 'channel_functions_b': [0, 1, 4, 4, 4, 4, 4, 4, 2, 3, 5, 5]},
|
||||
# "sbb3": {'type': 3, 'downlink_frequency_c': 2284, 'uplink_frequency_c': 1225, 'downlink_bandwidth_c': 2, 'uplink_bandwidth_c': 1, 'channel_slot_lengths_c': [6, 6, 2, 6, 6, 6, 6, 2, 2, 12, 0, 0], 'channel_functions_c': [5, 5, 4, 4, 4, 4, 4, 4, 2, 3, 5, 5], 'downlink_frequency_d': 1284, 'uplink_frequency_d': 2225, 'downlink_bandwidth_d': 2, 'uplink_bandwidth_d': 1, 'channel_slot_lengths_d': [6, 6, 2, 6, 6, 6, 6, 2, 2, 12, 0, 0], 'channel_functions_d': [5, 5, 4, 4, 4, 4, 4, 4, 2, 3, 5, 5]},
|
||||
# "sbb4": {'type': 4, 'downlink_frequency_e': 2225, 'uplink_frequency_e': 1224, 'downlink_bandwidth_e': 3, 'uplink_bandwidth_e': 1, 'channel_slot_lengths_e': [6, 6, 2, 6, 6, 6, 6, 2, 2, 12, 0, 0], 'channel_functions_e': [5, 5, 4, 4, 4, 4, 4, 4, 2, 3, 5, 5], 'downlink_frequency_f': 1225, 'uplink_frequency_f': 2224, 'downlink_bandwidth_f': 3, 'uplink_bandwidth_f': 1, 'channel_slot_lengths_f': [6, 6, 2, 6, 6, 6, 6, 2, 2, 12, 0, 0], 'channel_functions_f': [5, 5, 4, 4, 4, 4, 4, 4, 2, 3, 5, 5]},
|
||||
# "sbb5": {'type': 5, 'digital_signature_part1': '00000000400050033a529c960c39c0a44afbcc752414aa14b540233ada55b61d'},
|
||||
# "sbb6": {'type': 5, 'digital_signature_part1': '00000000400050033a529c960c39c0a44afbcc752414aa14b540233ada55b61d'}
|
||||
# }
|
||||
return sbb_data
|
||||
95
code/system/apis/post.py
Normal file
95
code/system/apis/post.py
Normal file
@@ -0,0 +1,95 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# @Time : 2022/5/10 21:56
|
||||
# @Author : 臧成龙
|
||||
# @FileName: post.py
|
||||
# @Software: PyCharm
|
||||
|
||||
from typing import List
|
||||
|
||||
from django.shortcuts import get_object_or_404
|
||||
from ninja import Field, ModelSchema, Query, Router, Schema, UploadedFile
|
||||
from ninja.pagination import paginate
|
||||
from system.models import Post
|
||||
from utils.fu_crud import (
|
||||
ImportSchema,
|
||||
create,
|
||||
delete,
|
||||
export_data,
|
||||
import_data,
|
||||
retrieve,
|
||||
update,
|
||||
)
|
||||
from utils.fu_ninja import FuFilters, MyPagination
|
||||
|
||||
router = Router()
|
||||
|
||||
|
||||
class Filters(FuFilters):
|
||||
name: str = Field(None, alias="name")
|
||||
code: str = Field(None, alias="code")
|
||||
status: int = Field(None, alias="status")
|
||||
|
||||
id: str = Field(None, alias="post_id")
|
||||
|
||||
|
||||
class PostSchemaIn(ModelSchema):
|
||||
class Config:
|
||||
model = Post
|
||||
model_fields = ['name', 'code', 'sort', 'status']
|
||||
|
||||
|
||||
class PostSchemaOut(ModelSchema):
|
||||
creator: str = Field(None, alias="creator.username")
|
||||
|
||||
class Config:
|
||||
model = Post
|
||||
model_fields = "__all__"
|
||||
|
||||
|
||||
@router.post("/post", response=PostSchemaOut)
|
||||
def create_post(request, data: PostSchemaIn):
|
||||
post = create(request, data, Post)
|
||||
return post
|
||||
|
||||
|
||||
@router.delete("/post/{post_id}")
|
||||
def delete_post(request, post_id: int):
|
||||
delete(post_id, Post)
|
||||
return {"success": True}
|
||||
|
||||
|
||||
@router.put("/post/{post_id}", response=PostSchemaOut)
|
||||
def update_post(request, post_id: int, data: PostSchemaIn):
|
||||
post = update(request, post_id, data, Post)
|
||||
return post
|
||||
|
||||
|
||||
@router.get("/post", response=List[PostSchemaOut])
|
||||
@paginate(MyPagination)
|
||||
def list_post(request, filters: Filters = Query(...)):
|
||||
qs = retrieve(request, Post, filters)
|
||||
return qs
|
||||
|
||||
|
||||
@router.get("/post/{post_id}", response=PostSchemaOut)
|
||||
def get_post(request, post_id: int):
|
||||
post = get_object_or_404(Post, id=post_id)
|
||||
return post
|
||||
|
||||
|
||||
@router.get("/post/all/list", response=List[PostSchemaOut])
|
||||
def all_list_post(request):
|
||||
qs = retrieve(request, Post)
|
||||
return qs
|
||||
|
||||
|
||||
@router.get("/post/all/export")
|
||||
def export_post(request):
|
||||
export_fields = ['name', 'code', 'status', 'sort', 'creator']
|
||||
return export_data(request, Post, PostSchemaOut, export_fields)
|
||||
|
||||
|
||||
@router.post("/post/all/import")
|
||||
def import_post(request, data: ImportSchema):
|
||||
import_fields = ['name', 'code', 'status', 'sort']
|
||||
return import_data(request, Post, PostSchemaIn, data, import_fields)
|
||||
191
code/system/apis/role.py
Normal file
191
code/system/apis/role.py
Normal file
@@ -0,0 +1,191 @@
|
||||
# from application.ninja_cof import api
|
||||
# Author 臧成龙
|
||||
# coding=utf-8
|
||||
# @Time : 2022/5/19 21:36
|
||||
# @File : role.py
|
||||
# @Software: PyCharm
|
||||
# @qq: 939589097
|
||||
|
||||
from typing import List
|
||||
|
||||
from django.shortcuts import get_object_or_404
|
||||
from ninja import Field, ModelSchema, Query, Router, Schema
|
||||
from ninja.pagination import paginate
|
||||
from system.models import Menu, Role, MenuButton
|
||||
from utils.fu_crud import create, delete, retrieve
|
||||
from utils.fu_ninja import FuFilters, MyPagination
|
||||
from utils.fu_response import FuResponse
|
||||
from utils.list_to_tree import list_to_tree
|
||||
|
||||
router = Router()
|
||||
|
||||
|
||||
class Filters(FuFilters):
|
||||
name: str = Field(None, alias="name")
|
||||
status: bool = Field(None, alias="status")
|
||||
id: str = Field(None, alias="id")
|
||||
|
||||
|
||||
class SchemaIn(ModelSchema):
|
||||
menu: list = []
|
||||
permission: list = []
|
||||
dept: list = []
|
||||
column: list = []
|
||||
|
||||
class Config:
|
||||
model = Role
|
||||
model_exclude = ['id', 'dept', 'menu', 'permission', 'column', 'create_datetime', 'update_datetime']
|
||||
|
||||
|
||||
class SchemaOut(ModelSchema):
|
||||
class Config:
|
||||
model = Role
|
||||
model_fields = "__all__"
|
||||
# model_fields = []
|
||||
|
||||
|
||||
@router.post("/role", response=SchemaOut)
|
||||
def create_role(request, data: SchemaIn):
|
||||
dict_data = data.dict()
|
||||
del dict_data['menu']
|
||||
del dict_data['permission']
|
||||
del dict_data['dept']
|
||||
del dict_data['column']
|
||||
|
||||
role = create(request, dict_data, Role)
|
||||
return role
|
||||
|
||||
|
||||
@router.delete("/role/{role_id}")
|
||||
def delete_role(request, role_id: int):
|
||||
delete(role_id, Role)
|
||||
return {"success": True}
|
||||
|
||||
|
||||
@router.put("/role/{role_id}", response=SchemaOut)
|
||||
def update_role(request, role_id: int, payload: SchemaIn):
|
||||
post = get_object_or_404(Role, id=role_id)
|
||||
for attr, value in payload.dict().items():
|
||||
if attr == 'menu':
|
||||
post.menu.set(value)
|
||||
elif attr == 'permission':
|
||||
post.permission.set(value)
|
||||
elif attr == 'dept':
|
||||
post.dept.set(value)
|
||||
elif attr == 'column':
|
||||
post.column.set(value)
|
||||
else:
|
||||
setattr(post, attr, value)
|
||||
post.save()
|
||||
return post
|
||||
|
||||
|
||||
@router.get("/role", response=List[SchemaOut])
|
||||
@paginate(MyPagination)
|
||||
def list_role(request, filters: Filters = Query(...)):
|
||||
qs = retrieve(request, Role, filters)
|
||||
return qs
|
||||
|
||||
|
||||
@router.get("/role/all/list", response=List[SchemaOut])
|
||||
def all_list_role(request):
|
||||
qs = retrieve(request, Role)
|
||||
return qs
|
||||
|
||||
|
||||
@router.get("/role/{role_id}", response=SchemaOut)
|
||||
def get_role(request, role_id: int):
|
||||
role = get_object_or_404(Role, id=role_id)
|
||||
return role
|
||||
|
||||
|
||||
class ButtonColumnFilters(FuFilters):
|
||||
menu_ids: list = Field(None, alias="menu_ids")
|
||||
|
||||
|
||||
@router.get("/role/list/menu_button")
|
||||
def list_menu_button_tree(request, filters: ButtonColumnFilters = Query(...)):
|
||||
qs = Menu.objects.filter()
|
||||
result = []
|
||||
menu_button_list = []
|
||||
|
||||
for item in qs:
|
||||
dict_item = item.__dict__
|
||||
menu_button = list(item.menuPermission.all().values())
|
||||
|
||||
for button_item in menu_button:
|
||||
button_item['id'] = f"b{button_item['id']}"
|
||||
button_item['parent_id'] = button_item.pop('menu_id')
|
||||
button_item['title'] = button_item.pop('name')
|
||||
|
||||
# dict_item['menu_button'] = list(item.menuPermission.all().values())
|
||||
del dict_item['_state']
|
||||
result.extend(menu_button)
|
||||
result.append(dict_item)
|
||||
return get_button_or_column_menu(result, 'b')
|
||||
|
||||
|
||||
@router.get("/role/list/menu_column")
|
||||
def list_menu_button_tree(request, filters: ButtonColumnFilters = Query(...)):
|
||||
qs = Menu.objects.all()
|
||||
result = []
|
||||
for item in qs:
|
||||
dict_item = item.__dict__
|
||||
column_field = list(item.menuColumnField.all().values())
|
||||
for column_field_item in column_field:
|
||||
column_field_item['id'] = f"c{column_field_item['id']}"
|
||||
column_field_item['parent_id'] = column_field_item.pop('menu_id')
|
||||
column_field_item['title'] = column_field_item.pop('name')
|
||||
|
||||
del dict_item['_state']
|
||||
result.append(dict_item)
|
||||
result.extend(column_field)
|
||||
|
||||
return get_button_or_column_menu(result, 'c')
|
||||
|
||||
|
||||
class SchemaMenuOut(ModelSchema):
|
||||
class Config:
|
||||
model = Menu
|
||||
model_fields = "__all__"
|
||||
|
||||
|
||||
@router.get("/role/list/menu", response=List[SchemaMenuOut])
|
||||
def list_menu_tree(request, filters: Filters = Query(...)):
|
||||
qs = retrieve(request, Menu, filters).values()
|
||||
# 将查询集转换成树形结构
|
||||
menu_tree = list_to_tree(list(qs))
|
||||
return FuResponse(data=menu_tree)
|
||||
|
||||
|
||||
# class SchemaButtonOut(ModelSchema):
|
||||
# class Config:
|
||||
# model = MenuButton
|
||||
# model_fields = "__all__"
|
||||
#
|
||||
#
|
||||
# @router.get("/role/list/button/{menu_id}", response=List[SchemaButtonOut])
|
||||
# def get_button_by_menu_id(request, menu_id: int):
|
||||
# filters: Filters
|
||||
# qs = MenuButton.objects.filter(menu_id=menu_id)
|
||||
#
|
||||
# return qs
|
||||
|
||||
|
||||
def get_button_or_column_menu(data, flag):
|
||||
return_data = []
|
||||
for i in data:
|
||||
m_id = i['id']
|
||||
if flag in str(m_id):
|
||||
return_data.append(i)
|
||||
get_menu_by_parent(i['parent_id'], data, return_data)
|
||||
return return_data
|
||||
|
||||
|
||||
def get_menu_by_parent(parent_id, data, return_data):
|
||||
for i in data:
|
||||
if parent_id == i['id'] and i not in return_data:
|
||||
return_data.append(i)
|
||||
get_menu_by_parent(i['parent_id'], data, return_data)
|
||||
if parent_id is None:
|
||||
return
|
||||
125
code/system/apis/user.py
Normal file
125
code/system/apis/user.py
Normal file
@@ -0,0 +1,125 @@
|
||||
# from application.ninja_cof import api
|
||||
# Author 臧成龙
|
||||
# coding=utf-8
|
||||
# @Time : 2022/5/29 01:36
|
||||
# @File : user.py
|
||||
# @Software: PyCharm
|
||||
# @qq: 939589097
|
||||
|
||||
from typing import List
|
||||
|
||||
from django.contrib.auth.hashers import make_password
|
||||
from django.shortcuts import get_object_or_404
|
||||
from ninja import Field, ModelSchema, Query, Router, Schema
|
||||
from ninja.pagination import paginate
|
||||
from system.models import Users
|
||||
from utils.fu_crud import create, delete, retrieve
|
||||
from utils.fu_ninja import FuFilters, MyPagination
|
||||
from utils.fu_response import FuResponse
|
||||
from utils.usual import get_user_info_from_token
|
||||
|
||||
router = Router()
|
||||
|
||||
|
||||
class Filters(FuFilters):
|
||||
name: str = Field(None, alias="name")
|
||||
mobile: str = Field(None, alias="mobile")
|
||||
status: bool = Field(None, alias="status")
|
||||
dept_id__in: list = Field(None, alias="dept_ids[]")
|
||||
id: int = Field(None, alias="id")
|
||||
|
||||
|
||||
class SchemaIn(ModelSchema):
|
||||
dept_id: int = Field(None, alias="dept")
|
||||
post: list = []
|
||||
role: list = []
|
||||
|
||||
class Config:
|
||||
model = Users
|
||||
model_exclude = ['id', 'groups', 'user_permissions', 'is_superuser', 'dept', 'post', 'role', 'password',
|
||||
'create_datetime', 'update_datetime']
|
||||
|
||||
|
||||
class SchemaOut(ModelSchema):
|
||||
class Config:
|
||||
model = Users
|
||||
model_exclude = ['password']
|
||||
|
||||
|
||||
@router.post("/user", response=SchemaOut)
|
||||
def create_user(request, data: SchemaIn):
|
||||
data_dic = data.dict()
|
||||
data_dic['password'] = make_password('123456')
|
||||
post = data_dic.pop('post')
|
||||
role = data_dic.pop('role')
|
||||
user = create(request, data_dic, Users)
|
||||
# 为多对多关系添加数据
|
||||
user.post.set(post)
|
||||
user.role.set(role)
|
||||
return user
|
||||
|
||||
|
||||
@router.delete("/user/{user_id}")
|
||||
def delete_user(request, user_id: int):
|
||||
delete(user_id, Users)
|
||||
return {"success": True}
|
||||
|
||||
|
||||
@router.put("/user/{user_id}", response=SchemaOut)
|
||||
def update_user(request, user_id: int, payload: SchemaIn):
|
||||
user = get_object_or_404(Users, id=user_id)
|
||||
for attr, value in payload.dict().items():
|
||||
if attr == 'role':
|
||||
user.role.set(value)
|
||||
elif attr == 'post':
|
||||
user.post.set(value)
|
||||
else:
|
||||
setattr(user, attr, value)
|
||||
user.save()
|
||||
return user
|
||||
|
||||
|
||||
@router.get("/user", response=List[SchemaOut])
|
||||
@paginate(MyPagination)
|
||||
def list_user(request, filters: Filters = Query(...)):
|
||||
qs = retrieve(request, Users, filters)
|
||||
return qs
|
||||
|
||||
|
||||
@router.get("/user/all/list", response=List[SchemaOut])
|
||||
def all_list_user(request):
|
||||
qs = retrieve(request, Users)
|
||||
return qs
|
||||
|
||||
|
||||
@router.get("/user/{user_id}", response=SchemaOut)
|
||||
def get_user(request, user_id: int):
|
||||
user = get_object_or_404(Users, id=user_id)
|
||||
return user
|
||||
|
||||
|
||||
class SchemaIn(Schema):
|
||||
id: int
|
||||
password: str
|
||||
|
||||
|
||||
@router.post("/user/set/repassword", response=SchemaOut)
|
||||
def repassword(request, data: SchemaIn):
|
||||
request_user = get_user_info_from_token(request)
|
||||
request_user_id = request_user['id']
|
||||
update_id = data.id
|
||||
if request_user_id == update_id:
|
||||
user = get_object_or_404(Users, id=update_id)
|
||||
user.set_password(data.password)
|
||||
user.save()
|
||||
return FuResponse(msg='密码修改成功')
|
||||
else:
|
||||
return FuResponse(code=403, msg='对不起, 只能修改自己的密码')
|
||||
|
||||
|
||||
@router.put("/user/reset/password/{id}", response=SchemaOut)
|
||||
def reset_password(request, id:int):
|
||||
user = get_object_or_404(Users, id=id)
|
||||
user.set_password('123456')
|
||||
user.save()
|
||||
return FuResponse(msg='密码重置成功')
|
||||
5
code/system/apis/vdes/__init__.py
Normal file
5
code/system/apis/vdes/__init__.py
Normal file
@@ -0,0 +1,5 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# @Time : 2022/5/9 23:26
|
||||
# @Author : 臧成龙
|
||||
# @FileName: __init__.py.py
|
||||
# @Software: PyCharm
|
||||
5
code/system/apis/vdes/chat/__init__.py
Normal file
5
code/system/apis/vdes/chat/__init__.py
Normal file
@@ -0,0 +1,5 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# @Time : 2022/5/9 23:26
|
||||
# @Author : 臧成龙
|
||||
# @FileName: __init__.py.py
|
||||
# @Software: PyCharm
|
||||
25
code/system/apis/vdes/chat/common.py
Normal file
25
code/system/apis/vdes/chat/common.py
Normal file
@@ -0,0 +1,25 @@
|
||||
from ninja import Router
|
||||
from ninja import Router, UploadedFile, File
|
||||
import os
|
||||
from fuadmin import settings
|
||||
|
||||
router = Router()
|
||||
|
||||
# @router.get("/uploadFile")
|
||||
# def upload_file(request, files: list[UploadedFile] = File(...)):
|
||||
# file_list = []
|
||||
# upload_dir = os.path.join(settings.BASE_DIR, 'upload')
|
||||
# os.makedirs(upload_dir, exist_ok=True)
|
||||
|
||||
# for file in files:
|
||||
# new_path = os.path.join(upload_dir, file.name)
|
||||
# with open(new_path, 'wb+') as destination:
|
||||
# for chunk in file.chunks():
|
||||
# destination.write(chunk)
|
||||
# file_list.append({
|
||||
# 'name': file.name,
|
||||
# 'path': new_path,
|
||||
# 'size': file.size
|
||||
# })
|
||||
|
||||
# return file_list
|
||||
102
code/system/apis/vdes/chat/contacts.py
Normal file
102
code/system/apis/vdes/chat/contacts.py
Normal file
@@ -0,0 +1,102 @@
|
||||
from system.models import Contacts, HistorySession
|
||||
from ninja import Router, Schema, Field
|
||||
|
||||
router = Router()
|
||||
|
||||
class addContactSchema(Schema):
|
||||
MMSI: str = Field(None, alias="mmsi")
|
||||
name: str = Field(None, alias="name")
|
||||
phone: str = Field(None, alias="phone")
|
||||
region: str = Field(None, alias="region")
|
||||
|
||||
class deleteContactSchema(Schema):
|
||||
MMSI: str = Field(None, alias="mmsi")
|
||||
|
||||
class editContactSchema(Schema):
|
||||
oldMMSI: str = Field(None, alias="old_mmsi")
|
||||
newMMSI: str = Field(None, alias="new_mmsi")
|
||||
name: str = Field(None, alias="name")
|
||||
phone: str = Field(None, alias="phone")
|
||||
region: str = Field(None, alias="region")
|
||||
|
||||
|
||||
@router.get('/contactsList')
|
||||
def get_contacts_list(request):
|
||||
try:
|
||||
contacts = Contacts.objects.all().values()
|
||||
return {
|
||||
"code": 200,
|
||||
"message": "成功",
|
||||
"data": list(contacts)
|
||||
}
|
||||
except Exception as e:
|
||||
return {'code': 500, 'message': str(e)}
|
||||
|
||||
@router.post('/deleteContact')
|
||||
def delete_contact(request, data: deleteContactSchema):
|
||||
try:
|
||||
if not data.MMSI:
|
||||
return {'code': 400, 'message': 'MMSI不能为空'}
|
||||
contact = Contacts.objects.filter(MMSI=data.MMSI).first()
|
||||
if not contact:
|
||||
return {'code': 404, 'message': '未找到对应联系人'}
|
||||
# 删除关联的历史会话记录
|
||||
HistorySession.objects.filter(FormId=contact.Id).delete()
|
||||
contact.delete()
|
||||
return {'code': 200, 'message': '删除成功'}
|
||||
except Exception as e:
|
||||
return {'code': 500, 'message': str(e)}
|
||||
|
||||
|
||||
@router.post('/editContact')
|
||||
def edit_contact(request, data: editContactSchema):
|
||||
try:
|
||||
if not data.oldMMSI:
|
||||
return {'code': 400, 'message': '原MMSI不能为空'}
|
||||
|
||||
contact = Contacts.objects.filter(MMSI=data.oldMMSI).first()
|
||||
if not contact:
|
||||
return {'code': 404, 'message': '未找到对应联系人'}
|
||||
# 更新联系人信息
|
||||
contact.MMSI = data.newMMSI if data.newMMSI else contact.MMSI
|
||||
contact.Name = data.name if data.name else contact.Name
|
||||
contact.Mobile = data.phone if data.phone else contact.Mobile
|
||||
contact.Region = data.region if data.region else contact.Region
|
||||
contact.save()
|
||||
|
||||
# 更新关联的历史会话记录
|
||||
if data.newMMSI or data.name:
|
||||
HistorySession.objects.filter(FormId=contact.Id).update(
|
||||
MMSI=data.newMMSI if data.newMMSI else contact.MMSI,
|
||||
Name=data.name if data.name else contact.Name
|
||||
)
|
||||
|
||||
return {
|
||||
'code': 200,
|
||||
'message': '修改成功',
|
||||
'data': {
|
||||
'id': contact.Id,
|
||||
'MMSI': contact.MMSI
|
||||
}
|
||||
}
|
||||
except Exception as e:
|
||||
return {'code': 500, 'message': str(e)}
|
||||
|
||||
@router.post('/addContact')
|
||||
def add_contact(request, data: addContactSchema):
|
||||
try:
|
||||
if not data.MMSI:
|
||||
return {'code': 400, 'message': 'MMSI不能为空'}
|
||||
name = data.name if data.name else data.MMSI
|
||||
contact = Contacts.objects.create(
|
||||
MMSI=data.MMSI,
|
||||
Name=name,
|
||||
Mobile=data.phone,
|
||||
Region=data.region
|
||||
)
|
||||
return {
|
||||
'code': 200,
|
||||
'message': '成功',
|
||||
}
|
||||
except Exception as e:
|
||||
return {'code': 500, 'message': str(e)}
|
||||
53
code/system/apis/vdes/chat/content.py
Normal file
53
code/system/apis/vdes/chat/content.py
Normal file
@@ -0,0 +1,53 @@
|
||||
from system.models import Content
|
||||
from ninja import Router, Schema, Field
|
||||
from datetime import datetime
|
||||
from vdes_utils.vdes_message_packaged import VdesMessagePackaged
|
||||
|
||||
router = Router()
|
||||
|
||||
@router.get('/getContent')
|
||||
def get_content(request):
|
||||
try:
|
||||
contacts = Content.objects.all().order_by('Id').values()
|
||||
return {
|
||||
"code": 200,
|
||||
"message": "成功",
|
||||
"data": list(contacts)
|
||||
}
|
||||
except Exception as e:
|
||||
return {'code': 500, 'message': str(e)}
|
||||
|
||||
@router.post('/sendMessage')
|
||||
def send_message(request):
|
||||
# try:
|
||||
# 解析JSON数据
|
||||
import json
|
||||
data = json.loads(request.body)
|
||||
MMSI = data['mmsi']
|
||||
# 创建并保存Content记录
|
||||
content = Content.objects.create(
|
||||
SendId=data['SendId'],
|
||||
ReciverId=data['ReciverId'],
|
||||
Content=data['Content'],
|
||||
Type=data['Type'],
|
||||
# State=data['State'],
|
||||
State=1, # 设置为已发送状态
|
||||
NoCode=data['NoCode'],
|
||||
CreateDateUtc=datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
|
||||
Title=data['Title'],
|
||||
Description=data['Description'],
|
||||
Label=data['Label'],
|
||||
Thumbnail=data['Thumbnail'],
|
||||
ReadFlag=data['ReadFlag'],
|
||||
Avatar=data['Avatar'],
|
||||
SoundStatus=0,
|
||||
|
||||
)
|
||||
message = data['Content'].replace('<p>', '').replace('</p>', '')
|
||||
# message = "这是一条长消息测试消息1111111111111111111111111111111111111111111111111111111"
|
||||
vdes_message_packaged = VdesMessagePackaged(message, MMSI)
|
||||
vdes_message_packaged.send_message()
|
||||
|
||||
return { "code": 200, "message": "消息保存成功" }
|
||||
# except Exception as e:
|
||||
# return {'code': 500, 'message': str(e)}
|
||||
50
code/system/apis/vdes/chat/historySession.py
Normal file
50
code/system/apis/vdes/chat/historySession.py
Normal file
@@ -0,0 +1,50 @@
|
||||
from system.models import HistorySession
|
||||
from ninja import Router, Schema, Field
|
||||
|
||||
router = Router()
|
||||
|
||||
|
||||
class HistorySessionSchema(Schema):
|
||||
FormId: str = Field(None, alias="formId")
|
||||
Name: str = Field(None, alias="name")
|
||||
MMSI: str = Field(None, alias="mmsi")
|
||||
Mobile: str = Field(None, alias="mobile")
|
||||
Region: str = Field(None, alias="region")
|
||||
ById: str = Field(None, alias="byId")
|
||||
|
||||
|
||||
@router.get('/getHistorySession')
|
||||
def get_history_session(request):
|
||||
try:
|
||||
contacts = HistorySession.objects.all().values()
|
||||
return {
|
||||
"code": 200,
|
||||
"message": "成功",
|
||||
"data": list(contacts)
|
||||
}
|
||||
except Exception as e:
|
||||
return {'code': 500, 'message': str(e)}
|
||||
|
||||
|
||||
@router.post('/insertHistorySession')
|
||||
def insert_history_session(request, data: HistorySessionSchema):
|
||||
try:
|
||||
existing_record = HistorySession.objects.filter(
|
||||
FormId=data.FormId,
|
||||
ById=data.ById
|
||||
).first()
|
||||
if not existing_record:
|
||||
history_session = HistorySession.objects.create(
|
||||
FormId=data.FormId,
|
||||
Name=data.Name,
|
||||
MMSI=data.MMSI,
|
||||
Mobile=data.Mobile,
|
||||
Region=data.Region,
|
||||
ById=data.ById
|
||||
)
|
||||
return {
|
||||
"code": 200,
|
||||
"message": "成功",
|
||||
}
|
||||
except Exception as e:
|
||||
return {'code': 500, 'message': str(e)}
|
||||
50
code/system/apis/vdes/chat/historysession_bk.py
Normal file
50
code/system/apis/vdes/chat/historysession_bk.py
Normal file
@@ -0,0 +1,50 @@
|
||||
# from system.models import HistorySession
|
||||
# from ninja import Router, Schema, Field
|
||||
|
||||
# router = Router()
|
||||
|
||||
|
||||
# class HistorySessionSchema(Schema):
|
||||
# FormId: str = Field(None, alias="formId")
|
||||
# Name: str = Field(None, alias="name")
|
||||
# MMSI: str = Field(None, alias="mmsi")
|
||||
# Mobile: str = Field(None, alias="mobile")
|
||||
# Region: str = Field(None, alias="region")
|
||||
# ById: str = Field(None, alias="byId")
|
||||
|
||||
|
||||
# @router.get('/getHistorySession')
|
||||
# def get_history_session(request):
|
||||
# try:
|
||||
# contacts = HistorySession.objects.all().values()
|
||||
# return {
|
||||
# "code": 200,
|
||||
# "message": "成功",
|
||||
# "data": list(contacts)
|
||||
# }
|
||||
# except Exception as e:
|
||||
# return {'code': 500, 'message': str(e)}
|
||||
|
||||
|
||||
# @router.post('/insertHistorySession')
|
||||
# def insert_history_session(request, data: HistorySessionSchema):
|
||||
# try:
|
||||
# existing_record = HistorySession.objects.filter(
|
||||
# FormId=data.FormId,
|
||||
# ById=data.ById
|
||||
# ).first()
|
||||
# if not existing_record:
|
||||
# history_session = HistorySession.objects.create(
|
||||
# FormId=data.FormId,
|
||||
# Name=data.Name,
|
||||
# MMSI=data.MMSI,
|
||||
# Mobile=data.Mobile,
|
||||
# Region=data.Region,
|
||||
# ById=data.ById
|
||||
# )
|
||||
# return {
|
||||
# "code": 200,
|
||||
# "message": "成功",
|
||||
# }
|
||||
# except Exception as e:
|
||||
# return {'code': 500, 'message': str(e)}
|
||||
151
code/system/apis/vdes/header.py
Normal file
151
code/system/apis/vdes/header.py
Normal file
@@ -0,0 +1,151 @@
|
||||
import random
|
||||
import math
|
||||
from ninja import Router
|
||||
|
||||
from django.utils import timezone
|
||||
from datetime import timedelta
|
||||
from system.models import GpsStatus, ShipLatestStatus, MessageFlag
|
||||
from fuadmin.settings import OWN_SHIP_MMSI
|
||||
|
||||
|
||||
router = Router()
|
||||
|
||||
def haversine(lat1, lon1, lat2, lon2):
|
||||
"""计算两点间的大圆距离(公里)"""
|
||||
R = 6371 # 地球半径(公里)
|
||||
|
||||
# 将角度转换为弧度
|
||||
lat1, lon1, lat2, lon2 = map(math.radians, [lat1, lon1, lat2, lon2])
|
||||
|
||||
# 差值
|
||||
dlat = lat2 - lat1
|
||||
dlon = lon2 - lon1
|
||||
|
||||
# Haversine公式
|
||||
a = math.sin(dlat/2)**2 + math.cos(lat1) * math.cos(lat2) * math.sin(dlon/2)**2
|
||||
c = 2 * math.atan2(math.sqrt(a), math.sqrt(1-a))
|
||||
|
||||
return R * c
|
||||
|
||||
|
||||
def calculate_relative_bearing(own_lat, own_lon, own_course, target_lat, target_lon):
|
||||
"""
|
||||
计算目标点相对本船航向的方向角(相对方位,单位°,0~360,顺时针)
|
||||
"""
|
||||
d_lon = math.radians(target_lon - own_lon)
|
||||
lat1 = math.radians(own_lat)
|
||||
lat2 = math.radians(target_lat)
|
||||
|
||||
x = math.sin(d_lon) * math.cos(lat2)
|
||||
y = math.cos(lat1)*math.sin(lat2) - math.sin(lat1)*math.cos(lat2)*math.cos(d_lon)
|
||||
initial_bearing = math.atan2(x, y)
|
||||
initial_bearing_deg = (math.degrees(initial_bearing) + 360) % 360 # 转换为 0~360 度
|
||||
|
||||
# 相对航向(目标相对于本船正前的角度)
|
||||
relative_bearing = (initial_bearing_deg - own_course + 360) % 360
|
||||
return relative_bearing
|
||||
|
||||
|
||||
@router.get("/header")
|
||||
def get_header_info(request):
|
||||
# 固定本船MMSI
|
||||
ownship_mmsi = OWN_SHIP_MMSI
|
||||
# current_time = datetime.now()
|
||||
# current_date = current_time.strftime('%Y-%m-%d')
|
||||
# current_time_str = current_time.strftime('%H:%M')
|
||||
|
||||
# # 检查是否是第一次调用,如果是则初始化位置
|
||||
# if not hasattr(get_header_info, 'ship_data'):
|
||||
# # 初始位置 (30.9918, 122.2788)
|
||||
# get_header_info.ship_data = {
|
||||
# 'mmsi': ownship_mmsi,
|
||||
# 'latitude': 30.9918,
|
||||
# 'longitude': 122.2788,
|
||||
# 'speed': 12.0,
|
||||
# 'course': 45,
|
||||
# 'date': current_date,
|
||||
# 'time': current_time_str
|
||||
# }
|
||||
# else:
|
||||
# # 更新位置和航向
|
||||
# get_header_info.ship_data['latitude'] = round(get_header_info.ship_data['latitude'] + 0.001, 4)
|
||||
# get_header_info.ship_data['longitude'] = round(get_header_info.ship_data['longitude'] + 0.001, 4)
|
||||
# get_header_info.ship_data['course'] = round((get_header_info.ship_data['course'] + 5) % 360, 1)
|
||||
# get_header_info.ship_data['date'] = current_date
|
||||
# get_header_info.ship_data['time'] = current_time_str
|
||||
|
||||
# # 将数据保存到GpsStatus模型
|
||||
# GpsStatus.objects.create(
|
||||
# utc_time=current_time,
|
||||
# longitude=get_header_info.ship_data['longitude'],
|
||||
# latitude=get_header_info.ship_data['latitude'],
|
||||
# altitude=0.0, # 固定值
|
||||
# used_satellites=10, # 固定值
|
||||
# gps_satellites=6, # 固定值
|
||||
# beidou_satellites=4, # 固定值
|
||||
# gps_status=1, # 固定值,1表示正常
|
||||
# fix_mode=3, # 固定值,3表示3D定位
|
||||
# hdop=1.2, # 固定值
|
||||
# speed=get_header_info.ship_data['speed'],
|
||||
# direction=get_header_info.ship_data['course']
|
||||
# )
|
||||
|
||||
#查询最新经纬度数据
|
||||
latest_gps = GpsStatus.objects.order_by('-utc_time').first()
|
||||
|
||||
# 计算 10 分钟前的时间,基于 GpsStatus 中的时间
|
||||
ten_minutes_ago = latest_gps.utc_time - timedelta(minutes=10)
|
||||
# 筛选出 10 分钟内的船只记录
|
||||
recent_ships = ShipLatestStatus.objects.filter(timestamp__gte=ten_minutes_ago)
|
||||
closest_ship = None
|
||||
min_distance_km = float('inf')
|
||||
for ship in recent_ships:
|
||||
# 计算本船与当前船只的距离(公里)
|
||||
distance_km = haversine(latest_gps.latitude, latest_gps.longitude, ship.latitude, ship.longitude)
|
||||
if distance_km < min_distance_km:
|
||||
min_distance_km = distance_km
|
||||
closest_ship = ship
|
||||
# 判断是否小于 5 海里(1 海里 = 1.852 公里)
|
||||
warning_message = ''
|
||||
# min_distance_km = random.randint(1, 20)
|
||||
print(min_distance_km)
|
||||
if 3 * 1.852 < min_distance_km < 5 * 1.852:
|
||||
warning_message = '注意,5海里范围内有其他船只'
|
||||
print(warning_message)
|
||||
elif min_distance_km <= 3 * 1.852:
|
||||
relative_bearing = calculate_relative_bearing(
|
||||
latest_gps.latitude,
|
||||
latest_gps.longitude,
|
||||
latest_gps.direction, # 本船航向
|
||||
closest_ship.latitude,
|
||||
closest_ship.longitude
|
||||
)
|
||||
warning_message = f'警告:有船靠近,相对方位 {relative_bearing:.1f}°,相距 {min_distance_km / 1.852:.2f} 海里, 速度{closest_ship.speed}节'
|
||||
else:
|
||||
warning_message = '保持正常航行'
|
||||
flag = MessageFlag.objects.first()
|
||||
has_message = flag.has_new_message
|
||||
if has_message:
|
||||
flag.has_new_message = False
|
||||
flag.save()
|
||||
return {
|
||||
'mmsi': ownship_mmsi, # 固定本船MMSI
|
||||
'latitude': latest_gps.latitude,
|
||||
'longitude': latest_gps.longitude,
|
||||
'speed': latest_gps.speed,
|
||||
'course': latest_gps.direction,
|
||||
'date': latest_gps.utc_time.strftime('%Y-%m-%d'),
|
||||
'time': latest_gps.utc_time.strftime('%H:%M'),
|
||||
"satelliteCount": random.randint(0, 12),
|
||||
"messageCount": 12,
|
||||
"reminderCount": random.randint(0, 20),
|
||||
"alertCount": random.randint(0, 15),
|
||||
"approachingCount": random.randint(0, 5),
|
||||
"trackStatus": False,
|
||||
"gpsSignal": round(random.uniform(0, 5), 1),
|
||||
"beidouSignal": round(random.uniform(0, 5), 1),
|
||||
"vdesSignal": round(random.uniform(0, 5), 1),
|
||||
"warning_message": warning_message,
|
||||
"is_receive_vdes_data": has_message
|
||||
}
|
||||
# return get_header_info.ship_data
|
||||
32
code/system/apps.py
Normal file
32
code/system/apps.py
Normal file
@@ -0,0 +1,32 @@
|
||||
from django.apps import AppConfig
|
||||
import logging
|
||||
from .management.commands.vdes_monitor import VDESSocketMonitor
|
||||
|
||||
class SystemConfig(AppConfig):
|
||||
default_auto_field = 'django.db.models.BigAutoField'
|
||||
name = 'system'
|
||||
|
||||
def ready(self):
|
||||
# 确保只在主进程中启动(避免在开发服务器中启动两次)
|
||||
if not self._is_runserver_with_reload():
|
||||
self.start_vdes_monitor()
|
||||
from .models import MessageFlag
|
||||
try:
|
||||
# 确保表创建完成后检查是否存在记录
|
||||
if not MessageFlag.objects.exists():
|
||||
MessageFlag.objects.create(has_new_message=False)
|
||||
print("初始化 MessageFlag: 已创建默认记录")
|
||||
except:
|
||||
print("初始化 MessageFlag 失败")
|
||||
|
||||
def _is_runserver_with_reload(self):
|
||||
"""检查是否在开发服务器的重载进程中"""
|
||||
import sys
|
||||
return 'runserver' in sys.argv and '--noreload' not in sys.argv
|
||||
|
||||
def start_vdes_monitor(self):
|
||||
"""启动 VDES 监控服务"""
|
||||
monitor = VDESSocketMonitor()
|
||||
monitor.start()
|
||||
logger = logging.getLogger(__name__)
|
||||
logger.info("VDES 监控服务已启动")
|
||||
0
code/system/code_template/__init__.py
Normal file
0
code/system/code_template/__init__.py
Normal file
0
code/system/code_template/backend/__init__.py
Normal file
0
code/system/code_template/backend/__init__.py
Normal file
100
code/system/code_template/backend/api.py
Normal file
100
code/system/code_template/backend/api.py
Normal file
@@ -0,0 +1,100 @@
|
||||
import json
|
||||
|
||||
from system.code_template.backend.mapping import component_to_search_type
|
||||
from utils.ru_convert import RuleConvert
|
||||
|
||||
|
||||
def generator_backend_api(api_info):
|
||||
form_info = api_info.form_info
|
||||
form_info_dict = json.loads(form_info)
|
||||
filter_txt = ''
|
||||
field_code = ''
|
||||
|
||||
for item in form_info_dict.get('schemas'):
|
||||
field_code = f''''{item['field']}', {field_code}'''
|
||||
column = f'''
|
||||
{item['field']}: {component_to_search_type[item['component']]} = Field(None, alias='{item['field']}')'''
|
||||
filter_txt = column + filter_txt
|
||||
|
||||
api_txt = f'''from typing import List
|
||||
from .model import {RuleConvert.to_upper_camel_case(api_info.code)}
|
||||
from ninja import Field, ModelSchema, Query, Router
|
||||
from ninja.pagination import paginate
|
||||
from utils.fu_crud import (
|
||||
ImportSchema,
|
||||
create,
|
||||
delete,
|
||||
export_data,
|
||||
import_data,
|
||||
retrieve,
|
||||
update,
|
||||
)
|
||||
from utils.fu_ninja import FuFilters, MyPagination
|
||||
|
||||
router = Router()
|
||||
|
||||
|
||||
# 设置过滤字段
|
||||
class Filters(FuFilters):
|
||||
{filter_txt}
|
||||
|
||||
|
||||
# 设置请求接收字段
|
||||
class {RuleConvert.to_upper_camel_case(api_info.code)}SchemaIn(ModelSchema):
|
||||
|
||||
class Config:
|
||||
model = {RuleConvert.to_upper_camel_case(api_info.code)}
|
||||
model_exclude = ['create_datetime', 'update_datetime', 'belong_dept', 'modifier', 'creator', 'id']
|
||||
|
||||
|
||||
# 设置响应字段
|
||||
class {RuleConvert.to_upper_camel_case(api_info.code)}SchemaOut(ModelSchema):
|
||||
|
||||
class Config:
|
||||
model = {RuleConvert.to_upper_camel_case(api_info.code)}
|
||||
model_fields = '__all__'
|
||||
|
||||
|
||||
# 创建{RuleConvert.to_upper_camel_case(api_info.code)}
|
||||
@router.post('/{api_info.code}', response={RuleConvert.to_upper_camel_case(api_info.code)}SchemaOut)
|
||||
def create_{api_info.code}(request, data: {RuleConvert.to_upper_camel_case(api_info.code)}SchemaIn):
|
||||
{api_info.code} = create(request, data, {RuleConvert.to_upper_camel_case(api_info.code)})
|
||||
return {api_info.code}
|
||||
|
||||
|
||||
# 删除{RuleConvert.to_upper_camel_case(api_info.code)}
|
||||
@router.delete('/{api_info.code}/{{id}}')
|
||||
def delete_{api_info.code}(request, id: int):
|
||||
delete(id, {RuleConvert.to_upper_camel_case(api_info.code)})
|
||||
return {{'success': True}}
|
||||
|
||||
|
||||
# 更新{RuleConvert.to_upper_camel_case(api_info.code)}
|
||||
@router.put('/{api_info.code}/{{id}}', response={RuleConvert.to_upper_camel_case(api_info.code)}SchemaOut)
|
||||
def update_{api_info.code}(request, id: int, data: {RuleConvert.to_upper_camel_case(api_info.code)}SchemaIn):
|
||||
{api_info.code} = update(request, id, data, {RuleConvert.to_upper_camel_case(api_info.code)})
|
||||
return {api_info.code}
|
||||
|
||||
|
||||
# 获取{RuleConvert.to_upper_camel_case(api_info.code)}
|
||||
@router.get('/{api_info.code}', response=List[{RuleConvert.to_upper_camel_case(api_info.code)}SchemaOut])
|
||||
@paginate(MyPagination)
|
||||
def list_{api_info.code}(request, filters: Filters = Query(...)):
|
||||
qs = retrieve(request, {RuleConvert.to_upper_camel_case(api_info.code)}, filters)
|
||||
return qs
|
||||
|
||||
|
||||
# 导入
|
||||
@router.get('/{api_info.code}/all/export')
|
||||
def export_{api_info.code}(request):
|
||||
export_fields = ({field_code})
|
||||
return export_data(request, {RuleConvert.to_upper_camel_case(api_info.code)}, {RuleConvert.to_upper_camel_case(api_info.code)}SchemaOut, export_fields)
|
||||
|
||||
|
||||
# 导出
|
||||
@router.post('/{api_info.code}/all/import')
|
||||
def import_{api_info.code}(request, data: ImportSchema):
|
||||
import_fields = ({field_code})
|
||||
return import_data(request, {RuleConvert.to_upper_camel_case(api_info.code)}, {RuleConvert.to_upper_camel_case(api_info.code)}SchemaIn, data, import_fields)
|
||||
'''
|
||||
return api_txt
|
||||
31
code/system/code_template/backend/mapping.py
Normal file
31
code/system/code_template/backend/mapping.py
Normal file
@@ -0,0 +1,31 @@
|
||||
component_to_db_type = {
|
||||
'Input': 'CharField(null=True, blank=True, max_length=255, ',
|
||||
'IconPicker': 'CharField(null=True, blank=True, max_length=255, ',
|
||||
'StrengthMeter': 'CharField(null=True, blank=True, max_length=255, ',
|
||||
'Select': 'CharField(null=True, blank=True, max_length=255, ',
|
||||
'CheckboxGroup': 'CharField(null=True, blank=True, max_length=255, ',
|
||||
'InputNumber': 'DecimalField(null=True, blank=True, max_digits = 13, decimal_places = 4, ',
|
||||
'InputTextArea': 'TextField(null=True, blank=True, ',
|
||||
'RadioGroup': 'CharField(null=True, blank=True, max_length=255, ',
|
||||
'DatePicker': 'DateField(null=True, blank=True, ',
|
||||
'MonthPicker': 'DateField(null=True, blank=True, ',
|
||||
'TimePicker': 'TimeField(null=True, blank=True, ',
|
||||
'Switch': 'BooleanField(null=True, blank=True, ',
|
||||
'Rate': 'CharField(null=True, blank=True, max_length=255, ',
|
||||
}
|
||||
|
||||
component_to_search_type = {
|
||||
'Input': 'str',
|
||||
'IconPicker': 'str',
|
||||
'StrengthMeter': 'str',
|
||||
'Select': 'str',
|
||||
'CheckboxGroup': 'str',
|
||||
'InputNumber': 'int',
|
||||
'InputTextArea': 'str',
|
||||
'RadioGroup': 'str',
|
||||
'DatePicker': 'str',
|
||||
'MonthPicker': 'str',
|
||||
'TimePicker': 'str',
|
||||
'Switch': 'bool',
|
||||
'Rate': 'str',
|
||||
}
|
||||
31
code/system/code_template/backend/model.py
Normal file
31
code/system/code_template/backend/model.py
Normal file
@@ -0,0 +1,31 @@
|
||||
import json
|
||||
|
||||
from system.code_template.backend.mapping import component_to_db_type
|
||||
from utils.ru_convert import RuleConvert
|
||||
|
||||
|
||||
def generator_backend_model(model_info):
|
||||
form_info = model_info.form_info
|
||||
form_info_dict = json.loads(form_info)
|
||||
header_txt = f'''from django.db import models
|
||||
from utils.models import CoreModel
|
||||
|
||||
|
||||
'''
|
||||
columns_txt = ''
|
||||
for item in form_info_dict.get('schemas'):
|
||||
column = f'''
|
||||
{item['field']} = models.{component_to_db_type[item['component']]}verbose_name='{item['label']}', help_text='{item['label']}')'''
|
||||
columns_txt = column + columns_txt
|
||||
|
||||
bottom_txt = f'''class {RuleConvert.to_upper_camel_case(model_info.code)}(CoreModel):
|
||||
{columns_txt}
|
||||
|
||||
class Meta:
|
||||
db_table = 'generator_{model_info.code}'
|
||||
verbose_name = '{model_info.name}'
|
||||
verbose_name_plural = verbose_name
|
||||
ordering = ('-create_datetime',)
|
||||
'''
|
||||
model_txt = header_txt + bottom_txt
|
||||
return model_txt
|
||||
9
code/system/code_template/backend/router.py
Normal file
9
code/system/code_template/backend/router.py
Normal file
@@ -0,0 +1,9 @@
|
||||
def generator_router(router_info):
|
||||
router_txt = f'''from ninja import Router
|
||||
from .api import router
|
||||
|
||||
{router_info.code}_router = Router()
|
||||
{router_info.code}_router.add_router('/', router, tags=['{router_info.name}'])
|
||||
'''
|
||||
return router_txt
|
||||
pass
|
||||
0
code/system/code_template/web/__init__.py
Normal file
0
code/system/code_template/web/__init__.py
Normal file
48
code/system/code_template/web/api_template.py
Normal file
48
code/system/code_template/web/api_template.py
Normal file
@@ -0,0 +1,48 @@
|
||||
def generator_api(api_info):
|
||||
return f'''import {{ defHttp }} from '/@/utils/http/axios';
|
||||
|
||||
enum DeptApi {{
|
||||
prefix = '/api/{'generator'}/{api_info.code}',
|
||||
}}
|
||||
/**
|
||||
* 获取list
|
||||
*/
|
||||
export const getList = (params) => {{
|
||||
return defHttp.get({{ url: DeptApi.prefix, params }});
|
||||
}};
|
||||
|
||||
/**
|
||||
* 保存或更新
|
||||
*/
|
||||
export const createOrUpdate = (params, isUpdate) => {{
|
||||
if (isUpdate) {{
|
||||
return defHttp.put({{ url: DeptApi.prefix + '/' + params.id, params }});
|
||||
}} else {{
|
||||
return defHttp.post({{ url: DeptApi.prefix, params }});
|
||||
}}
|
||||
}};
|
||||
|
||||
/**
|
||||
* 导入
|
||||
*/
|
||||
export const importData = (params) => {{
|
||||
return defHttp.post({{ url: DeptApi.prefix + '/all/import', params }});
|
||||
}};
|
||||
|
||||
/**
|
||||
* 导出
|
||||
*/
|
||||
export const exportData = () => {{
|
||||
return defHttp.get(
|
||||
{{ url: DeptApi.prefix + '/all/export', responseType: 'blob' }},
|
||||
{{ isReturnNativeResponse: true }},
|
||||
);
|
||||
}};
|
||||
|
||||
/**
|
||||
* 删除
|
||||
*/
|
||||
export const deleteItem = (id) => {{
|
||||
return defHttp.delete({{ url: DeptApi.prefix + '/' + id }});
|
||||
}};
|
||||
'''
|
||||
84
code/system/code_template/web/data_template.py
Normal file
84
code/system/code_template/web/data_template.py
Normal file
@@ -0,0 +1,84 @@
|
||||
import json
|
||||
import re
|
||||
|
||||
|
||||
# 自定义函数用于处理布尔值转换
|
||||
def bool_to_str(val):
|
||||
if isinstance(val, bool):
|
||||
return str(val).lower() # 将布尔值转换为小写字符串
|
||||
raise TypeError("Object of type %s is not JSON serializable" % type(val))
|
||||
|
||||
|
||||
# Custom JSON decoder to convert boolean strings to boolean values
|
||||
def custom_json_decoder(data):
|
||||
for key, value in data.items():
|
||||
if value == 'true':
|
||||
data[key] = True
|
||||
elif value == 'false':
|
||||
data[key] = False
|
||||
return data
|
||||
|
||||
|
||||
def generator_data(data_info):
|
||||
head_txt = f'''
|
||||
import {{ BasicColumn }} from '/@/components/Table';
|
||||
import {{ FormSchema }} from '/@/components/Table';
|
||||
'''
|
||||
column_schemas = ''
|
||||
table_info = json.loads(data_info.table_info)
|
||||
column_info = table_info.get('columnInfo')
|
||||
for item in column_info:
|
||||
column_schema = f'''
|
||||
{{
|
||||
title: '{item['column_name']}',
|
||||
dataIndex: '{item['field_name']}',
|
||||
width: '{item['width']}',
|
||||
fixed: '{item['freeze']}',
|
||||
align: '{item['align']}',
|
||||
auth: ['{data_info.code + ':' + item['field_name']}'],
|
||||
resizable: {str(item['resizable']).lower()},
|
||||
}},
|
||||
'''
|
||||
column_schemas = column_schemas + column_schema
|
||||
column_txt = f'''export const columns: BasicColumn[] = [
|
||||
{column_schemas}
|
||||
]
|
||||
'''
|
||||
|
||||
search_schemas = ''
|
||||
table_info = json.loads(data_info.table_info)
|
||||
search_info = table_info.get('searchInfo')
|
||||
for item in search_info:
|
||||
search_schema = f'''
|
||||
{{
|
||||
label: '{item['column_name']}',
|
||||
field: '{item['field_name']}',
|
||||
component: 'Input',
|
||||
colProps: {{ span: 6 }},
|
||||
}},
|
||||
'''
|
||||
search_schemas = search_schemas + search_schema
|
||||
search_txt = f'''export const searchFormSchema: FormSchema[] = [
|
||||
{search_schemas}
|
||||
];
|
||||
'''
|
||||
form_info = json.loads(data_info.form_info).get('schemas')
|
||||
for item in form_info:
|
||||
item.pop('key')
|
||||
item.pop('icon')
|
||||
|
||||
id_dict = {
|
||||
'field': 'id',
|
||||
'label': 'id',
|
||||
'component': 'Input',
|
||||
'show': False,
|
||||
}
|
||||
form_info.insert(0, id_dict)
|
||||
|
||||
json_str = json.dumps(form_info, ensure_ascii=False, indent=2, default=bool_to_str)
|
||||
js_object = re.sub(r'"\b(\w+)\b":', r'\1:', json_str)
|
||||
form_schemas = js_object.replace('"', "'")
|
||||
|
||||
form_txt = f'''export const formSchema: FormSchema[] = {form_schemas}'''
|
||||
|
||||
return head_txt + column_txt + search_txt + form_txt
|
||||
72
code/system/code_template/web/drawer_template.py
Normal file
72
code/system/code_template/web/drawer_template.py
Normal file
@@ -0,0 +1,72 @@
|
||||
def generator_drawer(drawer_info):
|
||||
return f'''<template>
|
||||
<BasicDrawer
|
||||
v-bind="$attrs"
|
||||
@register="registerDrawer"
|
||||
showFooter
|
||||
:title="getTitle"
|
||||
width="50%"
|
||||
@ok="handleSubmit"
|
||||
>
|
||||
<BasicForm @register="registerForm" />
|
||||
</BasicDrawer>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import {{ defineComponent, ref, computed, unref }} from 'vue';
|
||||
import {{ BasicForm, useForm }} from '/@/components/Form/index';
|
||||
import {{ BasicDrawer, useDrawerInner }} from '/@/components/Drawer';
|
||||
import {{ createOrUpdate }} from './api';
|
||||
import {{ formSchema }} from './data';
|
||||
import {{ useI18n }} from '/@/hooks/web/useI18n';
|
||||
|
||||
export default defineComponent({{
|
||||
name: '{drawer_info.code}Drawer',
|
||||
components: {{ BasicDrawer, BasicForm }},
|
||||
emits: ['success', 'register'],
|
||||
setup(_, {{ emit }}) {{
|
||||
const isUpdate = ref(true);
|
||||
const {{ t }} = useI18n();
|
||||
|
||||
const [registerForm, {{ resetFields, setFieldsValue, validate }}] = useForm({{
|
||||
labelWidth: 100,
|
||||
schemas: formSchema,
|
||||
showActionButtonGroup: false,
|
||||
}});
|
||||
|
||||
const [registerDrawer, {{ setDrawerProps, closeDrawer }}] = useDrawerInner(async (data) => {{
|
||||
await resetFields();
|
||||
setDrawerProps({{ confirmLoading: false }});
|
||||
isUpdate.value = !!data?.isUpdate;
|
||||
|
||||
if (unref(isUpdate)) {{
|
||||
await setFieldsValue({{
|
||||
...data.record,
|
||||
}});
|
||||
}}
|
||||
}});
|
||||
|
||||
const getTitle = computed(() =>
|
||||
!unref(isUpdate) ? t('common.addText') : t('common.updateText'),
|
||||
);
|
||||
|
||||
async function handleSubmit() {{
|
||||
try {{
|
||||
const values = await validate();
|
||||
setDrawerProps({{ confirmLoading: true }});
|
||||
await createOrUpdate(values, unref(isUpdate));
|
||||
closeDrawer();
|
||||
emit('success');
|
||||
}} finally {{
|
||||
setDrawerProps({{ confirmLoading: false }});
|
||||
}}
|
||||
}}
|
||||
|
||||
return {{
|
||||
registerDrawer,
|
||||
registerForm,
|
||||
getTitle,
|
||||
handleSubmit,
|
||||
}};
|
||||
}},
|
||||
}});
|
||||
</script>'''
|
||||
190
code/system/code_template/web/index_template.py
Normal file
190
code/system/code_template/web/index_template.py
Normal file
@@ -0,0 +1,190 @@
|
||||
def generator_index(index_info):
|
||||
return f'''<template>
|
||||
<div>
|
||||
<BasicTable @register="registerTable">
|
||||
<template #tableTitle>
|
||||
<Space style="height: 40px">
|
||||
<a-button
|
||||
type="primary"
|
||||
v-auth="['{index_info.code}:add']"
|
||||
preIcon="ant-design:plus-outlined"
|
||||
@click="handleCreate"
|
||||
>
|
||||
{{{{ t('common.addText') }}}}
|
||||
</a-button>
|
||||
<a-button
|
||||
type="error"
|
||||
v-auth="['{index_info.code}:delete']"
|
||||
preIcon="ant-design:delete-outlined"
|
||||
@click="handleBulkDelete"
|
||||
>
|
||||
{{{{ t('common.delText') }}}}
|
||||
</a-button>
|
||||
<BasicUpload
|
||||
:maxSize="20"
|
||||
:maxNumber="1"
|
||||
@change="handleChange"
|
||||
class="my-5"
|
||||
type="warning"
|
||||
:text="t('common.importText')"
|
||||
v-auth="['{index_info.code}:update']"
|
||||
/>
|
||||
<a-button
|
||||
type="success"
|
||||
v-auth="['{index_info.code}:update']"
|
||||
preIcon="carbon:cloud-download"
|
||||
@click="handleExportData"
|
||||
>
|
||||
{{{{ t('common.exportText') }}}}
|
||||
</a-button>
|
||||
</Space>
|
||||
</template>
|
||||
<template #bodyCell="{{ column, record }}">
|
||||
<template v-if="column.key === 'action'">
|
||||
<TableAction
|
||||
:actions="[
|
||||
{{
|
||||
type: 'button',
|
||||
icon: 'clarity:note-edit-line',
|
||||
color: 'primary',
|
||||
auth: ['{index_info.code}:update'],
|
||||
onClick: handleEdit.bind(null, record),
|
||||
}},
|
||||
{{
|
||||
icon: 'ant-design:delete-outlined',
|
||||
type: 'button',
|
||||
color: 'error',
|
||||
placement: 'left',
|
||||
auth: ['{index_info.code}:delete'],
|
||||
popConfirm: {{
|
||||
title: t('common.delHintText'),
|
||||
confirm: handleDelete.bind(null, record.id),
|
||||
}},
|
||||
}},
|
||||
]"
|
||||
/>
|
||||
</template>
|
||||
</template>
|
||||
</BasicTable>
|
||||
<Drawer @register="registerDrawer" @success="handleSuccess" />
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import {{ defineComponent }} from 'vue';
|
||||
|
||||
import {{ BasicTable, useTable, TableAction }} from '/@/components/Table';
|
||||
import {{ usePermission }} from '/@/hooks/web/usePermission';
|
||||
import {{ useDrawer }} from '/@/components/Drawer';
|
||||
import Drawer from './drawer.vue';
|
||||
import {{ Space }} from 'ant-design-vue';
|
||||
import {{ BasicUpload }} from '/@/components/Upload';
|
||||
import {{ deleteItem, getList, exportData, importData }} from './api';
|
||||
import {{ columns, searchFormSchema }} from './data';
|
||||
import {{ message }} from 'ant-design-vue';
|
||||
import {{ useMessage }} from '/@/hooks/web/useMessage';
|
||||
import {{ downloadByData }} from '/@/utils/file/download';
|
||||
import {{ useI18n }} from '/@/hooks/web/useI18n';
|
||||
export default defineComponent({{
|
||||
name: '{index_info.code}',
|
||||
components: {{ BasicTable, Drawer, TableAction, BasicUpload, Space }},
|
||||
setup() {{
|
||||
const {{ t }} = useI18n();
|
||||
|
||||
const [registerDrawer, {{ openDrawer }}] = useDrawer();
|
||||
const {{ createConfirm }} = useMessage();
|
||||
const {{ hasPermission }} = usePermission();
|
||||
const [registerTable, {{ reload, getSelectRows }}] = useTable({{
|
||||
api: getList,
|
||||
columns,
|
||||
formConfig: {{
|
||||
labelWidth: 80,
|
||||
schemas: searchFormSchema,
|
||||
}},
|
||||
useSearchForm: true,
|
||||
showTableSetting: true,
|
||||
tableSetting: {{ fullScreen: true }},
|
||||
bordered: true,
|
||||
showIndexColumn: false,
|
||||
rowSelection: {{
|
||||
type: 'checkbox',
|
||||
}},
|
||||
actionColumn: {{
|
||||
width: 150,
|
||||
title: t('common.operationText'),
|
||||
dataIndex: 'action',
|
||||
fixed: undefined,
|
||||
}},
|
||||
}});
|
||||
|
||||
function handleCreate() {{
|
||||
openDrawer(true, {{
|
||||
isUpdate: false,
|
||||
}});
|
||||
}}
|
||||
|
||||
function handleEdit(record: Recordable) {{
|
||||
openDrawer(true, {{
|
||||
record,
|
||||
isUpdate: true,
|
||||
}});
|
||||
}}
|
||||
|
||||
async function handleDelete(id: number) {{
|
||||
await deleteItem(id);
|
||||
message.success(t('common.successText'));
|
||||
await reload();
|
||||
}}
|
||||
|
||||
function handleBulkDelete() {{
|
||||
if (getSelectRows().length == 0) {{
|
||||
message.warning(t('common.batchDelHintText'));
|
||||
}} else {{
|
||||
createConfirm({{
|
||||
iconType: 'warning',
|
||||
title: t('common.hintText'),
|
||||
content: t('common.delHintText'),
|
||||
async onOk() {{
|
||||
for (const item of getSelectRows()) {{
|
||||
await deleteItem(item.id);
|
||||
}}
|
||||
message.success(t('common.successText'));
|
||||
await reload();
|
||||
}},
|
||||
}});
|
||||
}}
|
||||
}}
|
||||
|
||||
async function handleChange(list: string[]) {{
|
||||
console.log(list[0]);
|
||||
await importData({{ path: list[0] }});
|
||||
message.success('导入成功');
|
||||
await reload();
|
||||
}}
|
||||
|
||||
async function handleExportData() {{
|
||||
const response = await exportData();
|
||||
await downloadByData(response.data, '{index_info.name}.xlsx');
|
||||
}}
|
||||
|
||||
function handleSuccess() {{
|
||||
message.success(t('common.successText'));
|
||||
reload();
|
||||
}}
|
||||
|
||||
return {{
|
||||
registerTable,
|
||||
registerDrawer,
|
||||
handleCreate,
|
||||
handleEdit,
|
||||
handleDelete,
|
||||
handleSuccess,
|
||||
hasPermission,
|
||||
handleBulkDelete,
|
||||
getSelectRows,
|
||||
handleExportData,
|
||||
handleChange,
|
||||
t,
|
||||
}};
|
||||
}},
|
||||
}});
|
||||
</script>'''
|
||||
1503
code/system/initialize.py
Normal file
1503
code/system/initialize.py
Normal file
File diff suppressed because it is too large
Load Diff
0
code/system/management/__init__.py
Normal file
0
code/system/management/__init__.py
Normal file
0
code/system/management/commands/__init__.py
Normal file
0
code/system/management/commands/__init__.py
Normal file
260
code/system/management/commands/generator.py
Normal file
260
code/system/management/commands/generator.py
Normal file
@@ -0,0 +1,260 @@
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
import shutil
|
||||
|
||||
from django.core.management.base import BaseCommand
|
||||
|
||||
from fuadmin.settings import BASE_DIR
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
"""
|
||||
创建App命令:
|
||||
python manage.py createapp app名
|
||||
python manage.py createapp app01 app02 ...
|
||||
python manage.py createapp 一级文件名/app01 ... # 支持多级目录建app
|
||||
"""
|
||||
|
||||
def add_arguments(self, parser):
|
||||
parser.add_argument('app_info', nargs='*', type=str, )
|
||||
|
||||
def handle(self, *args, **options):
|
||||
app_info = options.get('app_info')
|
||||
for name in app_info:
|
||||
app = json.loads(name)
|
||||
name = app.get('app_name')
|
||||
names = name.split('/')
|
||||
dnames = ".".join(names)
|
||||
app_path = os.path.join(BASE_DIR, "dvadmin", *names)
|
||||
# 判断app是否存在
|
||||
if os.path.exists(app_path):
|
||||
print(f"App {name} 已存在!")
|
||||
# break
|
||||
else:
|
||||
source_path = os.path.join(BASE_DIR, "dvadmin", "utils", "template")
|
||||
target_path = app_path
|
||||
if not os.path.exists(target_path):
|
||||
# 如果目标路径不存在原文件夹的话就创建
|
||||
os.makedirs(target_path)
|
||||
if os.path.exists(source_path):
|
||||
# 如果目标路径存在原文件夹的话就先删除
|
||||
shutil.rmtree(target_path)
|
||||
shutil.copytree(source_path, target_path)
|
||||
# 注册app到 settings.py 中
|
||||
injection(os.path.join(BASE_DIR, "application", "settings.py"), f" 'dvadmin.{dnames}',\n",
|
||||
"INSTALLED_APPS",
|
||||
"]")
|
||||
|
||||
# 注册app到 urls.py 中
|
||||
injection(os.path.join(BASE_DIR, "application", "urls.py"),
|
||||
f" path(r'api/{name}/', include('dvadmin.{dnames}.urls')),\n", "urlpatterns = [",
|
||||
"]")
|
||||
|
||||
# 修改app中的apps 内容
|
||||
app_content = f"""
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class {name.capitalize()}Config(AppConfig):
|
||||
name = 'dvadmin.{dnames}'
|
||||
verbose_name = "{names[-1]}App"
|
||||
"""
|
||||
with open(os.path.join(app_path, "apps.py"), 'w', encoding='UTF-8') as f:
|
||||
f.write(app_content)
|
||||
|
||||
# 修改Model的内容
|
||||
|
||||
table_name = app.get('table_name')
|
||||
model_name = app.get('model_name')
|
||||
|
||||
# app中所有不重复的字段
|
||||
app_fields = app.get('fields')
|
||||
field_content = ''
|
||||
# app中所有不重复的字段type
|
||||
fields_type = set()
|
||||
# app中所有的字段name
|
||||
fields_name = set()
|
||||
for field in app_fields:
|
||||
fields_type.add(field['type'])
|
||||
fields_name.add(field['name'])
|
||||
if field['type'] == 'CharField':
|
||||
field_content = f"""{field_content}
|
||||
{field['name']} = {field['type']}(max_length={field['max_length']}, verbose_name='{field['name']}', help_text='{field['description']}')"""
|
||||
elif field['type'] == 'TextField':
|
||||
field_content = f"""{field_content}
|
||||
{field['name']} = {field['type']}(verbose_name='{field['name']}', help_text='{field['description']}')"""
|
||||
model_content = f"""
|
||||
from django.db.models import {','.join(fields_type)}
|
||||
from dvadmin.utils.models import CoreModel
|
||||
|
||||
|
||||
class {model_name}(CoreModel):
|
||||
{field_content}
|
||||
|
||||
class Meta:
|
||||
verbose_name = '{model_name}'
|
||||
verbose_name_plural = verbose_name
|
||||
|
||||
def __str__(self):
|
||||
return self.{fields_name.pop()}
|
||||
"""
|
||||
|
||||
with open(os.path.join(app_path, "models", f"{table_name}.py"), 'w', encoding='UTF-8') as f:
|
||||
f.write(model_content)
|
||||
|
||||
# 修改 model init文件
|
||||
model_init_content = f"from dvadmin.{name}.models.{table_name} import {model_name}"
|
||||
with open(os.path.join(app_path, "models", "__init__.py"), 'a', encoding='UTF-8') as f:
|
||||
f.write(model_init_content)
|
||||
f.write('\n')
|
||||
|
||||
# 修改filters的内容
|
||||
filter_content = f"""
|
||||
import django_filters
|
||||
from dvadmin.{name}.models import {model_name}
|
||||
|
||||
|
||||
class {model_name}Filter(django_filters.rest_framework.FilterSet):
|
||||
|
||||
# 通过 lookup_expr 可进行模糊查询,其他配置可自行百度
|
||||
# name = django_filters.CharFilter(lookup_expr='icontains')
|
||||
|
||||
class Meta:
|
||||
model = {model_name}
|
||||
fields = '__all__'
|
||||
|
||||
"""
|
||||
with open(os.path.join(app_path, "filters", f"{table_name}_filter.py"), 'w', encoding='UTF-8') as f:
|
||||
f.write(filter_content)
|
||||
|
||||
# 修改 filters init文件
|
||||
filter_init_content = f"from .{table_name}_filter import {model_name}Filter"
|
||||
with open(os.path.join(app_path, "filters", "__init__.py"), 'a', encoding='UTF-8') as f:
|
||||
f.write(filter_init_content)
|
||||
f.write('\n')
|
||||
|
||||
# 修改serializers的内容
|
||||
serializer_content = f"""
|
||||
from rest_framework import serializers
|
||||
from dvadmin.{name}.models import {model_name}
|
||||
from dvadmin.utils.serializers import CustomModelSerializer
|
||||
|
||||
|
||||
class {model_name}Serializer(CustomModelSerializer):
|
||||
|
||||
class Meta:
|
||||
model = {model_name}
|
||||
fields = '__all__'
|
||||
|
||||
|
||||
class {model_name}CreateUpdateSerializer(CustomModelSerializer):
|
||||
|
||||
# 此处可写定制的 创建/更新 内容
|
||||
def validate(self, attrs: dict):
|
||||
return super().validate(attrs)
|
||||
|
||||
class Meta:
|
||||
model = {model_name}
|
||||
fields = '__all__'
|
||||
|
||||
|
||||
class {model_name}ImportSerializer(CustomModelSerializer):
|
||||
|
||||
class Meta:
|
||||
model = {model_name}
|
||||
fields = '__all__'
|
||||
|
||||
|
||||
class {model_name}ExportSerializer(CustomModelSerializer):
|
||||
|
||||
class Meta:
|
||||
model = {model_name}
|
||||
fields = '__all__'
|
||||
|
||||
"""
|
||||
with open(os.path.join(app_path, 'serializers', f'{table_name}_serializer.py'), 'w', encoding='UTF-8') as f:
|
||||
f.write(serializer_content)
|
||||
|
||||
# 修改 serializers init文件
|
||||
serializer_init_content = f"from .{table_name}_serializer import {model_name}Serializer, " \
|
||||
f"{model_name}CreateUpdateSerializer, " \
|
||||
f"{model_name}ImportSerializer, " \
|
||||
f"{model_name}ExportSerializer"
|
||||
with open(os.path.join(app_path, "serializers", "__init__.py"), 'a', encoding='UTF-8') as f:
|
||||
f.write(serializer_init_content)
|
||||
f.write('\n')
|
||||
# 修改view内容
|
||||
view_content = f"""
|
||||
from dvadmin.utils.filters import DataLevelPermissionsFilter
|
||||
from dvadmin.utils.viewset import CustomModelViewSet
|
||||
from dvadmin.{name}.models import {model_name}
|
||||
from dvadmin.{name}.filters import {model_name}Filter
|
||||
from dvadmin.{name}.serializers import {model_name}Serializer, {model_name}CreateUpdateSerializer, {model_name}ImportSerializer, {model_name}ExportSerializer
|
||||
|
||||
|
||||
class {model_name}ModelViewSet(CustomModelViewSet):
|
||||
|
||||
queryset = {model_name}.objects.all()
|
||||
serializer_class = {model_name}Serializer # 序列化器
|
||||
create_serializer_class = {model_name}CreateUpdateSerializer # 创建/更新时的列化器
|
||||
update_serializer_class = {model_name}CreateUpdateSerializer # 创建/更新时的列化器
|
||||
filter_class = {model_name}Filter # 过滤器
|
||||
extra_filter_backends = [DataLevelPermissionsFilter] # 数据权限类,不需要可注释掉
|
||||
# ordering = ['create_datetime'] # 默认排序
|
||||
# 导出
|
||||
export_field_data = [] # 导出
|
||||
export_serializer_class = {model_name}ExportSerializer # 导出序列化器
|
||||
# 导入
|
||||
import_field_data = []
|
||||
import_serializer_class = {model_name}ImportSerializer
|
||||
|
||||
|
||||
"""
|
||||
with open(os.path.join(app_path, 'views', f'{table_name}_view.py'), 'w', encoding='UTF-8') as f:
|
||||
f.write(view_content)
|
||||
|
||||
# 修改 views init文件
|
||||
view_init_content = f"from .{table_name}_view import {model_name}ModelViewSet"
|
||||
with open(os.path.join(app_path, "views", "__init__.py"), 'a', encoding='UTF-8') as f:
|
||||
f.write(view_init_content)
|
||||
f.write('\n')
|
||||
|
||||
# 修改 url文件
|
||||
url_package = f"from dvadmin.{name}.views import {model_name}ModelViewSet"
|
||||
injection(os.path.join(app_path, "urls.py"),
|
||||
f"{url_package}\n", "from rest_framework.routers import DefaultRouter",
|
||||
"router = DefaultRouter()")
|
||||
|
||||
url_path = f"router.register(r'{table_name}', {model_name}ModelViewSet)"
|
||||
injection(os.path.join(app_path, "urls.py"),
|
||||
f"{url_path}\n", "router = DefaultRouter()",
|
||||
"urlpatterns = [")
|
||||
|
||||
export_import_url = f""" # 导出项目
|
||||
path('{table_name}/export/', {model_name}ModelViewSet.as_view({{'get': 'export', }})),
|
||||
# 项目导入模板下载及导入
|
||||
path('{table_name}/importTemplate/', {model_name}ModelViewSet.as_view({{'get': 'importTemplate', 'post': 'importTemplate'}}))"""
|
||||
injection(os.path.join(app_path, "urls.py"),
|
||||
f"{export_import_url}\n", "urlpatterns = [", "]")
|
||||
|
||||
print(f"创建 {name} App成功")
|
||||
|
||||
|
||||
def injection(file_path, insert_content, startswith, endswith):
|
||||
with open(file_path, "r+", encoding="utf-8") as f:
|
||||
data = f.readlines()
|
||||
with open(file_path, 'w', encoding='UTF-8') as f1:
|
||||
is_INSTALLED_APPS = False
|
||||
is_insert = False
|
||||
for content in data:
|
||||
# 判断文件是否 INSTALLED_APPS 开头
|
||||
if not is_insert and content.startswith(startswith):
|
||||
is_INSTALLED_APPS = True
|
||||
if not is_insert and content.startswith(endswith) and is_INSTALLED_APPS:
|
||||
# 给前一行插入数据
|
||||
content = insert_content + content
|
||||
is_insert = True
|
||||
f1.writelines(content)
|
||||
39
code/system/management/commands/init.py
Normal file
39
code/system/management/commands/init.py
Normal file
@@ -0,0 +1,39 @@
|
||||
import logging
|
||||
|
||||
from django.core.management.base import BaseCommand
|
||||
|
||||
from fuadmin import settings
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
"""
|
||||
项目初始化命令: python manage.py init
|
||||
"""
|
||||
|
||||
def add_arguments(self, parser):
|
||||
parser.add_argument('init_name', nargs='*', type=str, )
|
||||
parser.add_argument('-y', nargs='*')
|
||||
parser.add_argument('-Y', nargs='*')
|
||||
parser.add_argument('-n', nargs='*')
|
||||
parser.add_argument('-N', nargs='*')
|
||||
|
||||
def handle(self, *args, **options):
|
||||
reset = False
|
||||
if isinstance(options.get('y'), list) or isinstance(options.get('Y'), list):
|
||||
reset = True
|
||||
if isinstance(options.get('n'), list) or isinstance(options.get('N'), list):
|
||||
reset = False
|
||||
print(f"正在准备初始化数据,{'如有初始化数据,将会不做操作跳过' if not reset else '初始数据将会先删除后新增'}...")
|
||||
|
||||
for app in settings.INSTALLED_APPS:
|
||||
|
||||
try:
|
||||
exec(f"""
|
||||
from {app}.initialize import main
|
||||
main(reset={reset})
|
||||
""")
|
||||
except ModuleNotFoundError:
|
||||
pass
|
||||
print("初始化数据完成!")
|
||||
83
code/system/management/commands/init_area.py
Normal file
83
code/system/management/commands/init_area.py
Normal file
@@ -0,0 +1,83 @@
|
||||
# 城市联动
|
||||
"""
|
||||
到乡级 使用方法
|
||||
1. https://www.npmjs.com/package/china-division 下载数据,把对应的json放入对应目录
|
||||
2. 修改此文件中对应json名
|
||||
3. 右击执行此py文件进行初始化
|
||||
"""
|
||||
import json
|
||||
import os
|
||||
|
||||
import django
|
||||
import pypinyin
|
||||
from django.core.management import BaseCommand
|
||||
from django.db import connection
|
||||
|
||||
from fuadmin.settings import BASE_DIR
|
||||
from system.models import Area
|
||||
|
||||
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'application.settings')
|
||||
django.setup()
|
||||
|
||||
area_code_list = []
|
||||
|
||||
|
||||
def area_list(code_list, pcode=None, depth=1):
|
||||
"""
|
||||
递归获取所有列表
|
||||
"""
|
||||
for code_dict in code_list:
|
||||
code = code_dict.get('code', None)
|
||||
name = code_dict.get('name', None)
|
||||
children = code_dict.get('children', None)
|
||||
pinyin = ''.join([''.join(i) for i in pypinyin.pinyin(name, style=pypinyin.NORMAL)])
|
||||
area_code_list.append(
|
||||
{
|
||||
"name": name,
|
||||
"code": code,
|
||||
"level": depth,
|
||||
"pinyin": pinyin,
|
||||
"initials": pinyin[0].upper() if pinyin else "#",
|
||||
"pcode_id": pcode,
|
||||
}
|
||||
)
|
||||
if children:
|
||||
area_list(code_list=children, pcode=code, depth=depth + 1)
|
||||
|
||||
|
||||
def main():
|
||||
with open(os.path.join(BASE_DIR, 'utils', 'pca-code.json'), 'r',encoding="utf-8") as load_f:
|
||||
code_list = json.load(load_f)
|
||||
area_list(code_list)
|
||||
if Area.objects.count() == 0:
|
||||
Area.objects.bulk_create([Area(**ele) for ele in area_code_list])
|
||||
else:
|
||||
for ele in area_code_list:
|
||||
code = ele.pop("code")
|
||||
Area.objects.update_or_create(code=code, defaults=ele)
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
"""
|
||||
项目初始化命令: python manage.py init
|
||||
"""
|
||||
|
||||
def add_arguments(self, parser):
|
||||
pass
|
||||
|
||||
def handle(self, *args, **options):
|
||||
|
||||
print(f"正在准备初始化省份数据...")
|
||||
|
||||
if hasattr(connection, 'tenant') and connection.tenant.schema_name:
|
||||
from django_tenants.utils import get_tenant_model
|
||||
from django_tenants.utils import tenant_context,schema_context
|
||||
for tenant in get_tenant_model().objects.exclude(schema_name='public'):
|
||||
with tenant_context(tenant):
|
||||
print(f"租户[{connection.tenant.schema_name}]初始化数据开始...")
|
||||
main()
|
||||
print(f"租户[{connection.tenant.schema_name}]初始化数据完成!")
|
||||
else:
|
||||
main()
|
||||
print("省份数据初始化数据完成!")
|
||||
|
||||
42
code/system/management/commands/vdes_monitor.py
Normal file
42
code/system/management/commands/vdes_monitor.py
Normal file
@@ -0,0 +1,42 @@
|
||||
import time
|
||||
import logging
|
||||
from django.core.management.base import BaseCommand
|
||||
from system.services.vdes_data_receive import VDESSocketMonitor
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
class Command(BaseCommand):
|
||||
help = '控制 VDES 监控服务'
|
||||
|
||||
def add_arguments(self, parser):
|
||||
parser.add_argument(
|
||||
'action',
|
||||
choices=['start', 'stop', 'restart'],
|
||||
help='操作: start, stop 或 restart'
|
||||
)
|
||||
|
||||
def handle(self, *args, **options):
|
||||
monitor = VDESSocketMonitor()
|
||||
action = options['action']
|
||||
|
||||
if action == 'start':
|
||||
monitor.start()
|
||||
self.stdout.write(self.style.SUCCESS('VDES 监控服务已启动'))
|
||||
elif action == 'stop':
|
||||
monitor.stop()
|
||||
self.stdout.write(self.style.SUCCESS('VDES 监控服务已停止'))
|
||||
elif action == 'restart':
|
||||
monitor.stop()
|
||||
time.sleep(1)
|
||||
monitor.start()
|
||||
self.stdout.write(self.style.SUCCESS('VDES 监控服务已重启'))
|
||||
|
||||
# 确保程序不会立即退出
|
||||
if action == 'start' or action == 'restart':
|
||||
try:
|
||||
# 保持命令运行,直到用户中断
|
||||
while True:
|
||||
time.sleep(1)
|
||||
except KeyboardInterrupt:
|
||||
monitor.stop()
|
||||
self.stdout.write(self.style.SUCCESS('\nVDES 监控服务已停止'))
|
||||
550
code/system/models.py
Normal file
550
code/system/models.py
Normal file
@@ -0,0 +1,550 @@
|
||||
import hashlib
|
||||
import os
|
||||
|
||||
from django.contrib.auth.models import AbstractUser
|
||||
from django.db import models
|
||||
from utils.models import CoreModel
|
||||
|
||||
|
||||
STATUS_CHOICES = (
|
||||
(0, "禁用"),
|
||||
(1, "启用"),
|
||||
)
|
||||
|
||||
|
||||
class Users(AbstractUser, CoreModel):
|
||||
username = models.CharField(max_length=150, unique=True, db_index=True, verbose_name='用户账号',
|
||||
help_text="用户账号")
|
||||
email = models.EmailField(max_length=255, verbose_name="邮箱", null=True, blank=True, help_text="邮箱")
|
||||
mobile = models.CharField(max_length=255, verbose_name="电话", null=True, blank=True, help_text="电话")
|
||||
avatar = models.TextField(verbose_name="头像", null=True, blank=True, help_text="头像")
|
||||
name = models.CharField(max_length=40, verbose_name="姓名", help_text="姓名")
|
||||
status = models.BooleanField(default=True, verbose_name="状态", help_text="状态")
|
||||
GENDER_CHOICES = (
|
||||
(0, "女"),
|
||||
(1, "男"),
|
||||
)
|
||||
gender = models.IntegerField(choices=GENDER_CHOICES, default=1, verbose_name="性别", null=True, blank=True,
|
||||
help_text="性别")
|
||||
USER_TYPE = (
|
||||
(0, "后台用户"),
|
||||
(1, "前台用户"),
|
||||
)
|
||||
user_type = models.IntegerField(choices=USER_TYPE, default=0, verbose_name="用户类型", null=True, blank=True,
|
||||
help_text="用户类型")
|
||||
post = models.ManyToManyField(to='Post', verbose_name='关联岗位', db_constraint=False, help_text="关联岗位")
|
||||
role = models.ManyToManyField(to='Role', verbose_name='关联角色', db_constraint=False, help_text="关联角色")
|
||||
dept = models.ForeignKey(to='Dept', verbose_name='所属部门', on_delete=models.SET_NULL, db_constraint=False,
|
||||
null=True,
|
||||
blank=True, help_text="关联部门")
|
||||
first_name = models.CharField(max_length=150, blank=True, null=True)
|
||||
last_name = models.CharField(max_length=150, blank=True, null=True)
|
||||
home_path = models.CharField(max_length=150, blank=True, null=True)
|
||||
|
||||
|
||||
class Meta:
|
||||
db_table = "system_users"
|
||||
verbose_name = '用户表'
|
||||
verbose_name_plural = verbose_name
|
||||
ordering = ('-create_datetime',)
|
||||
|
||||
|
||||
class Post(CoreModel):
|
||||
name = models.CharField(null=False, max_length=64, verbose_name="岗位名称", help_text="岗位名称")
|
||||
code = models.CharField(max_length=32, verbose_name="岗位编码", help_text="岗位编码")
|
||||
STATUS_CHOICES = (
|
||||
(0, "离职"),
|
||||
(1, "在职"),
|
||||
)
|
||||
status = models.IntegerField(choices=STATUS_CHOICES, default=1, verbose_name="岗位状态", help_text="岗位状态")
|
||||
|
||||
class Meta:
|
||||
db_table = "system_post"
|
||||
verbose_name = '岗位表'
|
||||
verbose_name_plural = verbose_name
|
||||
ordering = ('sort',)
|
||||
|
||||
|
||||
class Role(CoreModel):
|
||||
name = models.CharField(max_length=64, verbose_name="角色名称", help_text="角色名称")
|
||||
code = models.CharField(max_length=64, unique=True, verbose_name="角色编码", help_text="角色编码")
|
||||
# key = models.CharField(max_length=64, unique=True, verbose_name="权限字符", help_text="权限字符")
|
||||
status = models.BooleanField(default=True, verbose_name="角色状态", help_text="角色状态")
|
||||
admin = models.BooleanField(default=False, verbose_name="是否为admin", help_text="是否为admin")
|
||||
DATASCOPE_CHOICES = (
|
||||
(0, "仅本人数据权限"),
|
||||
(1, "本部门数据权限"),
|
||||
(2, "本部门及以下数据权限"),
|
||||
(3, "全部数据权限"),
|
||||
(4, "自定数据权限"),
|
||||
)
|
||||
data_range = models.IntegerField(default=0, choices=DATASCOPE_CHOICES, verbose_name="数据权限范围",
|
||||
help_text="数据权限范围")
|
||||
dept = models.ManyToManyField(to='Dept', verbose_name='数据权限-关联部门', db_constraint=False,
|
||||
help_text="数据权限-关联部门")
|
||||
menu = models.ManyToManyField(to='Menu', verbose_name='关联菜单', db_constraint=False, help_text="关联菜单")
|
||||
permission = models.ManyToManyField(to='MenuButton', verbose_name='关联菜单的接口按钮', db_constraint=False,
|
||||
help_text="关联菜单的接口按钮")
|
||||
column = models.ManyToManyField(to='MenuColumnField', verbose_name='列表权限', db_constraint=False,
|
||||
help_text="列表权限")
|
||||
|
||||
class Meta:
|
||||
db_table = 'system_role'
|
||||
verbose_name = '角色表'
|
||||
verbose_name_plural = verbose_name
|
||||
ordering = ('sort',)
|
||||
|
||||
|
||||
class Dept(CoreModel):
|
||||
name = models.CharField(max_length=64, verbose_name="部门名称", help_text="部门名称")
|
||||
owner = models.CharField(max_length=32, verbose_name="负责人", null=True, blank=True, help_text="负责人")
|
||||
phone = models.CharField(max_length=32, verbose_name="联系电话", null=True, blank=True, help_text="联系电话")
|
||||
email = models.EmailField(max_length=32, verbose_name="邮箱", null=True, blank=True, help_text="邮箱")
|
||||
status = models.BooleanField(default=True, verbose_name="部门状态", null=True, blank=True,
|
||||
help_text="部门状态")
|
||||
parent = models.ForeignKey(to='Dept', on_delete=models.PROTECT, default=None, verbose_name="上级部门",
|
||||
db_constraint=False, null=True, blank=True, help_text="上级部门")
|
||||
|
||||
class Meta:
|
||||
db_table = "system_dept"
|
||||
verbose_name = '部门表'
|
||||
verbose_name_plural = verbose_name
|
||||
ordering = ('sort',)
|
||||
|
||||
|
||||
class Button(CoreModel):
|
||||
name = models.CharField(max_length=64, unique=True, verbose_name="权限名称", help_text="权限名称")
|
||||
code = models.CharField(max_length=64, unique=True, verbose_name="权限值", help_text="权限值")
|
||||
status = models.BooleanField(default=True, verbose_name="按钮状态", null=True, blank=True, help_text="按钮状态")
|
||||
|
||||
class Meta:
|
||||
db_table = "system_button"
|
||||
verbose_name = '权限标识表'
|
||||
verbose_name_plural = verbose_name
|
||||
ordering = ('sort',)
|
||||
|
||||
|
||||
class Menu(CoreModel):
|
||||
parent = models.ForeignKey(to='Menu', on_delete=models.CASCADE, verbose_name="上级菜单", null=True, blank=True,
|
||||
db_constraint=False, help_text="上级菜单")
|
||||
icon = models.CharField(max_length=64, default='ant-design:book-outlined', verbose_name="菜单图标",
|
||||
help_text="菜单图标")
|
||||
title = models.CharField(max_length=64, verbose_name="菜单名称", help_text="菜单名称")
|
||||
permission = models.CharField(max_length=64, null=True, blank=True, verbose_name="权限标识", help_text="权限标识")
|
||||
ISLINK_CHOICES = (
|
||||
(0, "否"),
|
||||
(1, "是"),
|
||||
)
|
||||
is_ext = models.BooleanField(default=False, verbose_name="是否外链", help_text="是否外链")
|
||||
|
||||
TYPE_CHOICES = (
|
||||
(0, "目录"),
|
||||
(1, "菜单"),
|
||||
)
|
||||
type = models.IntegerField(choices=TYPE_CHOICES, verbose_name="是否目录", help_text="是否目录")
|
||||
path = models.CharField(max_length=128, verbose_name="路由地址", null=True, blank=True, help_text="路由地址")
|
||||
redirect = models.CharField(max_length=128, verbose_name="重定向地址", null=True, blank=True,
|
||||
help_text="重定向地址")
|
||||
|
||||
component = models.CharField(max_length=128, verbose_name="组件地址", null=True, blank=True, help_text="组件地址")
|
||||
name = models.CharField(max_length=50, verbose_name="组件名称", null=True, blank=True, help_text="组件名称")
|
||||
status = models.BooleanField(default=True, blank=True, verbose_name="菜单状态", help_text="菜单状态")
|
||||
keepalive = models.BooleanField(default=False, blank=True, verbose_name="是否页面缓存", help_text="是否页面缓存")
|
||||
hide_menu = models.BooleanField(default=False, blank=True, verbose_name="侧边栏中是否隐藏",
|
||||
help_text="侧边栏中是否隐藏")
|
||||
|
||||
class Meta:
|
||||
db_table = "system_menu"
|
||||
verbose_name = '菜单表'
|
||||
verbose_name_plural = verbose_name
|
||||
ordering = ('sort',)
|
||||
|
||||
|
||||
class MenuButton(CoreModel):
|
||||
menu = models.ForeignKey(to="Menu", db_constraint=False, related_name="menuPermission", on_delete=models.CASCADE,
|
||||
verbose_name="关联菜单", help_text='关联菜单')
|
||||
name = models.CharField(max_length=64, verbose_name="名称", help_text="名称")
|
||||
code = models.CharField(max_length=64, verbose_name="权限值", help_text="权限值")
|
||||
api = models.CharField(max_length=200, verbose_name="接口地址", help_text="接口地址")
|
||||
METHOD_CHOICES = (
|
||||
(0, "GET"),
|
||||
(1, "POST"),
|
||||
(2, "PUT"),
|
||||
(3, "DELETE"),
|
||||
)
|
||||
method = models.IntegerField(default=0, verbose_name="接口请求方法", null=True, blank=True,
|
||||
help_text="接口请求方法")
|
||||
|
||||
class Meta:
|
||||
db_table = "system_menu_button"
|
||||
verbose_name = '菜单权限表'
|
||||
verbose_name_plural = verbose_name
|
||||
ordering = ('sort',)
|
||||
|
||||
|
||||
class MenuColumnField(CoreModel):
|
||||
menu = models.ForeignKey(to="Menu", db_constraint=False, related_name="menuColumnField", on_delete=models.CASCADE,
|
||||
verbose_name="关联菜单", help_text='关联菜单')
|
||||
name = models.CharField(max_length=64, verbose_name="名称", help_text="名称")
|
||||
code = models.CharField(max_length=64, verbose_name="权限值", help_text="权限值")
|
||||
|
||||
class Meta:
|
||||
db_table = "system_menu_column_field"
|
||||
verbose_name = '菜单数据列表'
|
||||
verbose_name_plural = verbose_name
|
||||
ordering = ('sort',)
|
||||
|
||||
|
||||
class Dict(CoreModel):
|
||||
name = models.CharField(max_length=100, blank=True, null=True, verbose_name="字典名称", help_text="字典名称")
|
||||
code = models.CharField(max_length=100, blank=True, null=True, verbose_name="编码", help_text="编码")
|
||||
status = models.BooleanField(default=True, blank=True, verbose_name="状态", help_text="状态")
|
||||
remark = models.CharField(max_length=2000, blank=True, null=True, verbose_name="备注", help_text="备注")
|
||||
|
||||
class Meta:
|
||||
db_table = 'system_dict'
|
||||
verbose_name = "字典表"
|
||||
verbose_name_plural = verbose_name
|
||||
ordering = ('sort',)
|
||||
|
||||
|
||||
class DictItem(CoreModel):
|
||||
icon = models.CharField(max_length=100, blank=True, null=True, verbose_name="ICON", help_text="ICON")
|
||||
label = models.CharField(max_length=100, blank=True, null=True, verbose_name="显示名称", help_text="显示名称")
|
||||
value = models.CharField(max_length=100, blank=True, null=True, verbose_name="实际值", help_text="实际值")
|
||||
status = models.BooleanField(default=True, blank=True, verbose_name="状态", help_text="状态")
|
||||
dict = models.ForeignKey(to="Dict", db_constraint=False, related_name="dictItem", on_delete=models.CASCADE,
|
||||
help_text="字典")
|
||||
remark = models.CharField(max_length=2000, blank=True, null=True, verbose_name="备注", help_text="备注")
|
||||
|
||||
class Meta:
|
||||
db_table = 'system_dict_item'
|
||||
verbose_name = "字典表详情表"
|
||||
verbose_name_plural = verbose_name
|
||||
ordering = ('sort',)
|
||||
|
||||
|
||||
class CategoryDict(CoreModel):
|
||||
label = models.CharField(max_length=100, blank=True, null=True, verbose_name="显示名称", help_text="显示名称")
|
||||
value = models.CharField(max_length=100, blank=True, null=True, verbose_name="实际值", help_text="实际值")
|
||||
code = models.CharField(max_length=100, unique=True, blank=True, null=True, verbose_name="编码", help_text="编码")
|
||||
parent = models.ForeignKey(to='CategoryDict', on_delete=models.CASCADE, default=None, verbose_name="上级",
|
||||
db_constraint=False, null=True, blank=True, help_text="上级")
|
||||
|
||||
class Meta:
|
||||
db_table = 'system_category_dict'
|
||||
verbose_name = "分类字典表"
|
||||
verbose_name_plural = verbose_name
|
||||
ordering = ('sort',)
|
||||
|
||||
|
||||
class OperationLog(CoreModel):
|
||||
request_username = models.CharField(max_length=50, blank=True, null=True, verbose_name="请求用户",
|
||||
help_text="请求用户")
|
||||
request_modular = models.CharField(max_length=64, verbose_name="请求模块", null=True, blank=True,
|
||||
help_text="请求模块")
|
||||
request_path = models.CharField(max_length=400, verbose_name="请求地址", null=True, blank=True,
|
||||
help_text="请求地址")
|
||||
request_body = models.TextField(verbose_name="请求参数", null=True, blank=True, help_text="请求参数")
|
||||
request_method = models.CharField(max_length=8, verbose_name="请求方式", null=True, blank=True,
|
||||
help_text="请求方式")
|
||||
request_msg = models.TextField(verbose_name="操作说明", null=True, blank=True, help_text="操作说明")
|
||||
request_ip = models.CharField(max_length=32, verbose_name="请求ip地址", null=True, blank=True,
|
||||
help_text="请求ip地址")
|
||||
request_browser = models.CharField(max_length=64, verbose_name="请求浏览器", null=True, blank=True,
|
||||
help_text="请求浏览器")
|
||||
response_code = models.CharField(max_length=32, verbose_name="响应状态码", null=True, blank=True,
|
||||
help_text="响应状态码")
|
||||
request_os = models.CharField(max_length=64, verbose_name="操作系统", null=True, blank=True, help_text="操作系统")
|
||||
json_result = models.TextField(verbose_name="返回信息", null=True, blank=True, help_text="返回信息")
|
||||
status = models.BooleanField(default=False, verbose_name="响应状态", help_text="响应状态")
|
||||
|
||||
class Meta:
|
||||
db_table = 'system_operation_log'
|
||||
verbose_name = '操作日志'
|
||||
verbose_name_plural = verbose_name
|
||||
ordering = ('-create_datetime',)
|
||||
|
||||
|
||||
def media_file_name(instance, filename):
|
||||
h = instance.md5sum
|
||||
basename, ext = os.path.splitext(filename)
|
||||
return os.path.join('files', h[0:1], h[1:2], h + ext.lower())
|
||||
|
||||
|
||||
class File(CoreModel):
|
||||
name = models.CharField(max_length=255, null=True, blank=True, verbose_name="实际名称", help_text="实际名称")
|
||||
save_name = models.CharField(max_length=255, null=True, blank=True, verbose_name="存储名称", help_text="存储名称")
|
||||
url = models.FileField(upload_to=media_file_name)
|
||||
size = models.BigIntegerField(null=True, blank=True, verbose_name="大小", help_text="大小")
|
||||
md5sum = models.CharField(max_length=36, blank=True, verbose_name="文件md5", help_text="文件md5")
|
||||
|
||||
class Meta:
|
||||
db_table = 'system_file'
|
||||
verbose_name = '文件管理'
|
||||
verbose_name_plural = verbose_name
|
||||
ordering = ('-create_datetime',)
|
||||
|
||||
|
||||
class Area(CoreModel):
|
||||
name = models.CharField(max_length=100, verbose_name="名称", help_text="名称")
|
||||
code = models.CharField(max_length=20, verbose_name="地区编码", help_text="地区编码", unique=True, db_index=True)
|
||||
level = models.BigIntegerField(verbose_name="地区层级(1省份 2城市 3区县 4乡级)",
|
||||
help_text="地区层级(1省份 2城市 3区县 4乡级)")
|
||||
pinyin = models.CharField(max_length=255, verbose_name="拼音", help_text="拼音")
|
||||
initials = models.CharField(max_length=20, verbose_name="首字母", help_text="首字母")
|
||||
enable = models.BooleanField(default=True, verbose_name="是否启用", help_text="是否启用")
|
||||
pcode = models.ForeignKey(to='self', verbose_name='父地区编码', to_field="code", on_delete=models.CASCADE,
|
||||
db_constraint=False, null=True, blank=True, help_text="父地区编码")
|
||||
|
||||
class Meta:
|
||||
db_table = "system_area"
|
||||
verbose_name = '地区表'
|
||||
verbose_name_plural = verbose_name
|
||||
ordering = ('code',)
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.name}"
|
||||
|
||||
|
||||
class ApiWhiteList(CoreModel):
|
||||
url = models.CharField(max_length=200, help_text="url地址", verbose_name="url")
|
||||
METHOD_CHOICES = (
|
||||
(0, "GET"),
|
||||
(1, "POST"),
|
||||
(2, "PUT"),
|
||||
(3, "DELETE"),
|
||||
)
|
||||
method = models.IntegerField(default=0, verbose_name="接口请求方法", null=True, blank=True,
|
||||
help_text="接口请求方法")
|
||||
enable_datasource = models.BooleanField(default=True, verbose_name="激活数据权限", help_text="激活数据权限",
|
||||
blank=True)
|
||||
|
||||
class Meta:
|
||||
db_table = "system_api_white_list"
|
||||
verbose_name = '接口白名单'
|
||||
verbose_name_plural = verbose_name
|
||||
ordering = ('-create_datetime',)
|
||||
|
||||
|
||||
class SystemConfig(CoreModel):
|
||||
parent = models.ForeignKey(to='self', verbose_name='父级', on_delete=models.CASCADE,
|
||||
db_constraint=False, null=True, blank=True, help_text="父级")
|
||||
title = models.CharField(max_length=50, verbose_name="标题", help_text="标题")
|
||||
key = models.CharField(max_length=20, verbose_name="键", help_text="键")
|
||||
value = models.JSONField(max_length=100, verbose_name="值", help_text="值", null=True, blank=True)
|
||||
status = models.BooleanField(default=False, verbose_name="启用状态", help_text="启用状态")
|
||||
data_options = models.JSONField(verbose_name="数据options", help_text="数据options", null=True, blank=True)
|
||||
FORM_ITEM_TYPE_LIST = (
|
||||
(0, 'text'),
|
||||
(1, 'textarea'),
|
||||
(2, 'number'),
|
||||
(3, 'select'),
|
||||
(4, 'radio'),
|
||||
(5, 'checkbox'),
|
||||
(6, 'date'),
|
||||
(7, 'datetime'),
|
||||
(8, 'time'),
|
||||
(9, 'imgs'),
|
||||
(10, 'files'),
|
||||
(11, 'array'),
|
||||
(12, 'foreignkey'),
|
||||
(13, 'manytomany'),
|
||||
)
|
||||
form_item_type = models.IntegerField(choices=FORM_ITEM_TYPE_LIST, verbose_name="表单类型", help_text="表单类型",
|
||||
default=0,
|
||||
blank=True)
|
||||
rule = models.JSONField(null=True, blank=True, verbose_name="校验规则", help_text="校验规则")
|
||||
placeholder = models.CharField(max_length=50, null=True, blank=True, verbose_name="提示信息", help_text="提示信息")
|
||||
setting = models.JSONField(null=True, blank=True, verbose_name="配置", help_text="配置")
|
||||
|
||||
class Meta:
|
||||
db_table = "system_config"
|
||||
verbose_name = '系统配置表'
|
||||
verbose_name_plural = verbose_name
|
||||
ordering = ('sort',)
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.title}"
|
||||
|
||||
|
||||
class LoginLog(CoreModel):
|
||||
LOGIN_TYPE_CHOICES = (
|
||||
(1, '普通登录'),
|
||||
)
|
||||
username = models.CharField(max_length=32, verbose_name="登录用户名", null=True, blank=True, help_text="登录用户名")
|
||||
ip = models.CharField(max_length=32, verbose_name="登录ip", null=True, blank=True, help_text="登录ip")
|
||||
agent = models.TextField(verbose_name="agent信息", null=True, blank=True, help_text="agent信息")
|
||||
browser = models.CharField(max_length=200, verbose_name="浏览器名", null=True, blank=True, help_text="浏览器名")
|
||||
os = models.CharField(max_length=200, verbose_name="操作系统", null=True, blank=True, help_text="操作系统")
|
||||
continent = models.CharField(max_length=50, verbose_name="州", null=True, blank=True, help_text="州")
|
||||
country = models.CharField(max_length=50, verbose_name="国家", null=True, blank=True, help_text="国家")
|
||||
province = models.CharField(max_length=50, verbose_name="省份", null=True, blank=True, help_text="省份")
|
||||
city = models.CharField(max_length=50, verbose_name="城市", null=True, blank=True, help_text="城市")
|
||||
district = models.CharField(max_length=50, verbose_name="县区", null=True, blank=True, help_text="县区")
|
||||
isp = models.CharField(max_length=50, verbose_name="运营商", null=True, blank=True, help_text="运营商")
|
||||
area_code = models.CharField(max_length=50, verbose_name="区域代码", null=True, blank=True, help_text="区域代码")
|
||||
country_english = models.CharField(max_length=50, verbose_name="英文全称", null=True, blank=True,
|
||||
help_text="英文全称")
|
||||
country_code = models.CharField(max_length=50, verbose_name="简称", null=True, blank=True, help_text="简称")
|
||||
longitude = models.CharField(max_length=50, verbose_name="经度", null=True, blank=True, help_text="经度")
|
||||
latitude = models.CharField(max_length=50, verbose_name="纬度", null=True, blank=True, help_text="纬度")
|
||||
login_type = models.IntegerField(default=1, choices=LOGIN_TYPE_CHOICES, verbose_name="登录类型",
|
||||
help_text="登录类型")
|
||||
|
||||
class Meta:
|
||||
db_table = 'system_login_log'
|
||||
verbose_name = '登录日志'
|
||||
verbose_name_plural = verbose_name
|
||||
ordering = ('-create_datetime',)
|
||||
|
||||
|
||||
class GeneratorTemplate(CoreModel):
|
||||
name = models.CharField(null=False, max_length=64, verbose_name="模板名称", help_text="模板名称")
|
||||
code = models.CharField(max_length=32, verbose_name="模板编码", help_text="模板编码")
|
||||
form_info = models.TextField(verbose_name="表单信息", help_text="表单信息")
|
||||
table_info = models.TextField(verbose_name="表格信息", help_text="表格信息")
|
||||
has_menu = models.BooleanField(default=False, verbose_name="是否已经生成菜单", help_text="是否已经生成菜单")
|
||||
|
||||
class Meta:
|
||||
db_table = "system_generator_template"
|
||||
verbose_name = '代码生成器模板'
|
||||
verbose_name_plural = verbose_name
|
||||
ordering = ('-create_datetime',)
|
||||
|
||||
|
||||
|
||||
#-------------------------------------------------追加----------------------------------------------------
|
||||
|
||||
|
||||
|
||||
class VDESData(models.Model):
|
||||
"""本船VDES信息模型"""
|
||||
mmsi = models.CharField(max_length=15, verbose_name="MMSI号码", unique=True, help_text="船舶唯一识别码") #唯一
|
||||
latitude = models.FloatField(verbose_name="纬度", help_text="船舶当前位置纬度")
|
||||
longitude = models.FloatField(verbose_name="经度", help_text="船舶当前位置经度")
|
||||
speed = models.FloatField(verbose_name="航速", help_text="航速(节)", null=True, blank=True)
|
||||
course = models.FloatField(verbose_name="航向", help_text="航向(度)", null=True, blank=True)
|
||||
heading = models.FloatField(verbose_name="船首向", help_text="船首向(度)", null=True, blank=True)
|
||||
nav_status = models.IntegerField(verbose_name="航行状态", null=True, blank=True, help_text="0=机动船在航,1=锚泊等")
|
||||
timestamp = models.DateTimeField(verbose_name="时间戳", help_text="AIS数据更新时间")
|
||||
ship_name = models.CharField(max_length=100, verbose_name="船名", null=True, blank=True)
|
||||
ship_type = models.IntegerField(verbose_name="船舶类型", null=True, blank=True, help_text="1-99=各种船舶类型")
|
||||
draught = models.FloatField(verbose_name="吃水", null=True, blank=True, help_text="单位:米")
|
||||
destination = models.CharField(max_length=100, verbose_name="目的地", null=True, blank=True)
|
||||
eta = models.DateTimeField(verbose_name="预计到达时间", null=True, blank=True)
|
||||
|
||||
class Meta:
|
||||
db_table = "system_vdes_data"
|
||||
verbose_name = 'VDES数据'
|
||||
verbose_name_plural = verbose_name
|
||||
ordering = ('-timestamp',) # 默认按时间倒序排列
|
||||
|
||||
|
||||
class ShipLatestStatus(models.Model):
|
||||
mmsi = models.CharField(max_length=15, verbose_name="MMSI号码", unique=True, help_text="船舶唯一识别码") #唯一
|
||||
latitude = models.FloatField(verbose_name="纬度", help_text="船舶当前位置纬度")
|
||||
longitude = models.FloatField(verbose_name="经度", help_text="船舶当前位置经度")
|
||||
speed = models.FloatField(verbose_name="航速", help_text="航速(节)", null=True, blank=True)
|
||||
course = models.FloatField(verbose_name="航向", help_text="航向(度)", null=True, blank=True)
|
||||
heading = models.FloatField(verbose_name="船首向", help_text="船首向(度)", null=True, blank=True)
|
||||
nav_status = models.IntegerField(verbose_name="航行状态", null=True, blank=True, help_text="0=机动船在航,1=锚泊等")
|
||||
timestamp = models.DateTimeField(verbose_name="时间戳", help_text="AIS数据更新时间")
|
||||
ship_name = models.CharField(max_length=100, verbose_name="船名", null=True, blank=True)
|
||||
ship_type = models.IntegerField(verbose_name="船舶类型", null=True, blank=True, help_text="1-99=各种船舶类型")
|
||||
draught = models.FloatField(verbose_name="吃水", null=True, blank=True, help_text="单位:米")
|
||||
destination = models.CharField(max_length=100, verbose_name="目的地", null=True, blank=True)
|
||||
eta = models.DateTimeField(verbose_name="预计到达时间", null=True, blank=True)
|
||||
# 添加虚拟字段:不写入数据库,但可以在 raw 查询中赋值
|
||||
distance_km = None
|
||||
class Meta:
|
||||
db_table = "ship_latest_status"
|
||||
ordering = ('-timestamp',) # 默认按时间倒序排列
|
||||
|
||||
|
||||
class GpsStatus(models.Model):
|
||||
utc_time = models.DateTimeField(verbose_name="UTC时间")
|
||||
longitude = models.FloatField(verbose_name="经度")
|
||||
latitude = models.FloatField(verbose_name="纬度")
|
||||
altitude = models.FloatField(verbose_name="海拔高度")
|
||||
used_satellites = models.IntegerField(verbose_name="定位卫星数")
|
||||
gps_satellites = models.IntegerField(verbose_name="GPS卫星数")
|
||||
beidou_satellites = models.IntegerField(verbose_name="北斗卫星数")
|
||||
gps_status = models.IntegerField(verbose_name="GPS状态")
|
||||
fix_mode = models.IntegerField(verbose_name="定位模式")
|
||||
hdop = models.FloatField(verbose_name="HDOP精度")
|
||||
speed = models.FloatField(verbose_name="运动速度")
|
||||
direction = models.FloatField(verbose_name="运动方向")
|
||||
|
||||
class Meta:
|
||||
db_table = "gps_status"
|
||||
ordering = ('-utc_time',)
|
||||
|
||||
|
||||
class Content(models.Model):
|
||||
Id = models.AutoField(primary_key=True, verbose_name="自增Id", help_text="自增Id")
|
||||
SendId = models.BigIntegerField(verbose_name="发送人Id", null=True, help_text="发送人Id")
|
||||
ReciverId = models.BigIntegerField(verbose_name="接收人Id", help_text="接收人Id")
|
||||
Content = models.TextField(verbose_name="聊天内容", help_text="聊天内容")
|
||||
Type = models.IntegerField(verbose_name="内容类型", help_text="内容类型:0html文本,1图片,2视频,3文件")
|
||||
State = models.IntegerField(verbose_name="是否发送成功", help_text="是否发送成功:0否,1是")
|
||||
NoCode = models.CharField(max_length=255, verbose_name="时间戳", help_text="时间戳")
|
||||
CreateDateUtc = models.CharField(max_length=255, verbose_name="创建时间", help_text="创建时间")
|
||||
Title = models.CharField(max_length=255, null=True, blank=True, verbose_name="拓展功能字段", help_text="拓展功能字段")
|
||||
Description = models.CharField(max_length=255, null=True, blank=True, verbose_name="拓展功能字段", help_text="拓展功能字段")
|
||||
Label = models.CharField(max_length=255, null=True, blank=True, verbose_name="拓展功能字段", help_text="拓展功能字段")
|
||||
Thumbnail = models.CharField(max_length=255, null=True, blank=True, verbose_name="拓展功能字段", help_text="拓展功能字段")
|
||||
ReadFlag = models.BooleanField(verbose_name="是否阅读", help_text="是否阅读:0否,1是")
|
||||
Avatar = models.CharField(max_length=500, null=True, blank=True, verbose_name="头像", help_text="头像")
|
||||
SoundStatus = models.IntegerField(default=0, verbose_name="音频播放状态", help_text="音频播放状态:0未播放,1播放中,2已播放")
|
||||
|
||||
class Meta:
|
||||
db_table = "content"
|
||||
verbose_name = "内容表"
|
||||
verbose_name_plural = verbose_name
|
||||
ordering = ('-CreateDateUtc',)
|
||||
|
||||
|
||||
class Contacts(models.Model):
|
||||
Id = models.AutoField(primary_key=True, verbose_name="Id", help_text="Id,自增")
|
||||
Name = models.CharField(max_length=255, null=True, blank=True, verbose_name="备注", help_text="备注")
|
||||
MMSI = models.CharField(max_length=255, verbose_name="MMSI号码", help_text="MMSI号码")
|
||||
Mobile = models.CharField(max_length=255, null=True, blank=True, verbose_name="手机号", help_text="手机号")
|
||||
OnlineState = models.BooleanField(null=True, blank=True, verbose_name="在线状态", help_text="在线状态")
|
||||
Region = models.CharField(max_length=255, null=True, blank=True, verbose_name="地区", help_text="地区")
|
||||
Avatar = models.CharField(max_length=500, null=True, blank=True, verbose_name="头像", help_text="头像")
|
||||
HistorySessionList = models.TextField(null=True, blank=True, verbose_name="历史会话列表", help_text="历史会话列表")
|
||||
OutTradeNo = models.CharField(max_length=255, null=True, blank=True, verbose_name="socket.id", help_text="socket.id")
|
||||
NoCode = models.CharField(max_length=255, null=True, blank=True, verbose_name="时间戳", help_text="时间戳")
|
||||
|
||||
class Meta:
|
||||
db_table = "contacts"
|
||||
verbose_name = "通讯录"
|
||||
verbose_name_plural = verbose_name
|
||||
ordering = ('-Id',)
|
||||
unique_together = ('Id', 'MMSI')
|
||||
|
||||
|
||||
class HistorySession(models.Model):
|
||||
Id = models.AutoField(primary_key=True, verbose_name="Id", help_text="Id,自增")
|
||||
FormId = models.BigIntegerField(verbose_name="来自用户Id", help_text="来自用户Id")
|
||||
Name = models.CharField(max_length=255, null=True, blank=True, verbose_name="备注", help_text="备注")
|
||||
MMSI = models.CharField(max_length=255, verbose_name="MMSI号码", help_text="MMSI号码")
|
||||
Mobile = models.CharField(max_length=255, null=True, blank=True, verbose_name="手机号", help_text="手机号")
|
||||
Region = models.CharField(max_length=255, null=True, blank=True, verbose_name="地区", help_text="地区")
|
||||
Avatar = models.CharField(max_length=500, null=True, blank=True, verbose_name="头像", help_text="头像")
|
||||
ById = models.BigIntegerField(verbose_name="归属用户Id", help_text="归属用户Id", default=1)
|
||||
|
||||
class Meta:
|
||||
db_table = "history_session"
|
||||
verbose_name = "历史会话表"
|
||||
verbose_name_plural = verbose_name
|
||||
ordering = ('-Id',)
|
||||
unique_together = ('Id', 'MMSI')
|
||||
|
||||
#记录是否获取到vdes消息
|
||||
class MessageFlag(models.Model):
|
||||
has_new_message = models.BooleanField(default=False) # 是否有新消息
|
||||
|
||||
class Meta:
|
||||
db_table = "message_flag"
|
||||
65
code/system/router.py
Normal file
65
code/system/router.py
Normal file
@@ -0,0 +1,65 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# @Time : 2022/5/10 00:02
|
||||
# @Author : 臧成龙
|
||||
# @FileName: router.py
|
||||
# @Software: PyCharm
|
||||
|
||||
from ninja import Router
|
||||
from system.apis.dept import router as dept_router
|
||||
from system.apis.post import router as post_router
|
||||
from system.apis.login import router as login_router
|
||||
from system.apis.menu import router as menu_router
|
||||
from system.apis.role import router as role_router
|
||||
from system.apis.button import router as button_router
|
||||
from system.apis.menu_button import router as menu_button_router
|
||||
from system.apis.user import router as user_router
|
||||
from system.apis.data_dict.dict import router as dict_router
|
||||
from system.apis.data_dict.dict_item import router as dict_item_router
|
||||
from system.apis.data_dict.category_dict import router as category_dict_router
|
||||
from system.apis.log.login_log import router as login_log_router
|
||||
from system.apis.log.operation_log import router as operation_log_router
|
||||
from system.apis.celery_crontab import router as celery_crontab_router
|
||||
from system.apis.celery_interval import router as celery_interval_router
|
||||
from system.apis.celery_periodic import router as celery_periodic_router
|
||||
from system.apis.log.celery_log import router as celery_log_router
|
||||
from system.apis.file import router as file_router
|
||||
from system.apis.monitor import router as monitor_router
|
||||
from system.apis.menu_column import router as menu_column_field_router
|
||||
from system.apis.code_generator import router as generator_template_router
|
||||
from system.apis.home import router as home_router
|
||||
from system.apis.vdes.header import router as header_router
|
||||
|
||||
from system.apis.vdes.chat.common import router as chat_common_router
|
||||
from system.apis.vdes.chat.historySession import router as chat_history_session_router
|
||||
from system.apis.vdes.chat.contacts import router as chat_contacts_router
|
||||
from system.apis.vdes.chat.content import router as chat_content_router
|
||||
|
||||
system_router = Router()
|
||||
system_router.add_router('/', dept_router, tags=["Dept"])
|
||||
system_router.add_router('/', post_router, tags=["Post"])
|
||||
system_router.add_router('/', login_router, tags=["Login"])
|
||||
system_router.add_router('/', menu_router, tags=["Menu"])
|
||||
system_router.add_router('/', role_router, tags=["Role"])
|
||||
system_router.add_router('/', button_router, tags=["Button"])
|
||||
system_router.add_router('/', menu_button_router, tags=["MenuButton"])
|
||||
system_router.add_router('/', user_router, tags=["User"])
|
||||
system_router.add_router('/', dict_router, tags=["Dict"])
|
||||
system_router.add_router('/', dict_item_router, tags=["DictItem"])
|
||||
system_router.add_router('/', category_dict_router, tags=["CategoryDict"])
|
||||
system_router.add_router('/', login_log_router, tags=["LoginLog"])
|
||||
system_router.add_router('/', operation_log_router, tags=["OperationLog"])
|
||||
system_router.add_router('/', celery_crontab_router, tags=["CeleryCrontab"])
|
||||
system_router.add_router('/', celery_interval_router, tags=["CeleryInterval"])
|
||||
system_router.add_router('/', celery_periodic_router, tags=["CeleryPeriodic"])
|
||||
system_router.add_router('/', celery_log_router, tags=["CeleryLog"])
|
||||
system_router.add_router('/', file_router, tags=["File"])
|
||||
system_router.add_router('/', monitor_router, tags=["Monitor"])
|
||||
system_router.add_router('/', menu_column_field_router, tags=["MenuColumnField"])
|
||||
system_router.add_router('/', generator_template_router, tags=["GeneratorTemplate"])
|
||||
system_router.add_router('/', home_router, tags=["Home"])
|
||||
system_router.add_router('/', header_router, tags=["Header"])
|
||||
|
||||
system_router.add_router('/', chat_common_router, tags=["ChatCommon"])
|
||||
system_router.add_router('/', chat_history_session_router, tags=["ChatHistorySession"])
|
||||
system_router.add_router('/', chat_contacts_router, tags=["ChatContacts"])
|
||||
system_router.add_router('/', chat_content_router, tags=["ChatContent"])
|
||||
80
code/system/services/vdes_data_receive.py
Normal file
80
code/system/services/vdes_data_receive.py
Normal file
@@ -0,0 +1,80 @@
|
||||
import socket
|
||||
import logging
|
||||
import time
|
||||
import json
|
||||
import struct
|
||||
import threading
|
||||
from fuadmin import settings
|
||||
from system import apps
|
||||
from vdes_utils.vdes_data_parsing import VdesDataParsing
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
class VDESSocketMonitor:
|
||||
_instance = None
|
||||
_lock = threading.Lock()
|
||||
|
||||
def __new__(cls):
|
||||
with cls._lock:
|
||||
if not cls._instance:
|
||||
cls._instance = super().__new__(cls)
|
||||
cls._instance._initialized = False
|
||||
return cls._instance
|
||||
|
||||
def __init__(self):
|
||||
if self._initialized:
|
||||
return
|
||||
self._initialized = True
|
||||
|
||||
self.host = getattr(settings, 'VDES_MONITOR_HOST', 'localhost')
|
||||
self.port = getattr(settings, 'VDES_MONITOR_PORT', 8083)
|
||||
self.buffer_size = getattr(settings, 'VDES_BUFFER_SIZE', 4096)
|
||||
|
||||
self.running = False
|
||||
self.thread = None
|
||||
self.vdes_data_parsing = VdesDataParsing()
|
||||
logger.info(f"VDES 监控服务初始化完成 - {self.host}:{self.port}")
|
||||
|
||||
def start(self):
|
||||
if self.running:
|
||||
logger.warning("监控服务已在运行中")
|
||||
return
|
||||
|
||||
self.running = True
|
||||
self.thread = threading.Thread(
|
||||
target=self._monitor_loop,
|
||||
name="VDES-Monitor-Thread",
|
||||
daemon=False
|
||||
)
|
||||
self.thread.start()
|
||||
logger.info("VDES 监控服务已启动")
|
||||
|
||||
def stop(self):
|
||||
self.running = False
|
||||
if self.thread and self.thread.is_alive():
|
||||
self.thread.join(timeout=5)
|
||||
logger.info("VDES 监控服务已停止")
|
||||
|
||||
def _monitor_loop(self):
|
||||
while self.running:
|
||||
try:
|
||||
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
|
||||
sock.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)
|
||||
print(f"尝试连接到服务器 {self.host}:{self.port}")
|
||||
sock.connect((self.host, self.port))
|
||||
print(f"成功连接到服务器 {self.host}:{self.port}")
|
||||
while self.running:
|
||||
data = sock.recv(self.buffer_size)
|
||||
if data:
|
||||
# print(data)
|
||||
self.vdes_data_parsing.parse_vdes_data(data)
|
||||
except (ConnectionRefusedError, TimeoutError) as e:
|
||||
print(f"连接失败: {e}")
|
||||
except Exception as e:
|
||||
print(f"连接异常: {e}")
|
||||
|
||||
# 如果服务仍在运行,则等待30秒后重新尝试连接
|
||||
if self.running:
|
||||
print("等待30秒后重新尝试连接...")
|
||||
time.sleep(30)
|
||||
|
||||
9
code/system/tasks.py
Normal file
9
code/system/tasks.py
Normal file
@@ -0,0 +1,9 @@
|
||||
|
||||
from celery.app import task
|
||||
|
||||
from fuadmin.celery import app
|
||||
|
||||
|
||||
@app.task(name="system.tasks.test_task")
|
||||
def test_task():
|
||||
print('test')
|
||||
14
code/system/tests.py
Normal file
14
code/system/tests.py
Normal file
@@ -0,0 +1,14 @@
|
||||
from django.test import TestCase
|
||||
|
||||
# Create your tests here.
|
||||
def debug(func):
|
||||
def wrapper():
|
||||
print("[DEBUG]: enter {}()".format(func.__name__))
|
||||
return func()
|
||||
return wrapper
|
||||
|
||||
@debug
|
||||
def hello():
|
||||
print("hello")
|
||||
|
||||
hello()
|
||||
51
code/utils/core_initialize.py
Normal file
51
code/utils/core_initialize.py
Normal file
@@ -0,0 +1,51 @@
|
||||
# 初始化基类
|
||||
from fuadmin import settings
|
||||
|
||||
|
||||
class CoreInitialize:
|
||||
"""
|
||||
使用方法:继承此类,重写 run方法,在 run 中调用 save 进行数据初始化
|
||||
"""
|
||||
creator_id = None
|
||||
reset = False
|
||||
|
||||
def __init__(self, reset=False, creator_id=None):
|
||||
"""
|
||||
reset: 是否重置初始化数据
|
||||
creator_id: 创建人id
|
||||
"""
|
||||
self.reset = reset or self.reset
|
||||
self.creator_id = creator_id or self.creator_id
|
||||
|
||||
def save(self, obj, data: list, name=None, no_reset=False):
|
||||
name = name or obj._meta.verbose_name
|
||||
print(f"正在初始化[{obj._meta.label} => {name}]")
|
||||
if not no_reset and self.reset and obj not in settings.INITIALIZE_RESET_LIST:
|
||||
try:
|
||||
obj.objects.all().delete()
|
||||
settings.INITIALIZE_RESET_LIST.append(obj)
|
||||
except Exception:
|
||||
pass
|
||||
for ele in data:
|
||||
m2m_dict = {}
|
||||
new_data = {}
|
||||
for key, value in ele.items():
|
||||
# 判断传的 value 为 list 的多对多进行抽离,使用set 进行更新
|
||||
if isinstance(value, list):
|
||||
m2m_dict[key] = value
|
||||
else:
|
||||
new_data[key] = value
|
||||
object, _ = obj.objects.get_or_create(id=ele.get("id"), defaults=new_data)
|
||||
for key, m2m in m2m_dict.items():
|
||||
m2m = list(set(m2m))
|
||||
if m2m and len(m2m) > 0 and m2m[0]:
|
||||
exec(f"""
|
||||
if object.{key}:
|
||||
values_list = object.{key}.all().values_list('id', flat=True)
|
||||
values_list = list(set(list(values_list) + {m2m}))
|
||||
object.{key}.set(values_list)
|
||||
""")
|
||||
print(f"初始化完成[{obj._meta.label} => {name}]")
|
||||
|
||||
def run(self):
|
||||
raise NotImplementedError('.run() must be overridden')
|
||||
113
code/utils/fu_auth.py
Normal file
113
code/utils/fu_auth.py
Normal file
@@ -0,0 +1,113 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# @Time : 2022/6/2 23:19
|
||||
# @Author : 臧成龙
|
||||
# @FileName: fu_auth.py
|
||||
# @Software: PyCharm
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import re
|
||||
from datetime import datetime
|
||||
|
||||
# from django.core.cache import cache
|
||||
from fuadmin.settings import DEMO, SECRET_KEY, WHITE_LIST
|
||||
from ninja.security import HttpBearer
|
||||
from system.models import MenuButton, Users
|
||||
|
||||
from .fu_jwt import FuJwt
|
||||
from .fu_ninja import FuFilters
|
||||
from .usual import get_dept, get_user_info_from_token
|
||||
|
||||
METHOD = {
|
||||
'GET': 0,
|
||||
'POST': 1,
|
||||
'PUT': 2,
|
||||
'DELETE': 3,
|
||||
}
|
||||
|
||||
|
||||
class GlobalAuth(HttpBearer):
|
||||
def authenticate(self, request, token):
|
||||
# 直接放行所有请求
|
||||
return token
|
||||
# jwt = FuJwt(SECRET_KEY)
|
||||
# value = jwt.decode(SECRET_KEY, token)
|
||||
# time_now = int(datetime.now().timestamp())
|
||||
# # 判断token是否过期
|
||||
# if value.valid_to >= time_now:
|
||||
# token_user = value.payload
|
||||
# token_user_id = token_user['id']
|
||||
# user = Users.objects.get(id=token_user_id)
|
||||
# request_path = request.path
|
||||
# request_method = request.method
|
||||
# if DEMO:
|
||||
# # 判断是否在白名单中
|
||||
# if request_path in WHITE_LIST:
|
||||
# return token
|
||||
# if request_method == 'GET':
|
||||
# return token
|
||||
# else:
|
||||
# raise TimeoutError(403, '演示环境')
|
||||
# else:
|
||||
# # 判断是否是超级管理员
|
||||
# if not token_user['is_superuser']:
|
||||
# # 判断是path是否是‘/数字’结尾
|
||||
# result = re.search(r'/\d+$', request_path)
|
||||
# if result:
|
||||
# match_value = result.group()
|
||||
# # 将数字结尾的接口替换成.*? 因为接口中是/{id}
|
||||
# request_path = request_path.replace(match_value, '/*')
|
||||
# # 判断是否在白名单中
|
||||
# if request_path in WHITE_LIST:
|
||||
# return token
|
||||
# else:
|
||||
# menuIds = user.role.values_list('permission__id', flat=True)
|
||||
# queryset = MenuButton.objects.filter(id__in=menuIds, api__regex=request_path,
|
||||
# method=METHOD[request_method])
|
||||
# if queryset.exists():
|
||||
# return token
|
||||
# else:
|
||||
# raise TimeoutError(403, '没有权限')
|
||||
# # cache_token = cache.get(token_user_id)
|
||||
# # if token == cache_token:
|
||||
# return token
|
||||
# else:
|
||||
# raise TimeoutError(401, 'token时间过期')
|
||||
|
||||
|
||||
def data_permission(request, filters: FuFilters):
|
||||
return filters # 直接返回不进行任何权限过滤
|
||||
# user_info = get_user_info_from_token(request)
|
||||
# if user_info['is_superuser']:
|
||||
# return filters
|
||||
# user = Users.objects.get(id=user_info['id'])
|
||||
# data_range_qs = user.role.values_list('data_range', flat=True)
|
||||
# dept_ids = user.role.values_list('dept__id', flat=True)
|
||||
|
||||
# # 如果有多个角色,取数据权限最大的角色
|
||||
# data_range = max(list(data_range_qs))
|
||||
|
||||
# # 仅本人数据权限
|
||||
# if data_range == 0:
|
||||
# filters.creator_id = user_info['id']
|
||||
|
||||
# # 本部门数据权限
|
||||
# if data_range == 1:
|
||||
# filters.belong_dept = user_info['dept']
|
||||
|
||||
# # 本部门及以下数据权限
|
||||
# if data_range == 2:
|
||||
# dept_and_below_ids = get_dept(user_info['dept'])
|
||||
# filters.belong_dept__in = dept_and_below_ids
|
||||
|
||||
# # 自定义数据权限
|
||||
# if data_range == 3:
|
||||
# filters.belong_dept__in = list(dept_ids)
|
||||
|
||||
# # 所有数据权限
|
||||
# if data_range == 4:
|
||||
# pass
|
||||
|
||||
# return filters
|
||||
|
||||
|
||||
|
||||
242
code/utils/fu_crud.py
Normal file
242
code/utils/fu_crud.py
Normal file
@@ -0,0 +1,242 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# @Time : 2022/6/6 14:22
|
||||
# @Author : 臧成龙
|
||||
# @FileName: usual.py
|
||||
# @Software: PyCharm
|
||||
# -*- coding: utf-8 -*-
|
||||
import os
|
||||
from datetime import datetime
|
||||
from urllib.parse import unquote
|
||||
|
||||
import openpyxl
|
||||
from django.http import FileResponse
|
||||
from django.shortcuts import get_object_or_404
|
||||
from fuadmin.settings import BASE_DIR, STATIC_URL
|
||||
from ninja import Schema
|
||||
from openpyxl import load_workbook
|
||||
|
||||
from .fu_auth import data_permission
|
||||
from .fu_ninja import FuFilters
|
||||
from .fu_response import FuResponse
|
||||
from .usual import get_user_info_from_token
|
||||
|
||||
|
||||
class ImportSchema(Schema):
|
||||
path: str
|
||||
|
||||
|
||||
def create(request, data, model):
|
||||
"""
|
||||
创建新记录的函数。
|
||||
|
||||
参数:
|
||||
- request: 请求对象,用于获取用户信息。
|
||||
- data: 创建记录的数据,可以是字典或者支持dict()方法的对象。
|
||||
- model: Django ORM模型,指定要创建的对象类型。
|
||||
|
||||
返回值:
|
||||
- 创建成功的对象查询集合。
|
||||
"""
|
||||
if not isinstance(data, dict):
|
||||
# 如果data不是字典类型,则转换为字典
|
||||
data = data.dict()
|
||||
user_info = get_user_info_from_token(request)
|
||||
# 从请求中提取用户信息,并添加到数据中作为创建人、修改者和所属部门信息
|
||||
data['creator_id'] = user_info['id']
|
||||
data['modifier'] = user_info['name']
|
||||
data['belong_dept'] = user_info['dept']
|
||||
# 使用提供的模型和数据创建新记录
|
||||
query_set = model.objects.create(**data)
|
||||
return query_set
|
||||
|
||||
|
||||
def batch_create(request, data, model):
|
||||
"""
|
||||
批量创建模型实例。
|
||||
|
||||
参数:
|
||||
- request: HTTP请求对象,用于获取用户信息。
|
||||
- data: 一个包含创建数据的列表,每个元素可以是字典或者具有dict方法的对象。
|
||||
- model: Django模型类,用于实例化和批量创建。
|
||||
|
||||
返回值:
|
||||
- query_set: 批量创建后的模型实例查询集。
|
||||
"""
|
||||
user_info = get_user_info_from_token(request) # 从请求中获取用户信息
|
||||
data_list = []
|
||||
for item in data:
|
||||
if not isinstance(item, dict):
|
||||
item = item.dict() # 如果item不是字典,则转换为字典
|
||||
|
||||
# 为每个创建的数据项添加默认的创建人、修改者和所属部门信息
|
||||
item['creator_id'] = user_info['id']
|
||||
item['modifier'] = user_info['name']
|
||||
item['belong_dept'] = user_info['dept']
|
||||
data_list.append(model(**item)) # 根据字典内容实例化模型对象并添加到列表中
|
||||
query_set = model.objects.bulk_create(data_list) # 批量创建模型实例
|
||||
return query_set
|
||||
|
||||
|
||||
def delete(id, model):
|
||||
"""
|
||||
根据提供的ID和模型删除对象实例。
|
||||
|
||||
参数:
|
||||
- id: 要删除的对象的ID。
|
||||
- model: 对象所属的模型类。
|
||||
|
||||
返回值:
|
||||
- 无返回值。
|
||||
"""
|
||||
# 根据ID和模型获取对象实例,如果找不到则返回404错误页面
|
||||
instance = get_object_or_404(model, id=id)
|
||||
# 删除对象实例
|
||||
instance.delete()
|
||||
pass
|
||||
|
||||
|
||||
def update(request, id, data, model):
|
||||
"""
|
||||
更新给定模型实例的数据。
|
||||
|
||||
参数:
|
||||
- request: HTTP请求对象,用于获取用户信息。
|
||||
- id: 要更新的模型实例的ID。
|
||||
- data: 包含更新数据的实例,应能转换为字典格式。
|
||||
- model: 要更新的模型类。
|
||||
|
||||
返回值:
|
||||
- 更新后的模型实例。
|
||||
"""
|
||||
dict_data = data.dict() # 将data转换为字典格式
|
||||
user_info = get_user_info_from_token(request) # 从请求中获取用户信息
|
||||
# 为更新的数据添加修改者信息
|
||||
dict_data['modifier'] = user_info['name']
|
||||
instance = get_object_or_404(model, id=id) # 获取指定ID的模型实例
|
||||
# 遍历字典,将更新的数据设置到模型实例上
|
||||
for attr, value in dict_data.items():
|
||||
setattr(instance, attr, value)
|
||||
instance.save() # 保存更新
|
||||
return instance # 返回更新后的实例
|
||||
|
||||
|
||||
def retrieve(request, model, filters: FuFilters = FuFilters()):
|
||||
"""
|
||||
根据提供的过滤条件从数据库中检索模型实例。
|
||||
|
||||
参数:
|
||||
- request: HttpRequest对象,用于获取请求信息。
|
||||
- model: Django模型类,指定要检索的数据模型。
|
||||
- filters: FuFilters类的实例,包含过滤条件。默认为FuFilters(),即无条件过滤。
|
||||
|
||||
返回值:
|
||||
- query_set: 一个Django QuerySet对象,包含根据过滤条件检索到的模型实例。
|
||||
"""
|
||||
# 根据请求和过滤条件应用数据权限控制
|
||||
filters = data_permission(request, filters)
|
||||
if filters is not None:
|
||||
# 将filters中的空字符串值转换为None
|
||||
for attr, value in filters.__dict__.items():
|
||||
if getattr(filters, attr) == '':
|
||||
setattr(filters, attr, None)
|
||||
# 使用过滤条件查询模型实例
|
||||
query_set = model.objects.filter(**filters.dict(exclude_none=True))
|
||||
else:
|
||||
# 如果没有有效的过滤条件,则返回所有模型实例
|
||||
query_set = model.objects.all()
|
||||
return query_set
|
||||
|
||||
|
||||
def export_data(request, model, scheme, export_fields):
|
||||
"""
|
||||
导出数据为Excel文件。
|
||||
|
||||
参数:
|
||||
- request: HttpRequest对象,表示客户端请求。
|
||||
- model: Django模型类,指定要导出数据的模型。
|
||||
- scheme: 表示数据转换规则的对象,用于将ORM对象转换为字典。
|
||||
- export_fields: 包含要导出的字段名的列表。
|
||||
|
||||
返回值:
|
||||
- FileResponse对象,提供下载Excel文件。
|
||||
"""
|
||||
|
||||
title_dict = {}
|
||||
# 根据export_fields列表获取字段的显示名称
|
||||
for field in export_fields:
|
||||
field_obj = getattr(model, field).field
|
||||
title_dict[field] = field_obj.help_text
|
||||
|
||||
qs = retrieve(request, model)
|
||||
list_data = []
|
||||
# 将查询集中的每一项转换为指定格式的字典
|
||||
for qs_item in qs:
|
||||
qs_item = scheme.from_orm(qs_item)
|
||||
dict_data = {}
|
||||
for item, value in title_dict.items():
|
||||
dict_data[value] = getattr(qs_item, item)
|
||||
list_data.append(dict_data)
|
||||
|
||||
# 创建并初始化Excel工作簿
|
||||
wb = openpyxl.Workbook()
|
||||
ws = wb.active
|
||||
# 向Excel中写入数据
|
||||
for index, data in enumerate(list_data):
|
||||
if index == 0:
|
||||
# 写入表头
|
||||
ws.append(list(data.keys()))
|
||||
ws.append(list(data.values()))
|
||||
|
||||
# 生成唯一的文件名并设置文件路径
|
||||
file_name = datetime.now().strftime('%Y%m%d%H%M%S%f') + '.xlsx'
|
||||
current_ymd = datetime.now().strftime('%Y%m%d')
|
||||
file_path = os.path.join(STATIC_URL, current_ymd)
|
||||
file_url = os.path.join(file_path, file_name)
|
||||
# 确保导出文件的目录存在
|
||||
if not os.path.exists(file_path):
|
||||
os.makedirs(file_path)
|
||||
|
||||
# 保存Excel文件到指定路径
|
||||
wb.save(file_url)
|
||||
# 返回供下载的文件响应
|
||||
return FileResponse(open(file_url, "rb"), as_attachment=True)
|
||||
|
||||
|
||||
def import_data(request, model, scheme, data, import_fields):
|
||||
"""
|
||||
导入数据到指定模型
|
||||
|
||||
参数:
|
||||
- request: HttpRequest对象,表示客户端请求
|
||||
- model: Django模型类,数据将被导入到这个模型
|
||||
- scheme: 一个函数,用于根据给定的数据字典创建模型实例
|
||||
- data: 包含要导入文件信息的对象,比如上传的Excel文件
|
||||
- import_fields: 一个列表,指定模型中需要导入的字段名
|
||||
|
||||
返回值:
|
||||
- FuResponse对象,包含导入结果的消息
|
||||
"""
|
||||
title_dict = {} # 字段名与Excel列对应的字典
|
||||
for field in import_fields:
|
||||
field_obj = getattr(model, field).field
|
||||
title_dict[field_obj.help_text] = field_obj.column
|
||||
# 文件路径处理
|
||||
file_path = str(BASE_DIR) + unquote(data.path)
|
||||
# 加载Excel工作簿
|
||||
wb = load_workbook(file_path)
|
||||
ws = wb.active # 获取活动工作表
|
||||
title_value = []
|
||||
for index_row, row in enumerate(ws.values):
|
||||
if index_row == 0:
|
||||
title_value = row # 读取Excel表头
|
||||
else:
|
||||
dict_data = {} # 存储每一行数据转换后的字典
|
||||
for index, cell in enumerate(row):
|
||||
title_cell = title_value[index]
|
||||
value = title_dict.get(title_cell) # 根据表头查找对应字段
|
||||
if value is not None:
|
||||
dict_data[value] = cell
|
||||
print(dict_data) # 打印处理后的数据,用于调试
|
||||
data = scheme(**dict_data) # 根据处理后的字典创建模型实例
|
||||
create(request, data, model) # 在数据库中创建模型实例
|
||||
return FuResponse(msg='导入成功') # 返回成功消息
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user