Compare commits
16 Commits
fb1e4131ca
...
d99f5ed4a4
| Author | SHA1 | Date |
|---|---|---|
|
|
d99f5ed4a4 | |
|
|
f9a9774af7 | |
|
|
f164168fac | |
|
|
66d24d51af | |
|
|
c8a6ced7a0 | |
|
|
42146f4ff7 | |
|
|
7abebc58d6 | |
|
|
78a781290d | |
|
|
216e82dae7 | |
|
|
7128252315 | |
|
|
54db14937e | |
|
|
986c82f838 | |
|
|
434002ba98 | |
|
|
a8477ce63e | |
|
|
c4f86cf961 | |
|
|
f79f40b9ad |
|
|
@ -45,5 +45,6 @@ class DeptFilterSet(filters.FilterSet):
|
|||
model = Dept
|
||||
fields = {
|
||||
'type': ['exact', 'in'],
|
||||
'name': ['exact', 'in', 'contains']
|
||||
'name': ['exact', 'in', 'contains'],
|
||||
"parent": ['exact', 'isnull'],
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,100 @@
|
|||
# Generated by Django 4.2.19 on 2025-02-23 02:59
|
||||
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('system', '0006_auto_20241213_1249'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='dept',
|
||||
name='create_by',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_create_by', to=settings.AUTH_USER_MODEL, verbose_name='创建人'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='dept',
|
||||
name='update_by',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_update_by', to=settings.AUTH_USER_MODEL, verbose_name='最后编辑人'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='dictionary',
|
||||
name='create_by',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_create_by', to=settings.AUTH_USER_MODEL, verbose_name='创建人'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='dictionary',
|
||||
name='update_by',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_update_by', to=settings.AUTH_USER_MODEL, verbose_name='最后编辑人'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='dicttype',
|
||||
name='create_by',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_create_by', to=settings.AUTH_USER_MODEL, verbose_name='创建人'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='dicttype',
|
||||
name='update_by',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_update_by', to=settings.AUTH_USER_MODEL, verbose_name='最后编辑人'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='file',
|
||||
name='create_by',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_create_by', to=settings.AUTH_USER_MODEL, verbose_name='创建人'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='file',
|
||||
name='update_by',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_update_by', to=settings.AUTH_USER_MODEL, verbose_name='最后编辑人'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='myschedule',
|
||||
name='create_by',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_create_by', to=settings.AUTH_USER_MODEL, verbose_name='创建人'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='myschedule',
|
||||
name='update_by',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_update_by', to=settings.AUTH_USER_MODEL, verbose_name='最后编辑人'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='post',
|
||||
name='create_by',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_create_by', to=settings.AUTH_USER_MODEL, verbose_name='创建人'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='post',
|
||||
name='update_by',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_update_by', to=settings.AUTH_USER_MODEL, verbose_name='最后编辑人'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='role',
|
||||
name='create_by',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_create_by', to=settings.AUTH_USER_MODEL, verbose_name='创建人'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='role',
|
||||
name='update_by',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_update_by', to=settings.AUTH_USER_MODEL, verbose_name='最后编辑人'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='user',
|
||||
name='belong_dept',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_belong_dept', to='system.dept', verbose_name='所属部门'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='user',
|
||||
name='create_by',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_create_by', to=settings.AUTH_USER_MODEL, verbose_name='创建人'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='user',
|
||||
name='update_by',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_update_by', to=settings.AUTH_USER_MODEL, verbose_name='最后编辑人'),
|
||||
),
|
||||
]
|
||||
|
|
@ -300,7 +300,10 @@ class ComplexQueryMixin:
|
|||
serializer = self.get_serializer(page, many=True)
|
||||
return self.get_paginated_response(serializer.data)
|
||||
serializer = self.get_serializer(new_qs, many=True)
|
||||
return Response(serializer.data)
|
||||
rdata = serializer.data
|
||||
if hasattr(self, 'add_info_for_list'):
|
||||
rdata = self.add_info_for_list(rdata)
|
||||
return Response(rdata)
|
||||
|
||||
class MyLoggingMixin(object):
|
||||
"""Mixin to log requests"""
|
||||
|
|
|
|||
|
|
@ -150,7 +150,33 @@ class BaseModel(models.Model):
|
|||
raise
|
||||
time.sleep(0.1 * (attempt + 1))
|
||||
|
||||
|
||||
|
||||
@classmethod
|
||||
def locked_get_or_create(cls, defaults: dict, **kwargs):
|
||||
"""
|
||||
仅用于事务内
|
||||
并发安全的 get_or_create
|
||||
"""
|
||||
if not connection.in_atomic_block:
|
||||
raise RuntimeError("locked_get_or_create 必须在事务中调用")
|
||||
|
||||
defaults = defaults or {}
|
||||
|
||||
qs = cls.objects.select_for_update().filter(**kwargs)
|
||||
|
||||
cnt = qs.count()
|
||||
if cnt > 1:
|
||||
raise RuntimeError(
|
||||
f"{cls.__name__} 数据异常:定位条件 {kwargs} 命中 {cnt} 条"
|
||||
)
|
||||
|
||||
if cnt == 1:
|
||||
return qs.get(), False
|
||||
|
||||
params = {**kwargs, **defaults}
|
||||
obj = cls.objects.create(**params)
|
||||
return obj, True
|
||||
|
||||
def handle_parent(self):
|
||||
pass
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
import json
|
||||
import logging
|
||||
from server.settings import get_sysconfig
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
from aip import AipSpeech
|
||||
from django.conf import settings
|
||||
import uuid
|
||||
import os
|
||||
|
|
@ -17,6 +16,7 @@ def generate_voice(msg: str, per: int = 0):
|
|||
str: 地址
|
||||
dict: result
|
||||
"""
|
||||
from aip import AipSpeech
|
||||
client = AipSpeech(settings.BD_SP_ID, settings.BD_SP_KEY, settings.BD_SP_SECRET)
|
||||
result = client.synthesis(msg, 'zh', 1, {'vol': 5, 'spd': 5, 'per': per})
|
||||
# 识别正确返回语音二进制 错误则返回dict 参照下面错误码
|
||||
|
|
|
|||
|
|
@ -1,21 +1,12 @@
|
|||
|
||||
import os
|
||||
import cv2
|
||||
from django.http import HttpResponse
|
||||
from apps.utils.errors import SIGN_MAKE_FAIL
|
||||
from server.settings import BASE_DIR
|
||||
import numpy as np
|
||||
from rest_framework.response import Response
|
||||
from rest_framework.exceptions import ParseError
|
||||
from apps.utils.viewsets import CustomGenericViewSet
|
||||
from apps.utils.mixins import CustomCreateModelMixin
|
||||
from apps.utils.serializers import GenSignatureSerializer
|
||||
from rest_framework.views import APIView
|
||||
from rest_framework.decorators import action
|
||||
from rest_framework.serializers import Serializer
|
||||
from django.core.cache import cache
|
||||
import json
|
||||
import requests
|
||||
|
||||
|
||||
class SignatureViewSet(CustomCreateModelMixin, CustomGenericViewSet):
|
||||
|
|
@ -29,6 +20,8 @@ class SignatureViewSet(CustomCreateModelMixin, CustomGenericViewSet):
|
|||
|
||||
照片生成透明签名图片
|
||||
"""
|
||||
import cv2
|
||||
import numpy as np
|
||||
path = (BASE_DIR + request.data['path']).replace('\\', '/')
|
||||
try:
|
||||
image = cv2.imread(path, cv2.IMREAD_UNCHANGED)
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ from django.db import migrations, models
|
|||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('wf', '0004_workflow_view_path2'),
|
||||
('wf', '0003_workflow_view_path'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ import random
|
|||
from apps.utils.queryset import get_parent_queryset
|
||||
from apps.wf.tasks import run_task
|
||||
from rest_framework.exceptions import ParseError
|
||||
|
||||
import time
|
||||
|
||||
class WfService(object):
|
||||
@staticmethod
|
||||
|
|
@ -335,6 +335,15 @@ class WfService(object):
|
|||
act_state=Ticket.TICKET_ACT_STATE_DRAFT,
|
||||
belong_dept=handler.belong_dept,
|
||||
ticket_data=save_ticket_data, participant_type=1, participant=handler.id) # 先创建出来
|
||||
|
||||
sn = WfService.get_ticket_sn(ticket.workflow) # 流水号
|
||||
ticket.sn = sn
|
||||
ticket.save()
|
||||
if not transition:
|
||||
return ticket
|
||||
just_created = True # 刚创建的工单不需要校验权限
|
||||
|
||||
if transition and transition.source_state.type == State.STATE_TYPE_START:
|
||||
# 更新title和sn
|
||||
ticket_title = oinfo.get("title", "")
|
||||
title_template = ticket.workflow.title_template
|
||||
|
|
@ -344,13 +353,8 @@ class WfService(object):
|
|||
ticket_title = title_template.format(**all_ticket_data)
|
||||
except KeyError as e:
|
||||
raise ParseError(f"工单标题模板中存在未定义的变量:{e}")
|
||||
sn = WfService.get_ticket_sn(ticket.workflow) # 流水号
|
||||
ticket.sn = sn
|
||||
ticket.title = ticket_title
|
||||
ticket.save()
|
||||
if not transition:
|
||||
return ticket
|
||||
just_created = True # 刚创建的工单不需要校验权限
|
||||
ticket.save(update_fields=["title"])
|
||||
|
||||
source_state = ticket.state
|
||||
source_ticket_data = ticket.ticket_data
|
||||
|
|
@ -502,7 +506,7 @@ class WfService(object):
|
|||
@classmethod
|
||||
def send_ticket_notice(cls, ticketflow:TicketFlow):
|
||||
# 根据ticketflow发送通知
|
||||
Thread(target=send_ticket_notice_t, args=(ticketflow,), daemon=True).start()
|
||||
Thread(target=send_ticket_notice_t, args=(ticketflow.id,), daemon=True).start()
|
||||
|
||||
|
||||
@classmethod
|
||||
|
|
@ -538,11 +542,12 @@ class WfService(object):
|
|||
participant=handler, transition=None)
|
||||
cls.task_ticket(ticket=ticket)
|
||||
|
||||
def send_ticket_notice_t(ticketflow: TicketFlow):
|
||||
def send_ticket_notice_t(ticketflowId: str):
|
||||
"""
|
||||
发送通知
|
||||
"""
|
||||
ticket = ticketflow.ticket
|
||||
time.sleep(3)
|
||||
ticket = TicketFlow.objects.get(id=ticketflowId).ticket
|
||||
params = {'workflow': ticket.workflow.name, 'state': ticket.state.name}
|
||||
if ticket.participant_type == 1:
|
||||
# 发送短信通知
|
||||
|
|
|
|||
|
|
@ -1,12 +0,0 @@
|
|||
SECRET_KEY = 'xx'
|
||||
DEBUG = False
|
||||
DATABASES = {
|
||||
'default': {
|
||||
'ENGINE': 'django.db.backends.postgresql',
|
||||
'NAME': 'xx',
|
||||
'USER': 'postgres',
|
||||
'PASSWORD': 'xx',
|
||||
'HOST': 'xx',
|
||||
'PORT': '5432',
|
||||
}
|
||||
}
|
||||
10
readme.md
10
readme.md
|
|
@ -1,9 +1,17 @@
|
|||
## 如何运行
|
||||
|
||||
将 server 下的 conf_e.json 以及 conf_e.py,重名名为 conf.json 和 conf.py。
|
||||
将 server 下的 conf_e.json 以及 conf_e.py,移动到config文件夹下并重命名为 conf.json 和 conf.py。
|
||||
|
||||
根据自己的情况修改参数
|
||||
|
||||
进入虚拟环境后运行 python manage.py migrate
|
||||
|
||||
导入初始数据 python manage.py loaddata db.json
|
||||
|
||||
默认管理员账户密码为admin xtadmin123!
|
||||
|
||||
在项目目录下执行 python manage.py runserver 即可
|
||||
|
||||
运行后在 localhost:8000/api/swagger/下查看 api 文档
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,22 +1,21 @@
|
|||
celery==5.2.3
|
||||
Django==3.2.12
|
||||
django-celery-beat==2.3.0
|
||||
django-celery-results==2.4.0
|
||||
django-cors-headers==3.11.0
|
||||
django-filter==21.1
|
||||
djangorestframework==3.13.1
|
||||
djangorestframework-simplejwt==5.1.0
|
||||
drf-yasg==1.21.3
|
||||
celery==5.6.2
|
||||
Django==4.2.27
|
||||
django-celery-beat==2.8.1
|
||||
django-celery-results==2.6.0
|
||||
django-cors-headers==4.9.0
|
||||
django-filter==23.5
|
||||
djangorestframework==3.16.1
|
||||
djangorestframework-simplejwt==5.5.1
|
||||
drf-yasg==1.21.7
|
||||
psutil==5.9.0
|
||||
redis==4.4.0
|
||||
django-redis==5.2.0
|
||||
redis==7.1.0
|
||||
django-redis==6.0.0
|
||||
user-agents==2.2.0
|
||||
daphne==4.0.0
|
||||
channels-redis==4.0.0
|
||||
channels-redis==4.3.0
|
||||
django-restql==0.15.2
|
||||
requests==2.28.1
|
||||
xlwt==1.3.0
|
||||
openpyxl==3.1.0
|
||||
openpyxl==3.1.5
|
||||
cron-descriptor==1.2.35
|
||||
docxtpl==0.16.7
|
||||
# deepface==0.0.79
|
||||
|
|
|
|||
|
|
@ -3,7 +3,8 @@
|
|||
"base_name": "xx平台",
|
||||
"base_logo": "/media/default/logo.png",
|
||||
"base_name_short": "xx",
|
||||
"base_logo_side": ""
|
||||
"base_logo_side": "",
|
||||
"base_menucate": "dynamic"
|
||||
},
|
||||
"apk": {
|
||||
"apk_version": "1.0",
|
||||
|
|
|
|||
|
|
@ -14,7 +14,12 @@ EMAIL_USE_TLS = True
|
|||
|
||||
|
||||
# 数据库配置
|
||||
CACHE_LOCATION = "redis://127.0.0.1:6379/2"
|
||||
CACHES = {
|
||||
'default': {
|
||||
'BACKEND': 'django.core.cache.backends.redis.RedisCache',
|
||||
'LOCATION': 'redis://127.0.0.1:6379/2', # Redis URL
|
||||
}
|
||||
}
|
||||
CELERY_BROKER_URL = "redis://127.0.0.1:6379/3"
|
||||
CELERY_TASK_DEFAULT_QUEUE = BASE_PROJECT_CODE
|
||||
DEBUG = True
|
||||
|
|
@ -32,11 +37,6 @@ DATABASES = {
|
|||
# 雪花ID
|
||||
SNOW_DATACENTER_ID = 1
|
||||
|
||||
# 百度语音
|
||||
BD_SP_ID = 'xx'
|
||||
BD_SP_KEY = 'xx'
|
||||
BD_SP_SECRET = 'xx'
|
||||
|
||||
# 运维相关
|
||||
SD_PWD = 'xx'
|
||||
BACKUP_PATH = '/home/xx/xx/xx'
|
||||
|
|
|
|||
Loading…
Reference in New Issue