first commit

This commit is contained in:
lihansani
2026-07-13 15:37:05 +08:00
commit 8b11aa2efc
299 changed files with 206339 additions and 0 deletions

0
code/system/__init__.py Normal file
View File

3
code/system/admin.py Normal file
View File

@@ -0,0 +1,3 @@
from django.contrib import admin
# Register your models here.

View File

View 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}

View 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

View 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

View 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()

View 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='数据库生成成功')

View 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

View 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)

View 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

View 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
View 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
View 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
View 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

View 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

View 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

View 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

View 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
View 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
View 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)

View 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

View 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

View 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
View 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
View 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
View 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='密码重置成功')

View File

@@ -0,0 +1,5 @@
# -*- coding: utf-8 -*-
# @Time : 2022/5/9 23:26
# @Author : 臧成龙
# @FileName: __init__.py.py
# @Software: PyCharm

View File

@@ -0,0 +1,5 @@
# -*- coding: utf-8 -*-
# @Time : 2022/5/9 23:26
# @Author : 臧成龙
# @FileName: __init__.py.py
# @Software: PyCharm

View 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

View 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)}

View 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)}

View 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)}

View 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)}

View 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
View 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 监控服务已启动")

View File

View 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

View 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',
}

View 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

View 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

View 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 }});
}};
'''

View 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

View 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>'''

View 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

File diff suppressed because it is too large Load Diff

View File

View 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)

View 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("初始化数据完成!")

View 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("省份数据初始化数据完成!")

View 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
View 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
View 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"])

View 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
View 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
View 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()