171 lines
5.7 KiB
Python
171 lines
5.7 KiB
Python
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 (含报名信息)"
|