Przeglądaj źródła

数据库保存

flower_bs 1 dzień temu
rodzic
commit
b87989949d

+ 0 - 0
backup/__init__.py


+ 3 - 0
backup/admin.py

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

+ 6 - 0
backup/apps.py

@@ -0,0 +1,6 @@
+from django.apps import AppConfig
+
+
+class BackupConfig(AppConfig):
+    default_auto_field = 'django.db.models.BigAutoField'
+    name = 'backup'

+ 47 - 0
backup/backup_utils.py

@@ -0,0 +1,47 @@
+# 数据库备份核心模块:backup_utils.py
+import os
+import shutil
+from datetime import datetime
+from django.conf import settings
+import sqlite3
+from pathlib import Path
+import logging
+
+logger = logging.getLogger(__name__)
+
+def backup_database():
+    """执行数据库备份,返回备份路径"""
+    try:
+        # 源数据库路径(根据您的项目配置调整)
+        source_db = Path(settings.BASE_DIR) / 'db.sqlite3'
+        
+        if not source_db.exists():
+            raise FileNotFoundError(f"数据库文件不存在: {source_db}")
+        
+        # 生成备份目录路径
+        now = datetime.now()
+        year_month = now.strftime("%Y%m")  # 202507
+        day_time = now.strftime("%m%d_%H_%M")  # 0728_14_45
+        backup_dir = Path(f"E:/code/backup/{year_month}/{day_time}")
+
+        
+        # 创建备份目录
+        backup_dir.mkdir(parents=True, exist_ok=True)
+        
+        # 目标备份路径
+        backup_path = backup_dir / f"db.sqlite3"
+        
+        # 使用SQLite在线备份API确保备份完整性
+        con = sqlite3.connect(source_db)
+        bck = sqlite3.connect(backup_path)
+        with bck:
+            con.backup(bck, pages=1)
+        bck.close()
+        con.close()
+        
+        logger.info(f"数据库备份成功: {backup_path}")
+        return str(backup_path)
+    
+    except Exception as e:
+        logger.error(f"数据库备份失败: {str(e)}")
+        raise

+ 0 - 0
backup/migrations/__init__.py


+ 3 - 0
backup/models.py

@@ -0,0 +1,3 @@
+from django.db import models
+
+# Create your models here.

+ 3 - 0
backup/tests.py

@@ -0,0 +1,3 @@
+from django.test import TestCase
+
+# Create your tests here.

+ 6 - 0
backup/urls.py

@@ -0,0 +1,6 @@
+from django.urls import path
+from . import views
+
+urlpatterns = [
+    path('trigger/', views.trigger_backup, name='trigger_backup'),
+]

+ 49 - 0
backup/views.py

@@ -0,0 +1,49 @@
+# 视图和调度模块:views.py
+from django.http import JsonResponse
+from django.views.decorators.csrf import csrf_exempt
+from django.views.decorators.http import require_POST
+from .backup_utils import backup_database
+from apscheduler.schedulers.background import BackgroundScheduler
+from django.conf import settings
+import logging
+
+logger = logging.getLogger(__name__)
+
+# 初始化调度器
+scheduler = BackgroundScheduler()
+
+def scheduled_backup():
+    """定时备份任务"""
+    try:
+        backup_path = backup_database()
+        logger.info(f"定时备份完成: {backup_path}")
+    except Exception as e:
+        logger.error(f"定时备份失败: {str(e)}")
+
+# 启动定时备份(每小时执行一次)
+if not scheduler.running:
+    scheduler.add_job(
+        scheduled_backup,
+        'cron',
+        hour='*/6',  # 每6小时执行一次
+        minute=0,    # 在0分钟时执行
+        id='db_backup_job'
+    )
+    scheduler.start()
+
+@csrf_exempt
+@require_POST
+def trigger_backup(request):
+    """手动触发备份的API接口"""
+    try:
+        backup_path = backup_database()
+        return JsonResponse({
+            'status': 'success',
+            'message': 'Database backup completed',
+            'path': backup_path
+        })
+    except Exception as e:
+        return JsonResponse({
+            'status': 'error',
+            'message': str(e)
+        }, status=500)

+ 2 - 0
erp/views.py

