前言
上一篇文章分享了后台列表的自定义按钮,并通过按钮做一些操作。本文分享自定义上传文件页面,接收文件后并处理的过程。
过程
用到了simpleui的自定义左侧导航功能。修改项目总的setting.py
setting.py
自定义左侧导航
SIMPLEUI_CONFIG = {
# 是否使用系统默认菜单,自定义菜单时建议关闭。
'system_keep': False,
# 用于菜单排序和过滤, 不填此字段为默认排序和全部显示。空列表[] 为全部不显示.
'menu_display': ['综合', '用户'],
# 设置是否开启动态菜单, 默认为False. 如果开启, 则会在每次用户登陆时刷新展示菜单内容。
# 一般建议关闭。
'dynamic': False,
'menus': [
{
'app': 'auth',
'name': '用户',
'icon': 'fas fa-user-shield',
'models': [
{
'name': '用户列表',
'icon': 'fa fa-user',
'url': 'auth/user/'
},
{
'name': '用户组',
'icon': 'fa fa-th-list',
'url': 'auth/group/'
}
]
},
{
'name': '综合',
'icon': 'fa fa-desktop',
'models': [
{
'name': '卡片管理',
'url': '/adm/general/virtualcard/',
'icon': 'fa fa-cog'
},
{
'name': '账单上传',
'url': '/record/upfrecord/',
'icon': 'fa fa-file-import'
},
]
},
]
}
路由
修改项目模块目录下的url.py
from django.conf.urls import url
from record import views
urlpatterns = [
url(r'^upfrecord/$', views.up_fundrecord_by_csv, name='record'), #文件上传
]
处理
处理函数在views.py内定义,同样只允许管理员使用此接口。采用事物保证数据一致性。
@user_passes_test(lambda u: u.is_superuser)
@login_required
@transaction.atomic
def up_fundrecord_by_csv(request):
"""
卡片流水记录处理函数
"""
#未录入卡片
not_import_vcards = []
#处理条数
suc=0
repeat=0
fail=0
if request.method == 'POST':
form = MyForm(request.POST, request.FILES)
if form.is_valid():
# 处理上传的文件
uploaded_file = request.FILES['csvfile']
if uploaded_file.name.endswith('.csv'):
csv_data = uploaded_file.read().decode('utf-8')
lines = csv_data.splitlines()
reader = csv.reader(lines)
#跳过表头
next(reader)
#遍历处理
for row in reader:
#transid
vcard = VirtualCard.objects.filter(card_transid=row[0]).first()
if vcard:
#消费类型
if row[2] == 'Authorization(Purchase)':
record_type = 4
else:
record_type = 2
#时间
auth_at = row[7]
#金额
amount = Decimal(row[11])
datetime_obj = datetime.strptime(auth_at, '%Y-%m-%d %H:%M:%S')
timestamp = datetime_obj.timestamp()
unsign_str = str(amount)+vcard.currency+str(int(timestamp))+vcard.card_transid
md5str=common_utils_md5_sign_str(unsign_str)
#避免重复录入
fundrecord = FundRecord.objects.filter(record_md5=md5str).first()
if fundrecord:
repeat += 1
else:
#新增卡片消费记录
try:
with transaction.atomic():
#交易状态为成功
if row[3] == 'Success':
#卡片流水记录
fund_record = FundRecord.objects.create(
card=vcard,
record_md5=md5str,
record_type=record_type,
amount=amount,
pos_currency=row[8],
pos_amount=row[9],
auth_at=auth_at,
status=1,
)
#修改卡片余额
if int(record_type)==4:
vcard.settlement_amount += amount
vcard.remaining_amount -= amount
elif int(record_type)==2:
vcard.settlement_amount -= amount
vcard.remaining_amount += amount
vcard.save()
else:
fund_record = FundRecord.objects.create(
card=vcard,
record_md5=md5str,
record_type=record_type,
amount=amount,
pos_currency=row[8],
pos_amount=row[9],
auth_at=auth_at,
resmsg=row[5],
status=2,
)
#记录处理成功
suc += 1
except Exception as e:
fail += 1
logger.debug(e)
else:
not_import_vcards.append((row[0], row[1]))
else:
messages.error(request, '文件格式错误!')
if fail > 0:
messages.warning(request, '成功{}条,重复{}条,失败{}条'.format(suc,repeat,fail))
else:
messages.success(request, '成功{}条,重复{}条'.format(suc,repeat,fail))
else:
form = MyForm()
content = {
'title': '文件上传',
'form': form,
'not_import_vcards': list(set(not_import_vcards)),
'suc': suc,
'fail': fail,
}
return render(request, 'admin/fundrecord_up.html', content)
模板文件
还有一个模板文件,用来接收csv格式的上传文件。
{% extends 'admin/base_site.html' %}
{% block content %}
<div class="container" style="margin-bottom: 20px;">
<h1>{{ title }}</h1>
<form id="cardForm" method="post" action="" enctype="multipart/form-data">
{% csrf_token %}
{{ form.as_p }}
<br><br>
<button type="submit" class="el-btn el-btn--primary">确定</button>
</form>
</div>
{% if not_import_vcards %}
<div style="height: 20px; border-top: 1px solid #000;">
</div>
<div style="margin-top: 20px;">
<h2> 未在系统内发现下列虚拟卡, 请处理后重新上传:</h2>
<table style="width: 100%; border-collapse: collapse;">
<thead>
<tr>
<th style="padding: 8px; text-align: left; border-bottom: 1px solid #ddd; background-color: #f2f2f2;">transid</th>
<th style="padding: 8px; text-align: left; border-bottom: 1px solid #ddd; background-color: #f2f2f2;">卡号</th>
<th style="padding: 8px; text-align: left; border-bottom: 1px solid #ddd; background-color: #f2f2f2;">操作</th>
</tr>
</thead>
<tbody>
{% for ncard in not_import_vcards %}
<tr style="background-color: {% cycle 'white' 'lightgray' %};">
<td style="padding: 8px; text-align: left; border-bottom: 1px solid #ddd;">{{ ncard.0 }}</td>
<td style="padding: 8px; text-align: left; border-bottom: 1px solid #ddd;">{{ ncard.1 }}</td>
<td style="padding: 8px; text-align: left; border-bottom: 1px solid #ddd;"><button onclick="self.parent.app.openTab({'name':'添加新卡', 'icon': 'fas fa-user-tie', 'url':'/general/addcard/?ctid={{ ncard.0 }}'})" class="el-button el-button--primary el-button--small">添加此卡</button></td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% endif %}
{% endblock %}
评论 (0)