forked from erp-dev/erp
feat: celery_beat
This commit is contained in:
2
.env
2
.env
@@ -1,6 +1,6 @@
|
||||
SECRET_KEY=testing_$weak_secret_is_allowd
|
||||
DEBUG=True
|
||||
ALLOWED_HOSTS=*,
|
||||
ALLOWED_HOSTS=yuwenerp.yuwen.cloud,localhost,
|
||||
DB_HOST=localhost
|
||||
DB_PORT=5432
|
||||
DB_NAME=flower
|
||||
|
||||
@@ -119,5 +119,3 @@ class StatementRecordView(StatementViewBase):
|
||||
'records': matched_records,
|
||||
}
|
||||
return self._build_response(single_payload)
|
||||
|
||||
|
||||
|
||||
@@ -307,6 +307,7 @@ class PlateOrderListSerializer(PlateOrderDesignCodeMixin, serializers.ModelSeria
|
||||
progress_percentage = serializers.IntegerField(read_only=True)
|
||||
process_name = serializers.SerializerMethodField()
|
||||
plate_image_url = serializers.SerializerMethodField()
|
||||
last_completed_state = serializers.CharField(read_only=True)
|
||||
|
||||
class Meta:
|
||||
model = models.PlateOrder
|
||||
@@ -326,12 +327,12 @@ class PlateOrderListSerializer(PlateOrderDesignCodeMixin, serializers.ModelSeria
|
||||
'required_completion_date', 'completion_date',
|
||||
'approval_result', 'is_ordered', 'customer_feedback', 'print_count',
|
||||
'process', 'process_name',
|
||||
'status', 'status_id', 'is_completed', 'has_started',
|
||||
'status', 'status_id', 'is_completed', 'has_started', 'last_completed_state',
|
||||
'progress_percentage', 'business_object_id',
|
||||
'created_at', 'updated_at'
|
||||
]
|
||||
read_only_fields = [
|
||||
'id', 'status', 'progress_percentage',
|
||||
'id', 'status', 'progress_percentage', 'last_completed_state',
|
||||
'created_at', 'updated_at', 'print_count'
|
||||
]
|
||||
|
||||
@@ -367,6 +368,7 @@ class PlateOrderDetailSerializer(PlateOrderDesignCodeMixin, serializers.ModelSer
|
||||
business_object_id = serializers.SerializerMethodField()
|
||||
plate_image_url = serializers.SerializerMethodField()
|
||||
process_name = serializers.SerializerMethodField()
|
||||
last_completed_state = serializers.CharField(read_only=True)
|
||||
|
||||
class Meta:
|
||||
model = models.PlateOrder
|
||||
@@ -386,12 +388,12 @@ class PlateOrderDetailSerializer(PlateOrderDesignCodeMixin, serializers.ModelSer
|
||||
'approval_result', 'is_ordered', 'customer_feedback', 'print_count',
|
||||
'process', 'process_name',
|
||||
'status', 'status_id', 'is_completed', 'has_started',
|
||||
'progress_percentage', 'business_object_id',
|
||||
'progress_percentage', 'business_object_id', 'last_completed_state',
|
||||
'created_at', 'updated_at'
|
||||
]
|
||||
read_only_fields = [
|
||||
'id', 'status', 'status_id', 'is_completed', 'has_started',
|
||||
'progress_percentage', 'business_object_id',
|
||||
'progress_percentage', 'business_object_id', 'last_completed_state',
|
||||
'created_at', 'updated_at', 'print_count'
|
||||
]
|
||||
|
||||
|
||||
18
basic_info/migrations/0017_alter_customer_mobile.py
Normal file
18
basic_info/migrations/0017_alter_customer_mobile.py
Normal file
@@ -0,0 +1,18 @@
|
||||
# Generated by Django 5.2.8 on 2025-12-08 10:35
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('basic_info', '0016_merchantsetting_type'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='customer',
|
||||
name='mobile',
|
||||
field=models.CharField(blank=True, max_length=20, null=True, verbose_name='手机'),
|
||||
),
|
||||
]
|
||||
@@ -313,7 +313,7 @@ class Customer(ModelBase):
|
||||
null=True,
|
||||
verbose_name='创建者',
|
||||
)
|
||||
mobile = models.CharField(max_length=20, verbose_name='手机')
|
||||
mobile = models.CharField(max_length=20, null=True, blank=True, verbose_name='手机')
|
||||
area = models.CharField(max_length=100, blank=True, null=True, verbose_name='地区')
|
||||
email = models.EmailField(blank=True, null=True, verbose_name='电子邮箱')
|
||||
contact = models.CharField(blank=True, null=True, verbose_name='联系人')
|
||||
|
||||
BIN
celerybeat-schedule
Normal file
BIN
celerybeat-schedule
Normal file
Binary file not shown.
BIN
celerybeat-schedule-shm
Normal file
BIN
celerybeat-schedule-shm
Normal file
Binary file not shown.
BIN
celerybeat-schedule-wal
Normal file
BIN
celerybeat-schedule-wal
Normal file
Binary file not shown.
File diff suppressed because it is too large
Load Diff
@@ -51,6 +51,8 @@ services:
|
||||
- rabbitmq_data:/var/lib/rabbitmq
|
||||
restart: unless-stopped
|
||||
|
||||
|
||||
|
||||
web:
|
||||
build:
|
||||
context: .
|
||||
@@ -65,7 +67,7 @@ services:
|
||||
DJANGO_SETTINGS_MODULE: flower.settings
|
||||
PYTHONPATH: /app
|
||||
DEBUG: "1"
|
||||
ALLOWED_HOSTS: "localhost,127.0.0.1,0.0.0.0,8.148.215.233"
|
||||
ALLOWED_HOSTS: "localhost,127.0.0.1,0.0.0.0,8.148.215.233,yuwenerp.yuwen.cloud"
|
||||
DB_HOST: postgres
|
||||
DB_PORT: "5432"
|
||||
DB_NAME: flower
|
||||
@@ -108,6 +110,36 @@ services:
|
||||
DB_PASSWORD: postgres
|
||||
restart: unless-stopped
|
||||
|
||||
celery_beat:
|
||||
build:
|
||||
context: .
|
||||
target: base
|
||||
container_name: celery_beat
|
||||
command:
|
||||
- celery
|
||||
- -A
|
||||
- flower
|
||||
- beat
|
||||
- -l
|
||||
- info
|
||||
volumes:
|
||||
- .:/app
|
||||
depends_on:
|
||||
- postgres
|
||||
- redis
|
||||
- rabbitmq
|
||||
environment:
|
||||
DJANGO_SETTINGS_MODULE: flower.settings
|
||||
PYTHONPATH: /app
|
||||
DB_HOST: postgres
|
||||
DB_PORT: "5432"
|
||||
DB_NAME: flower
|
||||
DB_USER: postgres
|
||||
DB_PASSWORD: postgres
|
||||
CELERY_BROKER_URL: amqp://guest:guest@rabbitmq:5672//
|
||||
CELERY_RESULT_BACKEND: redis://redis:6379/0
|
||||
restart: unless-stopped
|
||||
|
||||
volumes:
|
||||
db_data:
|
||||
redis_data:
|
||||
|
||||
@@ -46,18 +46,22 @@ SECRET_KEY = env('SECRET_KEY', default='weak_secret_key_for_dev_only')
|
||||
# SECURITY WARNING: don't run with debug turned on in production!
|
||||
DEBUG = env('DEBUG', default=False)
|
||||
|
||||
ALLOWED_HOSTS = env.list('ALLOWED_HOSTS', default=[])
|
||||
ALLOWED_HOSTS = env.list('ALLOWED_HOSTS', default=[
|
||||
'yuwenerp.yuwen.cloud',
|
||||
])
|
||||
CSRF_TRUSTED_ORIGINS = env.list('CSRF_TRUSTED_ORIGINS', default=[
|
||||
'https://yuwenerp.yuwen.cloud',
|
||||
])
|
||||
|
||||
|
||||
# CORS 配置
|
||||
CORS_ALLOW_ALL_ORIGINS = DEBUG # 开发环境允许所有源,生产环境需要配置白名单
|
||||
CORS_ALLOWED_ORIGINS = env.list('CORS_ALLOWED_ORIGINS', default=[
|
||||
'http://localhost:3000',
|
||||
'http://localhost:5173',
|
||||
'http://localhost:5174',
|
||||
'http://127.0.0.1:3000',
|
||||
'http://localhost:8000',
|
||||
'http://127.0.0.1:5173',
|
||||
'http://127.0.0.1:5174',
|
||||
'http://127.0.0.1:5179',
|
||||
'https://yuwenerp.yuwen.cloud',
|
||||
])
|
||||
CORS_ALLOW_CREDENTIALS = True # 允许携带凭证(如 Cookie、认证头)
|
||||
|
||||
@@ -220,7 +224,7 @@ QINIU_ACCESS_KEY = 'IySZff0zEN7jHeDuxj5PrQNMv0qjKoVqBRCuFMzi'
|
||||
QINIU_SECRET_KEY = 'Mph5XkjUQO3VHFPT1JulYHY442uMOya2bXjbSZp8'
|
||||
QINIU_BUCKET_NAME = 'yunwenerp'
|
||||
QINIU_BUCKET_DOMAIN = 'image.yuwen.cloud'
|
||||
QINIU_SECURE_URL = False
|
||||
QINIU_SECURE_URL = True
|
||||
|
||||
STORAGES = {
|
||||
'default': {
|
||||
@@ -287,3 +291,16 @@ CELERY_ACCEPT_CONTENT = ['json']
|
||||
CELERY_TASK_SERIALIZER = 'json'
|
||||
CELERY_RESULT_SERIALIZER = 'json'
|
||||
CELERY_TIMEZONE = TIME_ZONE
|
||||
|
||||
from celery.schedules import crontab
|
||||
|
||||
CELERY_BEAT_SCHEDULE = {
|
||||
'daily_database_backup': {
|
||||
'task': 'api_v1.tasks.backup_database',
|
||||
'schedule': crontab(hour=3, minute=0),
|
||||
'kwargs': {
|
||||
'output_dir': str(BASE_DIR / 'data-bak'),
|
||||
'filename_prefix': 'db-backup',
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -169,6 +169,17 @@ class PlateOrder(ModelBase):
|
||||
from stateflow.services import get_display_status
|
||||
return get_display_status(self.business_object)
|
||||
|
||||
@property
|
||||
def last_completed_state(self) -> str:
|
||||
"""
|
||||
返回最后一个已完成节点的名称,用于详情展示
|
||||
"""
|
||||
if not self.business_object:
|
||||
return ''
|
||||
from stateflow.services import get_last_completed_state
|
||||
last_state = get_last_completed_state(self.business_object)
|
||||
return last_state.name if last_state else ''
|
||||
|
||||
@property
|
||||
def status_id(self):
|
||||
"""返回当前状态ID(下一个待执行的状态ID)"""
|
||||
|
||||
@@ -7,7 +7,7 @@ requires-python = ">=3.14"
|
||||
dependencies = [
|
||||
"celery>=5.5.3",
|
||||
"coverage>=7.12.0",
|
||||
"django>=5.2.7",
|
||||
"django==5.2.8",
|
||||
"django-cors-headers>=4.9.0",
|
||||
"django-environ>=0.12.0",
|
||||
"django-filter>=25.2",
|
||||
|
||||
@@ -231,7 +231,10 @@ class StateLogParameterRecordInline(admin.TabularInline):
|
||||
|
||||
@admin.register(models.StateFlowRecord)
|
||||
class StateFlowRecordAdmin(admin.ModelAdmin):
|
||||
list_display = ('id', 'business_object', 'state', 'completed_at', 'completed_by', 'is_cancelled', 'cancelled_at', 'parameter_count')
|
||||
list_display = (
|
||||
'id', 'business_object', 'business_object__object_id',
|
||||
'state', 'completed_at',
|
||||
'completed_by', 'is_cancelled', 'cancelled_at', 'parameter_count')
|
||||
search_fields = ('business_object__name', 'state__name')
|
||||
list_filter = ('state', 'is_cancelled', 'completed_at', 'completed_by')
|
||||
readonly_fields = ('business_object', 'state', 'completed_at', 'completed_by', 'cancelled_at', 'parameters_summary')
|
||||
|
||||
18
stock/migrations/0008_alter_stockchangerecord_source_type.py
Normal file
18
stock/migrations/0008_alter_stockchangerecord_source_type.py
Normal file
@@ -0,0 +1,18 @@
|
||||
# Generated by Django 5.2.8 on 2025-12-08 10:35
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('stock', '0007_transferorder'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='stockchangerecord',
|
||||
name='source_type',
|
||||
field=models.IntegerField(choices=[(1, '采购'), (2, '销退'), (3, '调入'), (4, '盘盈'), (5, '合并'), (6, '销售'), (7, '采购退货'), (8, '调出'), (9, '盘亏'), (10, '拆卷'), (11, '红冲')], verbose_name='变动来源'),
|
||||
),
|
||||
]
|
||||
8
uv.lock
generated
8
uv.lock
generated
@@ -283,16 +283,16 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "django"
|
||||
version = "5.2.7"
|
||||
version = "5.2.8"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "asgiref" },
|
||||
{ name = "sqlparse" },
|
||||
{ name = "tzdata", marker = "sys_platform == 'win32'" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/b1/96/bd84e2bb997994de8bcda47ae4560991084e86536541d7214393880f01a8/django-5.2.7.tar.gz", hash = "sha256:e0f6f12e2551b1716a95a63a1366ca91bbcd7be059862c1b18f989b1da356cdd", size = 10865812, upload-time = "2025-10-01T14:22:12.081Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/05/a2/933dbbb3dd9990494960f6e64aca2af4c0745b63b7113f59a822df92329e/django-5.2.8.tar.gz", hash = "sha256:23254866a5bb9a2cfa6004e8b809ec6246eba4b58a7589bc2772f1bcc8456c7f", size = 10849032, upload-time = "2025-11-05T14:07:32.778Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/8f/ef/81f3372b5dd35d8d354321155d1a38894b2b766f576d0abffac4d8ae78d9/django-5.2.7-py3-none-any.whl", hash = "sha256:59a13a6515f787dec9d97a0438cd2efac78c8aca1c80025244b0fe507fe0754b", size = 8307145, upload-time = "2025-10-01T14:22:49.476Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5e/3d/a035a4ee9b1d4d4beee2ae6e8e12fe6dee5514b21f62504e22efcbd9fb46/django-5.2.8-py3-none-any.whl", hash = "sha256:37e687f7bd73ddf043e2b6b97cfe02fcbb11f2dbb3adccc6a2b18c6daa054d7f", size = 8289692, upload-time = "2025-11-05T14:07:28.761Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -441,7 +441,7 @@ requires-dist = [
|
||||
{ name = "aiohttp", specifier = ">=3.13.2" },
|
||||
{ name = "celery", specifier = ">=5.5.3" },
|
||||
{ name = "coverage", specifier = ">=7.12.0" },
|
||||
{ name = "django", specifier = ">=5.2.7" },
|
||||
{ name = "django", specifier = "==5.2.8" },
|
||||
{ name = "django-cors-headers", specifier = ">=4.9.0" },
|
||||
{ name = "django-environ", specifier = ">=0.12.0" },
|
||||
{ name = "django-filter", specifier = ">=25.2" },
|
||||
|
||||
Reference in New Issue
Block a user