@@ -851,6 +851,8 @@ class GenerateOutbound(APIView):
             bound_desc = f"销售出库单{bill_obj.number}"
         elif bill_obj.type == 2:
             bound_desc = f"生产领料单{bill_obj.number}"
+        elif bill_obj.type == 4:
+            bound_desc = f"调拨出库单{bill_obj.number}"
         else:   
             bound_desc = f"其他出库单{bill_obj.number}"
         OutboundBillOperateLog.objects.create(

+ 1 - 0
greaterwms/urls.py

@@ -30,6 +30,7 @@ urlpatterns = [
     path('bound/', include('bound.urls')),
     path('container/', include('container.urls')),
     path ('wms/', include('erp.urls')),
+    path ('backup/', include('backup.urls')),
 
 
     re_path(r'^favicon\.ico$', views.favicon, name='favicon'),

+ 1 - 0
requirements.txt

@@ -50,3 +50,4 @@ unicodecsv==0.14.1
 uritemplate==4.1.1
 urllib3==1.26.12
 zope.interface==6.0
+APScheduler==3.10.4

Plik diff jest za duży
+ 1 - 0
templates/dist/spa/css/18.476963a1.css


Plik diff jest za duży
+ 0 - 1
templates/dist/spa/css/18.a5d7d7ca.css


Plik diff jest za duży
+ 1 - 1
templates/dist/spa/index.html


BIN
templates/dist/spa/js/18.23d853e1.js.gz


Plik diff jest za duży
+ 1 - 1
templates/dist/spa/js/18.23d853e1.js


BIN
templates/dist/spa/js/18.48a2ffbb.js.gz


Plik diff jest za duży
+ 0 - 1
templates/dist/spa/js/31.b371a6df.js


BIN
templates/dist/spa/js/31.b371a6df.js.gz


Plik diff jest za duży
+ 1 - 0
templates/dist/spa/js/31.c01e2730.js


BIN
templates/dist/spa/js/31.c01e2730.js.gz


Plik diff jest za duży
+ 1 - 1
templates/dist/spa/js/app.bf414b56.js


BIN
templates/dist/spa/js/app.0fd76e08.js.gz


BIN
templates/dist/spa/js/app.bf414b56.js.gz


+ 21 - 1
templates/src/layouts/MainLayout.vue

@@ -31,6 +31,14 @@
         </transition>
 
         <q-space />
+        <q-btn flat @click="save_db()" round dense icon="save"
+          ><q-tooltip
+            content-class="bg-amber text-black shadow-4"
+            :offset="[15, 15]"
+            content-style="font-size: 12px"
+            >{{ "保存数据库" }}</q-tooltip
+          ></q-btn
+        >
         <screenfull id="screenfull" class="right-menu-item hover-effect" />
         <transition appear enter-active-class="animated zoomIn">
           <q-btn
@@ -658,7 +666,7 @@
   </q-layout>
 </template>
 <script>
-import { get, getauth, post, baseurl } from 'boot/axios_request'
+import { get, getauth, post, postauth, baseurl } from 'boot/axios_request'
 import { LocalStorage, SessionStorage, openURL } from 'quasar'
 import Bus from 'boot/bus.js'
 import LottieWebCimo from 'components/lottie-web-cimo'
@@ -717,6 +725,18 @@ export default {
     }
   },
   methods: {
+    save_db () {
+      var _this = this
+      postauth('backup/trigger/')
+        .then((res) => {
+          _this.$q.notify({
+            message: '数据库备份成功',
+            icon: 'check',
+            color: 'green'
+          })
+        })
+    },
+
     linkChange (e) {
       var _this = this
       localStorage.removeItem('menulink')

+ 4 - 1
templates/src/pages/erp/erpdn.vue

@@ -850,7 +850,8 @@ export default {
           return [
             { label: '销售出库', value: 1 },
             { label: '生产领料', value: 2 },
-            { label: '其他出库', value: 3 }
+            { label: '其他出库', value: 3 },
+            { label: '调拨出库', value: 4 }
           ]
         case 'audit_status':
           return [
@@ -1211,6 +1212,8 @@ export default {
           return '生产领料申请'
         case 3:
           return '其他出库申请'
+        case 4:
+          return '调拨入库申请'
         default:
           return '未知'
       }