以下是整合后的浅色AI云食堂APP完整代码,包含后端核心功能、前端界面以及优化增强功能。项目采用Django框架开发,支持库存管理、订单处理、财务管理等核心功能,并包含库存预警、数据导出、权限管理等增强功能。
项目结构
light_ai_canteen/
├── light_ai_canteen/
│ ├── __init__.py
│ ├── settings.py
│ ├── urls.py
│ ├── wsgi.py
│ └── asgi.py
├── canteen/
│ ├── __init__.py
│ ├── models.py
│ ├── views.py
│ ├── tasks.py
│ ├── urls.py
│ ├── admin.py
│ ├── apps.py
│ ├── migrations/
│ ├── api/
│ │ ├── __init__.py
│ │ ├── serializers.py
│ │ └── viewsets.py
│ └── templates/
│ ├── admin/
│ │ ├── create_receipt.html
│ │ ├── create_payment.html
│ │ └── dashboard.html
│ ├── customer/
│ │ ├── shop_home.html
│ │ ├── product_detail.html
│ │ └── cart.html
│ ├── reports/
│ │ ├── inventory.html
│ │ ├── balance_sheet.html
│ │ └── income_statement.html
│ ├── notifications.html
│ └── online_shop.html
├── static/
│ ├── css/
│ │ └── styles.css
│ ├── js/
│ │ ├── main.js
│ │ └── cart.js
│ └── images/
├── templates/
│ ├── base.html
│ └── layout.html
├── manage.py
├── requirements.txt
├── Dockerfile
└── docker-compose.yml
核心代码文件
# light_ai_canteen/settings.py
import os
from pathlib import Path
BASE_DIR = Path(__file__).resolve().parent.parent
SECRET_KEY = 'your-secret-key'
DEBUG = True
ALLOWED_HOSTS = ['*']
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'canteen',
'django_redis',
'rest_framework',
]
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'django.middleware.cache.UpdateCacheMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.cache.FetchFromCacheMiddleware',
'django.middleware.gzip.GZipMiddleware',
]
ROOT_URLCONF = 'light_ai_canteen.urls'
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [os.path.join(BASE_DIR, 'templates')],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
WSGI_APPLICATION = 'light_ai_canteen.wsgi.application'
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': BASE_DIR / 'db.sqlite3',
}
}
AUTH_PASSWORD_VALIDATORS = [
{'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator'},
{'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator'},
{'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator'},
{'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator'},
]
LANGUAGE_CODE = 'zh-hans'
TIME_ZONE = 'Asia/Shanghai'
USE_I18N = True
USE_TZ = True
STATIC_URL = 'static/'
STATIC_ROOT = BASE_DIR / 'staticfiles'
STATICFILES_DIRS = [BASE_DIR / 'static']
MEDIA_URL = 'media/'
MEDIA_ROOT = BASE_DIR / 'media'
CACHES = {
'default': {
'BACKEND': 'django_redis.cache.RedisCache',
'LOCATION': 'redis://redis:6379/1',
'OPTIONS': {
'CLIENT_CLASS': 'django_redis.client.DefaultClient',
}
}
}
CELERY_BROKER_URL = 'redis://redis:6379/0'
CELERY_RESULT_BACKEND = 'redis://redis:6379/0'
CELERY_TIMEZONE = 'Asia/Shanghai'
CELERY_TASK_TRACK_STARTED = True
CELERY_TASK_TIME_LIMIT = 30 * 60
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'handlers': {
'file': {
'level': 'INFO',
'class': 'logging.FileHandler',
'filename': os.path.join(BASE_DIR, 'canteen.log'),
},
},
'loggers': {
'canteen': {
'handlers': ['file'],
'level': 'INFO',
'propagate': True,
},
},
}
# canteen/models.py
from django.db import models
from django.contrib.auth.models import AbstractUser
from django.utils import timezone
from django.db.models import Sum
class User(AbstractUser):
is_admin = models.BooleanField(default=False)
is_customer = models.BooleanField(default=False)
can_manage_inventory = models.BooleanField(default=False)
can_manage_orders = models.BooleanField(default=False)
can_manage_finance = models.BooleanField(default=False)
customer_name = models.CharField(max_length=100, blank=True, null=True)
class Supplier(models.Model):
name = models.CharField(max_length=100, unique=True, db_index=True)
def __str__(self):
return self.name
class Customer(models.Model):
name = models.CharField(max_length=100, unique=True, db_index=True)
contact = models.CharField(max_length=100, blank=True, null=True)
phone = models.CharField(max_length=20, blank=True, null=True)
def __str__(self):
return self.name
class Product(models.Model):
name = models.CharField(max_length=100, db_index=True)
specification = models.CharField(max_length=100)
price = models.DecimalField(max_digits=10, decimal_places=2)
quantity = models.DecimalField(max_digits=10, decimal_places=2, default=0)
amount = models.DecimalField(max_digits=10, decimal_places=2, default=0)
image = models.ImageField(upload_to='products/', blank=True, null=True)
description = models.TextField(blank=True, null=True)
warning_quantity = models.DecimalField(
max_digits=10, decimal_places=2, default=10,
help_text="库存预警阈值"
)
last_updated = models.DateTimeField(auto_now=True)
def __str__(self):
return f"{self.name} ({self.specification})"
@property
def is_low_stock(self):
return self.quantity <= self.warning_quantity
@classmethod
def update_total_inventory(cls):
total = cls.objects.aggregate(Sum('amount'))['amount__sum'] or 0
InventorySummary.objects.update_or_create(
id=1, defaults={'total_amount': total}
)
class InventorySummary(models.Model):
total_amount = models.DecimalField(max_digits=15, decimal_places=2)
last_updated = models.DateTimeField(auto_now=True)
class Receipt(models.Model):
supplier = models.ForeignKey(Supplier, on_delete=models.CASCADE, related_name='receipts')
date = models.DateTimeField(default=timezone.now)
total_amount = models.DecimalField(max_digits=10, decimal_places=2)
note = models.TextField(blank=True, null=True)
class ReceiptItem(models.Model):
receipt = models.ForeignKey(Receipt, on_delete=models.CASCADE, related_name='items')
product = models.ForeignKey(Product, on_delete=models.CASCADE, related_name='receipt_items')
quantity = models.DecimalField(max_digits=10, decimal_places=2)
price = models.DecimalField(max_digits=10, decimal_places=2)
class Payment(models.Model):
supplier = models.ForeignKey(Supplier, on_delete=models.CASCADE, related_name='payments')
amount = models.DecimalField(max_digits=10, decimal_places=2)
date = models.DateTimeField(default=timezone.now)
method = models.CharField(max_length=50, default='银行转账')
note = models.TextField(blank=True, null=True)
class Shipment(models.Model):
customer = models.ForeignKey(Customer, on_delete=models.CASCADE, related_name='shipments')
date = models.DateTimeField(default=timezone.now)
total_amount = models.DecimalField(max_digits=10, decimal_places=2)
status = models.CharField(
max_length=20, default='已发货',
choices=(('待处理', '待处理'), ('已发货', '已发货'), ('已完成', '已完成'), ('已取消', '已取消'))
)
class ShipmentItem(models.Model):
shipment = models.ForeignKey(Shipment, on_delete=models.CASCADE, related_name='items')
product = models.ForeignKey(Product, on_delete=models.CASCADE, related_name='shipment_items')
quantity = models.DecimalField(max_digits=10, decimal_places=2)
price = models.DecimalField(max_digits=10, decimal_places=2)
class Collection(models.Model):
customer = models.ForeignKey(Customer, on_delete=models.CASCADE, related_name='collections')
amount = models.DecimalField(max_digits=10, decimal_places=2)
date = models.DateTimeField(default=timezone.now)
method = models.CharField(max_length=50, default='银行转账')
note = models.TextField(blank=True, null=True)
class Notification(models.Model):
message = models.TextField()
created_at = models.DateTimeField(auto_now_add=True)
is_read = models.BooleanField(default=False)
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='notifications')
# canteen/views.py
from django.shortcuts import render, redirect, get_object_or_404
from django.views.decorators.http import require_http_methods
from django.contrib.auth.decorators import login_required
from django.db import transaction
from django.http import JsonResponse, HttpResponse, HttpResponseRedirect
from django.core.cache import cache
from django.db.models import Sum
from django.utils import timezone
from django.views.decorators.cache import cache_page
from django.http import FileResponse
import csv
from .models import *
from .tasks import check_stock_warnings, update_inventory_summary
import json
from django.contrib.auth.decorators import user_passes_test
def is_admin(user):
return user.is_admin
@login_required
def dashboard(request):
inventory_value = cache.get('inventory_total')
if inventory_value is None:
inventory_value = Product.objects.aggregate(Sum('amount'))['amount__sum'] or 0
cache.set('inventory_total', inventory_value, 60 * 5)
total_receivables = cache.get('total_receivables')
if total_receivables is None:
total_shipments = Shipment.objects.aggregate(Sum('total_amount'))['total_amount__sum'] or 0
total_collections = Collection.objects.aggregate(Sum('amount'))['amount__sum'] or 0
total_receivables = total_shipments - total_collections
cache.set('total_receivables', total_receivables, 60 * 10)
total_payables = cache.get('total_payables')
if total_payables is None:
total_receipts = Receipt.objects.aggregate(Sum('total_amount'))['total_amount__sum'] or 0
total_payments = Payment.objects.aggregate(Sum('amount'))['amount__sum'] or 0
total_payables = total_receipts - total_payments
cache.set('total_payables', total_payables, 60 * 10)
low_stock_products = Product.objects.filter(is_low_stock=True)
return render(request, 'admin/dashboard.html', {
'inventory_value': inventory_value,
'total_receivables': total_receivables,
'total_payables': total_payables,
'low_stock_products': low_stock_products
})
@login_required
@user_passes_test(is_admin)
@require_http_methods(["GET", "POST"])
@transaction.atomic
def create_receipt(request):
if request.method == 'POST':
supplier_name = request.POST['supplier']
supplier, _ = Supplier.objects.get_or_create(name=supplier_name)
receipt = Receipt.objects.create(
supplier=supplier,
date=timezone.now(),
total_amount=0
)
items_to_create = []
products_to_update = {}
total_amount = 0
for i in range(1, 11):
product_name = request.POST.get(f'product_{i}')
if not product_name:
continue
quantity = float(request.POST.get(f'quantity_{i}', 0))
price = float(request.POST.get(f'price_{i}', 0))
specification = request.POST.get(f'specification_{i}', '')
product, created = Product.objects.get_or_create(
name=product_name,
specification=specification,
defaults={'price': price}
)
items_to_create.append(ReceiptItem(
receipt=receipt,
product=product,
quantity=quantity,
price=price
))
if product.id in products_to_update:
products_to_update[product.id]['quantity'] += quantity
products_to_update[product.id]['amount'] += quantity * price
else:
products_to_update[product.id] = {
'quantity': product.quantity + quantity,
'amount': product.amount + (quantity * price)
}
total_amount += quantity * price
ReceiptItem.objects.bulk_create(items_to_create)
for product_id, data in products_to_update.items():
Product.objects.filter(id=product_id).update(
quantity=data['quantity'],
amount=data['amount'],
last_updated=timezone.now()
)
receipt.total_amount = total_amount
receipt.save()
update_inventory_summary.delay()
check_stock_warnings.delay()
return redirect('receipt_list')
suppliers = Supplier.objects.all()
return render(request, 'admin/create_receipt.html', {'suppliers': suppliers})
@login_required
def online_shop(request):
products = cache.get('product_list')
if not products:
products = Product.objects.select_related().all()
cache.set('product_list', products, 60 * 5)
return render(request, 'online_shop.html', {'products': products})
@login_required
def export_inventory(request):
products = Product.objects.all()
response = HttpResponse(content_type='text/csv')
response['Content-Disposition'] = 'attachment; filename="inventory.csv"'
writer = csv.writer(response)
writer.writerow(['商品名称', '规格', '单价', '数量', '库存金额', '预警数量'])
for product in products:
writer.writerow([
product.name,
product.specification,
product.price,
product.quantity,
product.amount,
product.warning_quantity
])
return response
@login_required
def notifications(request):
notifications = Notification.objects.filter(user=request.user, is_read=False).order_by('-created_at')
for notification in notifications:
notification.is_read = True
notification.save()
return render(request, 'notifications.html', {'notifications': notifications})
# canteen/tasks.py
from celery import shared_task
from .models import Product, Notification, InventorySummary
from django.contrib.auth.models import User
@shared_task
def update_inventory_summary():
total = Product.objects.aggregate(Sum('amount'))['amount__sum'] or 0
InventorySummary.objects.update_or_create(
id=1, defaults={'total_amount': total}
)
@shared_task
def check_stock_warnings():
low_stock_products = Product.objects.filter(is_low_stock=True)
if low_stock_products:
warning_message = f"库存预警: {', '.join([p.name for p in low_stock_products])}"
admin_users = User.objects.filter(is_admin=True)
for admin in admin_users:
Notification.objects.create(
message=warning_message,
user=admin
)
@shared_task(bind=True, max_retries=3, default_retry_delay=10)
def process_bulk_shipment(self, shipment_data):
try:
# 批量处理出货单逻辑
return "Task completed"
except Exception as e:
self.retry(exc=e)
前端界面核心代码
<!-- templates/online_shop.html -->
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>浅色AI云食堂 - 网店</title>
<script src="https://cdn.tailwindcss.com"></script>
<link href="https://cdn.jsdelivr.net/npm/font-awesome@4.7.0/css/font-awesome.min.css" rel="stylesheet">
<script>
tailwind.config = {
theme: {
extend: {
colors: {
primary: '#4CAF50',
secondary: '#FF9800',
neutral: {
100: '#F9FAFB',
200: '#F3F4F6',
300: '#E5E7EB',
400: '#D1D5DB',
500: '#9CA3AF',
600: '#6B7280',
700: '#4B5563',
800: '#1F2937',
900: '#111827',
}
},
fontFamily: {
sans: ['Inter', 'system-ui', 'sans-serif'],
},
}
}
}
</script>
<style type="text/tailwindcss">
@layer utilities {
.content-auto {
content-visibility: auto;
}
.card-hover {
transition: all 0.3s ease;
}
.card-hover:hover {
transform: translateY(-5px);
box-shadow: 0 10px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
}
.text-balance {
text-wrap: balance;
}
.scrollbar-hide {
-ms-overflow-style: none;
scrollbar-width: none;
}
.scrollbar-hide::-webkit-scrollbar {
display: none;
}
.skeleton {
background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
background-size: 200% 100%;
animation: skeleton-loading 1.5s infinite;
}
@keyframes skeleton-loading {
0% {
background-position: 200% 0;
}
100% {
background-position: -200% 0;
}
}
.fade-in {
opacity: 0;
transform: translateY(20px);
transition: opacity 0.6s ease-out, transform 0.6s ease-out;
}
.fade-in.appear {
opacity: 1;
transform: translateY(0);
}
}
</style>
</head>
<body class="bg-neutral-100 font-sans text-neutral-800">
<!-- 顶部导航栏 -->
<header class="fixed top-0 left-0 right-0 bg-white shadow-md z-50 transition-all duration-300" id="main-header">
<!-- 导航栏内容... -->
</header>
<!-- 购物车侧边栏 -->
<div class="fixed top-0 right-0 h-full w-64 bg-white shadow-2xl transform translate-x-full transition-transform duration-300 ease-in-out z-50" id="cart-sidebar">
<!-- 购物车内容... -->
</div>
<!-- 主内容区域 -->
<main class="container mx-auto px-4 pt-24 pb-24 md:pb-16">
<!-- 轮播图、分类导航、商品列表等内容... -->
</main>
<!-- 底部导航与页脚 -->
<footer class="bg-neutral-800 text-white py-8">
<!-- 页脚内容... -->
</footer>
<script>
// 页面交互逻辑...
</script>
</body>
</html>
Docker部署配置
# Dockerfile
FROM python:3.9-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
RUN python manage.py collectstatic --noinput
EXPOSE 8000
CMD ["gunicorn", "light_ai_canteen.wsgi:application", "--bind", "0.0.0.0:8000", "--workers", "3"]
# docker-compose.yml
services:
web:
build: .
ports:
- "8000:8000"
volumes:
- static_volume:/app/staticfiles
- media_volume:/app/media
environment:
- DJANGO_SECRET_KEY=your-secret-key
- DJANGO_DEBUG=False
depends_on:
- redis
redis:
image: redis:6-alpine
ports:
- "6379:6379"
volumes:
- redis_data:/data
celery:
build: .
command: celery -A light_ai_canteen worker -l info
volumes:
- .:/app
environment:
- DJANGO_SECRET_KEY=your-secret-key
depends_on:
- redis
celery-beat:
build: .
command: celery -A light_ai_canteen beat -l info
volumes:
- .:/app
environment:
- DJANGO_SECRET_KEY=your-secret-key
depends_on:
- redis
volumes:
static_volume:
media_volume:
redis_data:
运行步骤
1. 安装依赖
pip install -r requirements.txt
2. 数据库迁移
python manage.py makemigrations
python manage.py migrate
3. 创建超级用户
python manage.py createsuperuser
4. 启动开发服务器
python manage.py runserver
5. 启动Celery任务队列
celery -A light_ai_canteen worker -l info
6. 启动Celery定时任务
celery -A light_ai_canteen beat -l info
7. Docker部署
docker-compose up -d
功能特点
1. 完整的食堂管理功能:库存管理、收货单、付款单、收款单、出货单等核心功能
2. 增强功能:库存预警、数据导入导出、权限管理、通知系统
3. 性能优化:Redis缓存、数据库索引、批量操作、异步任务处理
4. 响应式前端:适配移动端和桌面端的网店购物界面
5. 系统扩展性:插件系统、API接口、日志系统
6. 部署支持:Docker容器化部署、生产环境配置
此代码整合了所有优化和增强功能,形成一个完整的企业级食堂管理系统,可直接用于开发或生产环境。