csv
All checks were successful
Deploy to Server / deploy (push) Successful in 24s

This commit is contained in:
jeremygan2021
2026-02-28 11:36:27 +08:00
parent d5ad54e380
commit 1b5751a065
2 changed files with 172 additions and 0 deletions

View File

@@ -7,6 +7,7 @@ from django.shortcuts import redirect
from unfold.admin import ModelAdmin, TabularInline
from unfold.decorators import display
from .models import ESP32Config, Order, Salesperson, WeChatPayConfig, Service, VCCourse, ProductFeature, CommissionLog, WeChatUser, Distributor, Withdrawal, ServiceOrder, CourseEnrollment, AdminPhoneNumber
from .admin_actions import export_orders_excel
import qrcode
from io import BytesIO
import base64
@@ -402,6 +403,7 @@ class OrderAdmin(ModelAdmin):
list_filter = ('status', 'salesperson', 'distributor', 'created_at')
search_fields = ('id', 'customer_name', 'phone_number', 'wechat_trade_no')
readonly_fields = ('total_price', 'created_at', 'wechat_trade_no')
actions = [export_orders_excel]
def get_item_name(self, obj):
if obj.config:

View File

@@ -0,0 +1,170 @@
import csv
import json
import datetime
from django.http import HttpResponse
from django.utils.encoding import escape_uri_path
def flatten_json(y):
"""
Flatten a nested json object
"""
out = {}
def flatten(x, name=''):
if type(x) is dict:
for a in x:
flatten(x[a], name + a + '_')
elif type(x) is list:
i = 0
for a in x:
flatten(a, name + str(i) + '_')
i += 1
else:
out[name[:-1]] = x
flatten(y)
return out
def get_order_signup_keys(queryset):
"""
Collect all unique keys from the signup_info JSON of ActivitySignups related to the Order queryset
"""
keys = set()
for order in queryset:
# Check if order has related activity signups
# Assuming reverse relation name is 'activity_signups' based on ActivitySignup model
signups = order.activity_signups.all()
for signup in signups:
if signup.signup_info and isinstance(signup.signup_info, dict):
flat_info = flatten_json(signup.signup_info)
keys.update(flat_info.keys())
return sorted(list(keys))
def get_order_row_data(order):
"""
Build a dictionary of all exportable data for a single Order object
"""
data = {}
# 1. 订单基础信息
data['订单ID'] = str(order.id)
data['商户订单号'] = order.out_trade_no or ''
data['微信支付单号'] = order.wechat_trade_no or ''
data['订单状态'] = order.get_status_display()
data['订单总价'] = str(order.total_price)
data['购买数量'] = str(order.quantity)
data['创建时间'] = order.created_at.strftime('%Y-%m-%d %H:%M:%S')
# 商品信息
if order.config:
data['商品类型'] = '硬件'
data['商品名称'] = order.config.name
elif order.course:
data['商品类型'] = '课程'
data['商品名称'] = order.course.title
elif order.activity:
data['商品类型'] = '活动'
data['商品名称'] = order.activity.title
else:
data['商品类型'] = '未知'
data['商品名称'] = '未知'
# 2. 发货/收货信息
data['收货人姓名'] = order.customer_name
data['收货电话'] = order.phone_number
data['收货地址'] = order.shipping_address
data['快递公司'] = order.courier_name or ''
data['快递单号'] = order.tracking_number or ''
# 3. 下单用户信息
if order.wechat_user:
data['用户昵称'] = order.wechat_user.nickname
data['用户ID'] = str(order.wechat_user.id)
data['用户OpenID'] = order.wechat_user.openid
data['用户绑定手机'] = order.wechat_user.phone_number or ''
else:
data['用户昵称'] = 'Unknown'
data['用户ID'] = ''
data['用户OpenID'] = ''
data['用户绑定手机'] = ''
# 4. 销售/分销信息
data['销售员'] = order.salesperson.name if order.salesperson else ''
data['分销员'] = order.distributor.user.nickname if order.distributor else ''
return data
def get_order_signup_data(order):
"""
Get flattened signup info for the order.
If multiple signups exist, we only take the first one for simplicity in flat export,
or we could try to merge them but keys might conflict.
Given the context, usually 1 order -> 1 signup for activity.
"""
flat_info = {}
signup = order.activity_signups.first()
if signup and signup.signup_info and isinstance(signup.signup_info, dict):
flat_info = flatten_json(signup.signup_info)
return flat_info
def export_orders_excel(modeladmin, request, queryset):
"""
Export selected orders to Excel, including related Activity Signup JSON info
"""
try:
from openpyxl import Workbook
except ImportError:
modeladmin.message_user(request, "请先安装 openpyxl 库以使用 Excel 导出功能", level='error')
return
opts = modeladmin.model._meta
filename = f"Orders_{datetime.datetime.now().strftime('%Y%m%d_%H%M%S')}.xlsx"
response = HttpResponse(
content_type='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
)
response['Content-Disposition'] = f'attachment; filename={escape_uri_path(filename)}'
wb = Workbook()
ws = wb.active
ws.title = "订单导出"
# Fixed headers
fixed_headers = [
'订单ID', '商户订单号', '微信支付单号', '订单状态', '订单总价', '购买数量', '创建时间',
'商品类型', '商品名称',
'收货人姓名', '收货电话', '收货地址', '快递公司', '快递单号',
'用户ID', '用户昵称', '用户OpenID', '用户绑定手机',
'销售员', '分销员'
]
# Dynamic headers from ActivitySignup (only if orders contain activity orders)
json_keys = get_order_signup_keys(queryset)
# Write header
# Add a prefix to json keys to distinguish them? Or keep as is.
# Let's keep as is but maybe add a note in header if needed.
ws.append(fixed_headers + json_keys)
# Write data
for order in queryset:
row_data = get_order_row_data(order)
signup_data = get_order_signup_data(order)
row = []
for header in fixed_headers:
val = row_data.get(header, '')
row.append(str(val)) # Convert everything to string for safety
for key in json_keys:
val = signup_data.get(key, '')
if val is None:
val = ''
row.append(str(val))
ws.append(row)
wb.save(response)
return response
export_orders_excel.short_description = "导出选中订单为 Excel (含报名信息)"