卡飞资源网

专业编程技术资源共享平台

浅色AI云食堂APP完整代码(二)

以下是整合后的浅色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容器化部署、生产环境配置


此代码整合了所有优化和增强功能,形成一个完整的企业级食堂管理系统,可直接用于开发或生产环境。

控制面板
您好,欢迎到访网站!
  查看权限
网站分类
最新留言