rtc_backend/apps/inventory/services.py
2026-01-29 10:02:15 +08:00

225 lines
6.1 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""
出入库模块服务层 - SN码生成、Excel导入导出
"""
import io
from datetime import datetime
from django.db import transaction
from openpyxl import Workbook, load_workbook
from openpyxl.drawing.image import Image as XLImage
import qrcode
from io import BytesIO
from apps.devices.models import DeviceType, DeviceBatch, Device
def generate_production_week(date):
"""
生成生产周格式: YYWXX
:param date: 日期对象
:return: 如 25W45
"""
year = date.year % 100
week = date.isocalendar()[1]
return f"{year}W{week:02d}"
def generate_sn_code(brand, product_code, production_week, batch_no, sequence):
"""
生成SN码
格式: {品牌}-{产品代号}-{生产周}-{批次号}-{5位序列号}
示例: AL-DZBJ-ON-25W45-A01-00001
"""
return f"{brand}-{product_code}-{production_week}-{batch_no}-{sequence:05d}"
def generate_batch_devices(batch):
"""
为批次生成设备和SN码
:param batch: DeviceBatch对象
:return: 生成的Device列表
"""
device_type = batch.device_type
brand = device_type.brand
product_code = device_type.product_code
production_week = batch.production_week or generate_production_week(batch.production_date)
batch_no = batch.batch_no
# 更新生产周
if not batch.production_week:
batch.production_week = production_week
batch.save(update_fields=['production_week'])
devices = []
for i in range(1, batch.quantity + 1):
sn = generate_sn_code(brand, product_code, production_week, batch_no, i)
device = Device(
sn=sn,
device_type=device_type,
batch=batch,
status='in_stock'
)
devices.append(device)
# 批量创建
Device.objects.bulk_create(devices)
# 更新批次状态
batch.status = 'generated'
batch.save(update_fields=['status'])
return devices
def generate_qrcode_image(data, size=100):
"""
生成二维码图片
:param data: 二维码内容
:param size: 尺寸
:return: BytesIO对象
"""
qr = qrcode.QRCode(
version=1,
error_correction=qrcode.constants.ERROR_CORRECT_L,
box_size=4,
border=1,
)
qr.add_data(data)
qr.make(fit=True)
img = qr.make_image(fill_color="black", back_color="white")
img_io = BytesIO()
img.save(img_io, format='PNG')
img_io.seek(0)
return img_io
def export_batch_excel(batch):
"""
导出批次SN码Excel
包含四列: SN码 / MAC地址 / 创建时间 / SN码二维码
:param batch: DeviceBatch对象
:return: BytesIO对象Excel文件
"""
wb = Workbook()
ws = wb.active
ws.title = f"批次{batch.batch_no}"
# 设置表头
headers = ['SN码', 'MAC地址', '创建时间', 'SN码二维码']
for col, header in enumerate(headers, 1):
ws.cell(row=1, column=col, value=header)
# 设置列宽
ws.column_dimensions['A'].width = 35
ws.column_dimensions['B'].width = 20
ws.column_dimensions['C'].width = 22
ws.column_dimensions['D'].width = 15
# 获取批次设备
devices = Device.objects.filter(batch=batch).order_by('sn')
for row, device in enumerate(devices, 2):
ws.cell(row=row, column=1, value=device.sn)
ws.cell(row=row, column=2, value=device.mac_address or '')
ws.cell(row=row, column=3, value=device.created_at.strftime('%Y-%m-%d %H:%M:%S'))
# 生成二维码并嵌入
try:
qr_img = generate_qrcode_image(device.sn)
img = XLImage(qr_img)
img.width = 60
img.height = 60
ws.add_image(img, f'D{row}')
ws.row_dimensions[row].height = 50
except Exception:
ws.cell(row=row, column=4, value='[QR]')
# 保存到内存
output = BytesIO()
wb.save(output)
output.seek(0)
return output
def import_mac_addresses(batch, file_obj):
"""
导入MAC地址
:param batch: DeviceBatch对象
:param file_obj: Excel文件对象
:return: 导入结果字典
"""
result = {
'total': 0,
'success': 0,
'failed': 0,
'errors': []
}
try:
wb = load_workbook(file_obj)
ws = wb.active
except Exception as e:
result['errors'].append({'row': 0, 'error': f'文件格式错误: {str(e)}'})
return result
# 跳过表头
rows = list(ws.iter_rows(min_row=2, values_only=True))
result['total'] = len(rows)
for row_num, row in enumerate(rows, 2):
if len(row) < 2:
continue
sn = row[0]
mac = row[1]
if not sn or not mac:
continue
# 统一MAC地址格式
mac = str(mac).upper().replace('-', ':').strip()
# 验证MAC地址格式
import re
if not re.match(r'^([0-9A-F]{2}:){5}[0-9A-F]{2}$', mac):
result['failed'] += 1
result['errors'].append({
'row': row_num,
'sn': sn,
'mac': mac,
'error': 'MAC地址格式不正确'
})
continue
try:
device = Device.objects.get(sn=sn, batch=batch)
except Device.DoesNotExist:
result['failed'] += 1
result['errors'].append({
'row': row_num,
'sn': sn,
'mac': mac,
'error': 'SN码不存在或不属于当前批次'
})
continue
# 检查MAC地址是否已被使用
existing = Device.objects.filter(mac_address=mac).exclude(id=device.id).first()
if existing:
result['failed'] += 1
result['errors'].append({
'row': row_num,
'sn': sn,
'mac': mac,
'error': f'MAC地址已被设备 {existing.sn} 使用'
})
continue
# 更新MAC地址
device.mac_address = mac
device.save(update_fields=['mac_address'])
result['success'] += 1
return result