一介闲人
一介闲人
以demo项目名为例
order
| order
| | tatic
| | | bootstrap.min.css
| | | jquery-3.7.1.min.js
| | | bootstrap.min.js
| | templates
| | | layout.html
| | | login.html
| | | order_list.html
| | | user_list.html
| | views
| | | account.py
| | | order.py
| | __init__.py
| task
| | worker.py
| utils
| | mycryputils.py
| | mydbutils.py
| | myredisutils.py
| | mytimeutils.py
| | myutils.py
| app.py
| db.sql
| requirements.txt
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
<title>订单列表</title>
<link rel="stylesheet" href="/static/bootstrap.min.css"/>
</head>
<body>
<nav class="navbar navbar-default" style="border-radius: 0">
<div class="container">
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse"
data-target="#bs-example-navbar-collapse-1" aria-expanded="false">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="/users?page=1">单多多订单平台</a>
</div>
<div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
<ul class="nav navbar-nav">
<li><a href="/users">用户管理</a></li>
<li><a href="/order/list?page=1">订单管理</a></li>
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">Dropdown <span class="caret"></span></a>
<ul class="dropdown-menu">
<li><a href="#">Action</a></li>
<li><a href="#">Another action</a></li>
<li><a href="#">Something else here</a></li>
<li role="separator" class="divider"></li>
<li><a href="#">Separated link</a></li>
<li role="separator" class="divider"></li>
<li><a href="#">One more separated link</a></li>
</ul>
</li>
</ul>
<ul class="nav navbar-nav navbar-right">
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">
{{getLoginUserInfo().real_name}} <span class="caret"></span>
</a>
<ul class="dropdown-menu">
<li><a href="#" onclick="showInfo('开发中,敬请期待!')">个人资料</a></li>
<li><a href="#" onclick="showInfo('开发中,敬请期待!')">修改密码</a></li>
<li role="separator" class="divider"></li>
<li><a href="/logout">退出登陆</a></li>
</ul>
</li>
</ul>
</div>
</div>
</nav>
<!-- 消息提示,3秒后自动关闭 -->
<div id="infoTopId" class="alert alert-success" style="display: none; position: fixed ; top: 80px; right: 30px; min-width: 300px;">
<span id="infoTopMsg"></span>
</div>
{%block body%}{%endblock%}
<script src="/static/jquery-3.7.1.min.js"></script>
<script src="/static/bootstrap.min.js"></script>
<script>
function showInfo(msg) {
let sec = 5;
$('#infoTopMsg').html(msg + '<span style="position:fixed; right: 60px"><u>' + sec + '</u> 秒后关闭</span>');
$("#infoTopId").css('display', 'block');
const timertop = setInterval(function () {
$('#infoTopMsg').html(msg + '<span style="position:fixed; right: 60px"><u>' + (sec - 1) + '</u> 秒后关闭</span>');
if (sec <= 1) {
$("#infoTopId").css('display', 'none');
clearInterval(timertop);
}
sec--;
}, 1000);
}
</script>
</body>
</html>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>用户登录</title>
<link rel="stylesheet" href="/static/bootstrap.min.css"/>
<style>
.form-box {
width: 500px;
border: 1px solid #ddd;
padding: 15px 30px 15px 30px;
margin: 200px auto 0;
}
.form-box h2 {
text-align: center;
}
.form-box .form-group {
margin-bottom: 15px;
}
.mybtn {
color: white;
justify-content: center;
text-align: center;
}
</style>
</head>
<body>
<div class="form-box">
<h2>用户登录</h2>
<form action="/login" method="post">
<div class="form-group">
<select name="role" class="form-control">
<option value="1">普通用户</option>
<option value="2">管理员</option>
</select>
</div>
<div class="form-group">
<label for="inputmobile">用户名:</label>
<input type="text" id="inputmobile" required class="form-control" name="mobile" placeholder="请输入手机号" />
</div>
<div class="form-group">
<label for="inputpassword">密 码:</label>
<input type="password" id="inputpassword" required class="form-control" name="pwd" placeholder="请输入密码" />
</div>
<div class="form-group">
<button class="mybtn btn btn-info btn-block" type="submit">登 录</button>
</div>
<span style="color: red;">{{error}}</span>
</form>
</div>
<script src="/static/jquery-3.7.1.min.js"></script>
<script src="/static/bootstrap.min.js"></script>
</body>
</html>
{% extends 'layout.html' %}
{%block body%}
<div class="container" style="margin-top: 10px">
<button type="button" class="btn btn-info" data-toggle="modal" data-target="#myModal" style="margin-bottom: 10px; text-align: right">
<span class="glyphicon glyphicon-plus" aria-hidden="true"></span> 添加订单
</button>
<table class="table table-bordered table-hover">
<thead>
<tr>
<th>订单编号</th>
<th>地址</th>
<th>数量</th>
<th>状态</th>
<th>用户</th>
<th>创建时间</th>
<th>修改时间</th>
<th>操作</th>
</tr>
</thead>
<tbody>
{% for item in data_list %}
<tr>
<td>{{item.order_no}}</td>
<td>{{item.url}}</td>
<td>{{item.count}}</td>
<td>
{% if item.active == 0 %}
<span class="label label-default">{{item.status_msg.text}}</span>
<span class="label label-default">已删除</span>
{% else %}
<span class="label label-{{item.status_msg.cls}}">{{item.status_msg.text}}</span>
{% endif %}
</td>
<td>{{item.real_name}}</td>
<td>{{item.create_time}}</td>
<td>{{item.update_time}}</td>
<td>
{% if item.status == 0 and item.active == 1 %}
<a href="#" type="button" class="btn btn-info btn-sm {{item.status_msg.disabled}}" onclick='showUpdate("{{item}}")'>修改</a>
{% endif %}
{% if item.active == 1 %}
<a href="#" type="button" class="btn btn-danger btn-sm" onclick='showDelete("{{item.id}}")'>删除</a>
{% endif %}
</td>
</tr>
<span style="display: none">{{item.status}}</span>
{% endfor %}
</tbody>
</table>
{% if pageinfo.page > 1 or pageinfo.islastpage == 0 %}
<nav aria-label="Page navigation" style="text-align: right">
<ul class="pagination">
{% if pageinfo.page > 1 %}
<li>
<a href="/order/list?page={{pageinfo.page-1}}" aria-label="Previous">
<span aria-hidden="true">«</span>
</a>
</li>
{% endif %}
<li><a href="#"><span id="curPageId">{{pageinfo.page}}</span></a></li>
{% if pageinfo.islastpage == 0 %}
<li>
<a href="/order/list?page={{pageinfo.page+1}}" aria-label="Next">
<span aria-hidden="true">»</span>
</a>
</li>
{% endif %}
</ul>
</nav>
{% endif %}
</div>
<!-- 消息提示,3秒后自动关闭 -->
<div id="infoId" class="alert alert-success" style="display: none; position: fixed ; bottom: 30px; right: 30px">
<a href="#" class="close" data-dismiss="alert" aria-label="close">×</a>
<span id="infoMsg"></span>
</div>
<!-- 新增 Modal -->
<div class="modal fade" id="myModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
<h4 class="modal-title" id="myModalLabel">新增订单</h4>
</div>
<div class="modal-body form-horizontal">
<div class="form-group">
<label for="inputUrl" class="col-sm-2 control-label">网址</label>
<div class="col-sm-10">
<input type="text" class="form-control" id="inputUrl" name="inputUrl" placeholder="请输入网址">
<span id="urlErrorMsg" style="color: red"></span>
</div>
</div>
<div class="form-group">
<label for="inputCount" class="col-sm-2 control-label">数量</label>
<div class="col-sm-10">
<input type="number" class="form-control" id="inputCount" name="inputCount" placeholder="请输入数量">
<span id="countErrorMsg" style="color: red"></span>
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">取消</button>
<button id="save-btn" class="btn btn-primary" disabled onclick="save()">提交</button>
</div>
</div>
</div>
<!-- 错误提示,3秒后自动关闭 -->
<div id="errorId" class="alert alert-danger" style="display: none; position: fixed ; top: 30px; right: 30px">
<a href="#" class="close" data-dismiss="alert" aria-label="close">×</a>
<span id="errorMsg"></span>
</div>
</div>
<!-- 修改 Modal -->
<div class="modal fade" id="updModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
<h4 class="modal-title" id="updModalLabel">修改订单</h4>
</div>
<div class="modal-body form-horizontal">
<div class="form-group hidden">
<input type="text" id="updInputId" name="updInputId">
<input type="text" id="old_url" name="old_url">
<input type="text" id="old_count" name="old_count">
</div>
<div class="form-group">
<label class="col-sm-2 control-label">订单编号</label>
<div class="col-sm-10">
<input type="text" class="form-control" id="updInputOrderNo" disabled/>
</div>
</div>
<div class="form-group">
<label for="inputUrl" class="col-sm-2 control-label">网址</label>
<div class="col-sm-10">
<input type="text" class="form-control" id="updInputUrl" name="updInputUrl" placeholder="请输入网址">
<span id="updUrlErrorMsg" style="color: red"></span>
</div>
</div>
<div class="form-group">
<label for="inputCount" class="col-sm-2 control-label">数量</label>
<div class="col-sm-10">
<input type="number" class="form-control" id="updInputCount" name="updInputCount" placeholder="请输入数量">
<span id="updCountErrorMsg" style="color: red">资料没变动,请先修改</span>
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">取消</button>
<button id="upd-btn" class="btn btn-primary" disabled onclick="update()">提交</button>
</div>
</div>
</div>
<!-- 错误提示,3秒后自动关闭 -->
<div id="updErrorId" class="alert alert-danger" style="display: none; position: fixed ; top: 30px; right: 30px">
<a href="#" class="close" data-dismiss="alert" aria-label="close">×</a>
<span id="updErrorMsg"></span>
</div>
</div>
<!-- 删除 Modal -->
<div class="modal fade" id="delModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
<h4 class="modal-title" id="delModalLabel">删除订单</h4>
</div>
<div class="modal-body form-horizontal">
<input type="hidden" id="delInputId" name="delInputId" />
<h2 style="color: red">确定删除吗?</h2>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">取消</button>
<button id="del-btn" class="btn btn-primary" onclick="deleteOrder()">提交</button>
</div>
</div>
</div>
</div>
<script>
function save() {
var url = $('#inputUrl').val()
var count = $('#inputCount').val()
if( url === undefined || url === '' ){
$('#urlErrorMsg').text('请输入网址');
$('#inputUrl').focus();
return;
}
if( !url.startsWith('http://') && !url.startsWith('https://')){
$('#urlErrorMsg').text('请输入以`http://`或`https://`的网址');
$('#inputUrl').focus();
return;
}
if( count === undefined || count === '' ){
$('#countErrorMsg').text('请输入数量');
$('#inputCount').focus();
return;
}
if( count < 1 ){
$('#countErrorMsg').text('数量值必须大于0');
$('#inputCount').focus();
return;
}
if( count == '' || count < 1){
$('#inputCount').focus();
return;
}
$.ajax({
type: "POST",
url: "/order/create",
data: JSON.stringify({
'url': url,
'count': count
}),
dataType: "json",
headers: {
'Content-Type': 'application/json; charset=utf-8',
'Access-Control-Allow-Origin': '*'
},
success: function(data){
if(data.code === 0){
window.location.reload();
showMsg(data.msg)
}else{
showError(data.msg)
}
}
});
}
document.getElementById('inputUrl').addEventListener('input', function(event) {
if ($(this).val() === '') {
$('#urlErrorMsg').text('请输入网址');
$("#save-btn").prop("disabled", true);
} else if (!$(this).val().startsWith('http://') && !$(this).val().startsWith('https://')){
$('#urlErrorMsg').text('请输入以`http://`或`https://`的网址');
$("#save-btn").prop("disabled", true);
}else{
$('#urlErrorMsg').text('');
if ($('#inputCount').val() !== ''){
$("#save-btn").prop("disabled", false);
}
}
});
document.getElementById('inputCount').addEventListener('input', function(event) {
if ($(this).val() === '') {
$('#countErrorMsg').text('请输入数量');
$("#save-btn").prop("disabled", true);
}else if ($(this).val() < 1){
$('#countErrorMsg').text('数量值必须大于0');
$("#save-btn").prop("disabled", true);
}else{
$('#countErrorMsg').text('');
if ($('#inputUrl').val() !== ''){
$("#save-btn").prop("disabled", false);
}
}
});
function showError(msg) {
let sec = 5;
$('#errorId').css('display', 'block');
$('#errorMsg').html(msg + ';<br/><br/><u>' + sec + '</u> 秒后关闭');
const timer = setInterval(function () {
$('#errorMsg').html(msg + ';<br/><br/><u>' + (sec - 1) + '</u> 秒后关闭');
if (sec <= 1) {
$('#errorId').css('display', 'none');
clearInterval(timer);
}
sec--;
}, 1000);
}
function showMsg(msg) {
let sec = 5;
$('#infoId').css('display', 'block');
$('#infoMsg').html(msg + ';<br/><br/><u>' + sec + '</u> 秒后关闭');
const timer = setInterval(function () {
$('#errorMsg').html(msg + ';<br/><br/><u>' + (sec - 1) + '</u> 秒后关闭');
if (sec <= 1) {
$('#errorId').css('display', 'none');
clearInterval(timer);
}
sec--;
}, 1000);
}
function showUpdate(obj) {
obj = obj.replaceAll("'", '"').replaceAll(""", '')
const item = JSON.parse(obj);
$("#updModal").modal('show');
$("#updInputId").val(item.id);
$("#updInputOrderNo").val(item.order_no)
$("#old_url").val(item.url);
$("#updInputUrl").val(item.url);
$("#old_count").val(item.count);
$("#updInputCount").val(item.count);
}
function update() {
var id = $('#updInputId').val()
var url = $('#updInputUrl').val()
var count = $('#updInputCount').val()
if( id === undefined || id === '' ){
showUpdError('请输入ID');
return;
}
if( url === undefined || url === '' ){
$('#updUrlErrorMsg').text('请输入网址');
$('#updInputUrl').focus();
return;
}
if( !url.startsWith('http://') && !url.startsWith('https://')){
// 提示错误信息
$('#updUrlErrorMsg').text('请输入以`http://`或`https://`的网址');
$('#updInputUrl').focus();
return;
}
if( count === undefined || count === '' ){
$('#updCountErrorMsg').text('请输入数量');
$('#inputCount').focus();
return;
}
if( count < 1 ){
$('#updCountErrorMsg').text('数量值必须大于0');
$('#updInputCount').focus();
return;
}
if( count == '' || count < 1){
$('#updInputUrl').focus();
return;
}
$.ajax({
type: "POST",
url: "/order/update",
data: JSON.stringify({
'id': id,
'url': url,
'count': count
}),
dataType: "json",
headers: {
'Content-Type': 'application/json; charset=utf-8',
'Access-Control-Allow-Origin': '*'
},
success: function(data){
if(data.code === 0){
window.location.reload();
showMsg(data.msg)
}else{
showUpdError(data.msg)
}
}
});
}
document.getElementById('updInputUrl').addEventListener('input', function(event) {
if ($(this).val() === '') {
$('#updUrlErrorMsg').text('请输入网址');
$("#upd-btn").prop("disabled", true);
} else if (!$(this).val().startsWith('http://') && !$(this).val().startsWith('https://')){
$('#updUrlErrorMsg').text('请输入以`http://`或`https://`的网址');
$("#upd-btn").prop("disabled", true);
} else {
$('#updUrlErrorMsg').text('');
if ($('#updInputCount').val() !== ''){
$('#updCountErrorMsg').text('');
$("#upd-btn").prop("disabled", false);
}
if ($('#updInputCount').val() === $('#old_count').val() && $('#old_url').val() === $('#updInputUrl').val() ){
$('#updCountErrorMsg').text('资料没变动,请先修改');
$("#upd-btn").prop("disabled", true);
}
}
});
document.getElementById('updInputCount').addEventListener('input', function(event) {
if ($(this).val() === '') {
$('#updCountErrorMsg').text('请输入数量');
$("#upd-btn").prop("disabled", true);
}else if ($(this).val() < 1){
$('#updCountErrorMsg').text('数量值必须大于0');
$("#upd-btn").prop("disabled", true);
} else if ($('#updInputCount').val() === $('#old_count').val() && $('#old_url').val() === $('#updInputUrl').val() ){
$('#updCountErrorMsg').text('资料没变动,请先修改');
$("#upd-btn").prop("disabled", true);
} else{
$('#updInputCount').text('');
if ($('#updInputUrl').val() !== ''){
$('#updCountErrorMsg').text('');
$("#upd-btn").prop("disabled", false);
}
}
});
function showUpdError(msg) {
let sec = 5;
//将errorId的display属性设置为block
$('#updErrorId').css('display', 'block');
$('#updErrorMsg').html(msg + ';<br/><br/><u>' + sec + '</u> 秒后关闭');
const timer1 = setInterval(function () {
$('#updErrorMsg').html(msg + ';<br/><br/><u>' + (sec - 1) + '</u> 秒后关闭');
if (sec <= 1) {
$('#updErrorId').css('display', 'none');
clearInterval(timer1);
}
sec--;
}, 1000);
}
function showDelete( id ){
$("#delModal").modal('show');
$("#delInputId").val(id);
}
function deleteOrder(){
var id = $('#delInputId').val()
if (id === '' || id === undefined)
return
$.ajax({
type: "POST",
url: "/order/delete",
data: JSON.stringify({
'id': id
}),
dataType: "json",
headers: {
'Content-Type': 'application/json; charset=utf-8',
'Access-Control-Allow-Origin': '*'
},
success: function(data){
window.location.reload();
}
});
}
</script>
{%endblock%}
{% extends 'layout.html' %}
{%block body%}
<div class="container" style="margin-top: 10px">
{% if user_info.role == 2 %}
<div style="margin-bottom: 10px">
<a type="button" class="btn btn-info" onclick="showAddUser()"><span class="glyphicon glyphicon-plus" aria-hidden="true"></span> 添加用户</a>
</div>
{% endif %}
<table class="table table-bordered table-hover">
<thead>
<tr>
<th>编号</th>
<th>姓名</th>
<th>手机号</th>
<th>角色</th>
<th>创建时间</th>
<th>修改时间</th>
<th>操作</th>
</tr>
</thead>
<tbody>
{% for item in data_list %}
<tr>
<td>{{item.user_no}}</td>
<td>{{item.real_name}}</td>
<td>{{item.mobile}}</td>
<td><span class="label label-{{item.role_msg.cls}}">{{item.role_msg.text}}</span></td>
<td>{{item.create_time}}</td>
<td>{{item.update_time}}</td>
<td><a type="button" class="btn btn-info btn-sm" onclick='showUpdateUser("{{item}}")'>修改用户</a></td>
</tr>
<span style="display: none">{{item.status}}</span>
{% endfor %}
</tbody>
</table>
{% if pageinfo.page > 1 or pageinfo.islastpage == 0 %}
<nav aria-label="Page navigation" style="text-align: right">
<ul class="pagination">
{% if pageinfo.page > 1 %}
<li>
<a href="/users?page={{pageinfo.page-1}}" aria-label="Previous">
<span aria-hidden="true">«</span>
</a>
</li>
{% endif %}
<li><a href="#"><span id="curPageId">{{pageinfo.page}}</span></a></li>
{% if pageinfo.islastpage == 0 %}
<li>
<a href="/users?page={{pageinfo.page+1}}" aria-label="Next">
<span aria-hidden="true">»</span>
</a>
</li>
{% endif %}
</ul>
</nav>
{% endif %}
</div>
<!-- 模态框 -->
<div class="modal fade" id="myModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
<h4 class="modal-title" id="modalTitleLabel">新增用户</h4>
</div>
<div id="modelBody" class="modal-body form-horizontal">
。。。。
</div>
<!-- 错误提示,3秒后自动关闭 -->
<div id="errorId" class="alert alert-danger" style="display:none; margin:0 15px 10px 15px;">
<a href="#" class="close" data-dismiss="alert" aria-label="close">×</a>
<span id="errorMsg"></span>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">取消</button>
<button id="save-btn" class="btn btn-primary" onclick="save()">提交</button>
</div>
</div>
</div>
</div>
<script>
function showAddUser() {
$("#myModal").modal('show');
$("#modalTitleLabel").text('新增用户');
$("#modelBody").html('' +
'<div class="form-group">' +
'<label class="col-sm-2 control-label">姓名</label>' +
'<div class="col-sm-10">' +
'<input type="text" class="form-control" id="real_name" placeholder="请输入姓名">' +
'</div>' +
'</div>' +
'<div class="form-group">' +
'<label class="col-sm-2 control-label">密码</label>' +
'<div class="col-sm-10">' +
'<input type="password" class="form-control" id="passwd" placeholder="请输入密码">' +
'</div>' +
'</div>'+
'<div class="form-group">' +
'<label class="col-sm-2 control-label">手机号</label>' +
'<div class="col-sm-10">' +
'<input type="text" class="form-control" id="mobile" placeholder="请输入手机号">' +
'</div>' +
'</div>'+
'<div class="checkbox">'+
'<label class="col-sm-2 control-label"></label>' +
'<label>'+
'<input type="checkbox" id="isManager"> 管理员'+
'</label>'+
'</div>'
)
}
function showUpdateUser(obj) {
obj = obj.replaceAll("'", '"').replaceAll(""", '')
const item = JSON.parse(obj);
$("#myModal").modal('show');
$("#modalTitleLabel").text('修改用户');
$("#modelBody").html('' +
'<div class="form-group">' +
'<input type="hidden" class="form-control" id="id" value="' + item.id + '">' +
'</div>' +
'<div class="form-group">' +
'<label class="col-sm-2 control-label">编号</label>' +
'<div class="col-sm-10">' +
'<input type="text" disabled class="form-control" value="' + item.user_no + '">' +
'</div>' +
'</div>' +
'<div class="form-group">' +
'<label class="col-sm-2 control-label">姓名</label>' +
'<div class="col-sm-10">' +
'<input type="text" class="form-control" id="real_name" placeholder="请输入姓名" required value="' + item.real_name + '">' +
'</div>' +
'</div>' +
'<div class="form-group">' +
'<label class="col-sm-2 control-label">密码</label>' +
'<div class="col-sm-10">' +
'<input type="password" class="form-control" id="passwd" placeholder="若不修改,可放空">' +
'</div>' +
'</div>'+
'<div class="form-group">' +
'<label class="col-sm-2 control-label">手机号</label>' +
'<div class="col-sm-10">' +
'<input type="text" class="form-control" id="mobile" placeholder="请输入手机号" required value="' + item.mobile + '">' +
'</div>' +
'</div>'+
'<div class="checkbox">'+
'<label class="col-sm-2 control-label"></label>' +
'<label>'+
'<input type="checkbox" id="isManager"> 管理员'+
'</label>'+
'</div>')
$("#isManager").prop("checked", item.role === 2)
}
function save() {
const id = $("#id").val();
const real_name = $("#real_name").val();
const mobile = $("#mobile").val();
const passwd = $("#passwd").val();
const isManager = $("#isManager").prop("checked");
if (real_name === '') {
showError('请输入姓名');
return;
}
if (id === undefined && passwd === '') {
showError('新增用户时,需要输入密码');
return;
}
if (mobile === '') {
showError('请输入手机号');
return;
}
$.ajax({
type: "POST",
url: "/user/create",
data: JSON.stringify({
id: id,
real_name: real_name,
mobile: mobile,
passwd: passwd,
isManager: isManager
}),
headers: {
'Content-Type': 'application/json; charset=utf-8',
'Access-Control-Allow-Origin': '*'
},
success: function (data) {
console.log(data);
if (data.code === 0) {
window.location.reload();
} else {
showError(data.msg);
}
},
error: function (data) {
showError('服务器错误');
}
});
}
function showError(msg) {
let sec = 5;
//将errorId的display属性设置为block
$('#errorId').css('display', 'block');
$('#errorMsg').html(msg + '; <span style="position:fixed; right: 50px"><u>' + sec + '</u> 秒后关闭</span>');
const timer = setInterval(function () {
$('#errorMsg').html(msg + '; <span style="position:fixed; right: 50px"><u>' + (sec - 1) + '</u> 秒后关闭</span>');
if (sec <= 1) {
$('#errorId').css('display', 'none');
clearInterval(timer);
}
sec--;
}, 1000);
}
</script>
{%endblock%}
from flask import Blueprint, render_template, request, redirect, session, jsonify
from utils import mydbutils, mytimeutils, mycryputils, myutils
from dict import Global_Dict
#创建蓝图对象
ac = Blueprint('account', __name__)
@ac.route('/')
def index():
return 'index'
@ac.route('/login', methods=['GET', 'POST'])
def login():
if request.method == 'GET':
return render_template('login.html')
role = request.form.get('role')
mobile = request.form.get('mobile')
pwd = request.form.get('pwd')
sql = "select * from p_user where mobile = %s and role = %s and password = %s"
user_dict = mydbutils.select_one(sql, [mobile, role, mycryputils.md5(pwd)])
if not user_dict:
return render_template('login.html', error='用户名或密码错误')
session["user_info"] = {
"id": user_dict['id'],
"user_no": user_dict['user_no'],
"real_name": user_dict['real_name'],
"mobile": user_dict['mobile'],
"role": user_dict['role']
}
return redirect('/users?page=1')
@ac.route('/users')
def users():
user_info = session.get('user_info')
role = user_info['role']
page = int(request.args.get('page', 1))
page_size = 10
if role == 2:
data_list = mydbutils.select_all("select id,user_no,mobile,real_name,role,c_time,m_time from p_user order by role desc, m_time desc limit %s, %s",[(page-1)*page_size, page_size])
else:
data_list = mydbutils.select_all("select id,user_no,mobile,real_name,role,c_time,m_time from p_user where id = %s",[user_info['id']])
for data in data_list:
data['role_msg'] = Global_Dict.role_info_dict.get(data['role'])
if data['role_msg'] is None:
data['role_msg'] = Global_Dict.role_info_dict.get(1)
data['create_time'] = mytimeutils.longtime2string(data['c_time'])
data['update_time'] = mytimeutils.longtime2string(data['m_time'])
pageinfo = {
'page': page,
'islastpage': 1 if len(data_list) < page_size else 0,
}
return render_template('user_list.html', user_info = user_info, data_list = data_list, pageinfo=pageinfo)
@ac.route('/user/create', methods=['POST'])
def user_create():
user_info = session.get('user_info')
user_id = request.json.get('id')
if not user_info or (user_info['role'] != 2 and int(user_info['id']) != int(user_id)):
return jsonify({'code': 1, 'msg': '您没有权限'})
mobile = request.json.get('mobile')
real_name = request.json.get('real_name')
pwd = request.json.get('passwd')
isManager = request.json.get('isManager')
role = 2 if isManager is True else 1
if not mobile or not real_name:
return jsonify({'code': 1, 'msg': '参数错误'})
if user_id is not None and int(user_id) > 0:
upduserinfo = mydbutils.select_one("select * from p_user where id = %s", [user_id])
if not upduserinfo:
return jsonify({'code': 1, 'msg': '用户不存在'})
if int(user_id) == 1 :
role = 2
if not pwd:
params = [mobile, real_name, role, mytimeutils.getcurrentlongsecondtime(), upduserinfo['password'], user_id]
else:
params = [mobile, real_name, role, mytimeutils.getcurrentlongsecondtime(), mycryputils.md5(pwd), user_id]
res = mydbutils.update("update p_user set mobile = %s, real_name = %s, role = %s, m_time = %s, password = %s where id = %s", params)
if res != 1:
return jsonify({'code': 1, 'msg': '修改失败'})
return jsonify({'code': 0, 'msg': '修改成功'})
else:
params = [myutils.getuuid(), mobile, real_name, mycryputils.md5(pwd), role, mytimeutils.getcurrentlongsecondtime(), mytimeutils.getcurrentlongsecondtime()]
res = mydbutils.insert("insert into p_user(user_no,mobile,real_name,password,role,c_time,m_time) values(%s,%s,%s,%s,%s,%s,%s)", params)
if int(res) < 1:
return jsonify({'code': 1, 'msg': '创建失败'})
return jsonify({'code': 0, 'msg': '创建成功'})
@ac.route('/logout')
def logout():
session.pop('user_info')
return redirect('/login')
import uuid
from flask import Blueprint, session, request, render_template, redirect, jsonify
from utils import mydbutils, myredisutils, mytimeutils, myutils
from dict import Global_Dict
#创建蓝图对象
od = Blueprint('order', __name__)
@od.route('/order/list')
def order_list():
user_info = session.get('user_info')
role = user_info['role']
page = int(request.args.get('page', 1))
page_size = 10
if role == 2:
data_list = mydbutils.select_all("select od.id,od.url,od.count,od.`status`,od.order_no,od.active,od.c_time,od.m_time, u.real_name from p_order od LEFT JOIN p_user u on od.user_no = u.user_no order by od.m_time desc limit %s, %s", [ (page-1)*page_size, page_size ])
else :
data_list = mydbutils.select_all("select od.id,od.url,od.count,od.`status`,od.order_no,od.active,od.c_time,od.m_time, u.real_name from p_order od LEFT JOIN p_user u on od.user_no = u.user_no where od.user_no = %s and od.active = 1 order by od.m_time desc limit %s, %s", [user_info['user_no'], (page-1)*page_size, page_size])
if data_list is None :
pageinfo1 = {
'page': page,
'islastpage': 1,
}
return render_template('order_list.html', data_list={}, pageinfo=pageinfo1)
if len(data_list) == 0 and page > 1:
return redirect('/order/list?page=%s' % (page-1))
for data in data_list:
data['status_msg'] = Global_Dict.order_status_dict.get(data['status'])
if data['status_msg'] is None:
data['status_msg'] = Global_Dict.order_status_dict.get(998)
data['create_time'] = mytimeutils.longtime2string(data['c_time'])
data['update_time'] = mytimeutils.longtime2string(data['m_time'])
pageinfo = {
'page': page,
'islastpage': 1 if len(data_list) < page_size else 0,
}
return render_template('order_list.html', data_list=data_list, pageinfo=pageinfo)
@od.route('/order/create', methods=['POST'])
def order_create():
user_info = session.get('user_info')
if not user_info or user_info['id'] <= 0:
return jsonify({'code': 1, 'msg': '请先登录'})
url = request.json.get('url')
count = request.json.get('count')
if not url or not count :
return jsonify({'code': 1, 'msg': '参数错误'})
if not url.startswith('http://') and not url.startswith('https://'):
return jsonify({'code': 1, 'msg': '网址必须是http://或者https://开头的'})
if not count.isdigit() or int(count) < 1:
return jsonify({'code': 1, 'msg': '数量必须是数字且不小于100'})
order_no = myutils.getuuid()
params = [order_no, url, count, user_info['user_no'], mytimeutils.getcurrentlongsecondtime(),mytimeutils.getcurrentlongsecondtime()]
orderId = mydbutils.insert("insert into p_order(order_no,url,count,user_no,`status`,c_time,m_time) values(%s,%s,%s,%s,0,%s,%s)", params)
if orderId <= 0:
return jsonify({'code': 1, 'msg': '创建失败'})
myredisutils.push_queue(order_no)
return jsonify({'code': 0, 'msg': '创建成功'})
@od.route('/order/update', methods=['POST'])
def order_update():
order_id = request.json.get('id')
url = request.json.get('url')
count = request.json.get('count')
user_info = session.get('user_info')
orderinfo = mydbutils.select_one("select * from p_order where id = %s and active = 1", [order_id])
if not orderinfo:
return jsonify({'code': 1, 'msg': '订单不存在'})
if orderinfo['status'] != 0:
return jsonify({'code': 1, 'msg': '当前订单状态禁止修改'})
if not user_info or (user_info['user_no'] != orderinfo['user_no'] and user_info['role'] != 2):
return jsonify({'code': 1, 'msg': '您没权限修改该订单'})
if not url or not count:
return jsonify({'code': 1, 'msg': '参数错误'})
if not url.startswith('http://') and not url.startswith('https://'):
return jsonify({'code': 1, 'msg': '网址必须是http://或者https://开头的'})
if not count.isdigit() or int(count) < 100:
return jsonify({'code': 1, 'msg': '数量必须是数字且不小于100'})
params = [url, count, mytimeutils.getcurrentlongsecondtime(), order_id]
res = mydbutils.update("update p_order set url = %s, count = %s, m_time = %s where id = %s", params)
if res != 1:
return jsonify({'code': 1, 'msg': '修改失败'})
return jsonify({'code': 0, 'msg': '修改成功'})
@od.route('/order/delete', methods=['POST'])
def order_delete():
id = request.json.get('id')
user_info = session.get('user_info')
orderinfo = mydbutils.select_one("select * from p_order where id = %s and active = 1", [id])
if not orderinfo:
return jsonify({'code': 1, 'msg': '订单不存在'})
if not user_info or (user_info['user_no'] != orderinfo['user_no'] and user_info['role'] != 2):
return jsonify({'code': 1, 'msg': '您没权限删除该订单'})
res = mydbutils.update("update p_order set active = 0, m_time = %s where id = %s", [mytimeutils.getcurrentlongsecondtime(), id])
if res != 1:
return jsonify({'code': 1, 'msg': '删除失败'})
return jsonify({'code': 0, 'msg': '删除成功'})
from flask import Flask, request, session, redirect
def auth():
if request.path == '/login' or request.path.startswith('/static'):
return
if session.get('user_info'):
return
return redirect('/login')
def getLoginUserInfo():
return session.get('user_info')
def create_app():
# 使用Flask创建app
app = Flask(__name__)
# 设置密钥 session用到
app.secret_key = '123456'
# 注册蓝图
from .views import account
from .views import order
app.register_blueprint(account.ac)
app.register_blueprint(order.od)
#设置拦截器
app.before_request(auth)
# 设置全局模板函数
app.template_global()(getLoginUserInfo)
# 设置session过期时间 无操作15分钟过期
app.permanent_session_lifetime = 900
# 设置session自动保存
@app.before_request
def before_request():
session.modified = True
return app
import time
from utils import myredisutils, mydbutils, mytimeutils
from concurrent.futures import ThreadPoolExecutor
def main():
while True:
orderId = myredisutils.pop_queue()
if orderId is None:
print(f"等待任务订单...")
continue
order_info = mydbutils.select_one("select * from p_order where order_no = %s and active = 1", [orderId])
if order_info is None:
print(f"{orderId} 订单不存在")
continue
if order_info['status'] != 0:
print(f"{orderId} 订单状态异常")
continue
try:
print(f"{orderId} 开始处理订单")
## 更新订单状态
mydbutils.update("update p_order set `status` = 1, m_time = %s where order_no = %s", [mytimeutils.getcurrentlongsecondtime(),orderId])
print(f"{orderId} 正在运行的订单状态更新完成")
# 模拟处理订单
time.sleep(5)
thread_pool = ThreadPoolExecutor(max_workers=10)
for i in range(order_info['count']):
thread_pool.submit(task_excute, order_info)
thread_pool.shutdown(wait= True)
print(f"{orderId} 订单处理完成")
## 更新订单状态
mydbutils.update("update p_order set `status` = 2, m_time = %s where order_no = %s",[mytimeutils.getcurrentlongsecondtime(), orderId])
print(f"{orderId} 处理完成订单状态更新完成")
except Exception as e:
print(f"{orderId} 订单处理异常:{e}")
## 订单处理异常
mydbutils.update("update p_order set `status` = 3, m_time = %s where order_no = %s",[mytimeutils.getcurrentlongsecondtime(), orderId])
print(f"{orderId} 处理异常订单状态更新完成")
def initsync():
# 同步数据库里有,队列里没有的数据
## 1.查询数据库中所有存在且是待执行的订单
db_list = mydbutils.select_all("select order_no from p_order where active = 1 and status = 0", [])
db_no_list = { item['order_no'] for item in db_list}
## 2.查询队列中所有存在且是待执行的订单
cache_no_list = myredisutils.select_all()
need_push = db_no_list - cache_no_list
myredisutils.push_queues(need_push)
def task_excute(info):
pass
if __name__ == '__main__':
initsync()
main()
# 这行代码看起来是要将data列表中的每个元素先解码为UTF-8字符串,然后再转换为整数
# 但是data变量在上文中被初始化为空列表[],所以这行代码实际上不会执行任何操作
# 如果data是从Redis或其他地方获取的字节数据,则需要先正确赋值给data变量
# 此处保留原逻辑,但实际运行时data需要包含有效的编码数据
# data = []
# datas = [int(item.decode('utf-8')) for item in data] # 将字节对象转换为整数
import hashlib
def md5(text):
obj = hashlib.md5(text.encode("utf-8"))
return obj.hexdigest()
import pymysql
from dbutils.pooled_db import PooledDB
POOL = PooledDB(
creator=pymysql, #使用链接数据库的模块
maxconnections=5, # 连接池允许的最大连接数,0和None表示不限制连接数
mincached=1, # 初始化时,链接池中至少创建的空闲的链接,0表示不创建
maxcached=2, # 链接池中最多闲置的链接,0和None不限制
maxshared=3, # 链接池中最小共享的链接数量,0和None表示全部共享
blocking=True, # 连接池中如果没有可用连接后,是否阻塞等待。 True:等待 False:不等待然后报错
ping=0, # ping MySQL服务端,检查是否服务可用。 0 不检查, 1 默认,请求时检查, 2 创建cursor时检查, 4 操作的时候检查, 7 一直检查
setsession=[], # 开始会话前执行的命令列表 如:["set datestyle to.….","set time zone"]
host="10.100.2.250",
port=3306,
user="root",
password="root",
database="test",
charset="utf8"
)
def select_one(sql, params):
"""
# 1.新增(需commit)
cursor.execute("insert into tbl(name,password) values('武沛齐','123123')")
conn.commit()
# 2.删除(需commit)
cursor.execute("delete from tbl where id=l")
conn.commit()
# 3.修改(需commit)
cursor.execute("update tb1 set name='xx' where id=1")
conn.commit()
# 4.查询
cursor.execute("select *from tb where id>10")
data =cursor.fetchone() #cursor.fetchall()
print(data)
# 关闭连接
cursor.close()
conn.close()
"""
try:
conn = POOL.connection()
curses = conn.cursor(cursor=pymysql.cursors.DictCursor)
curses.execute(sql, params)
result = curses.fetchone()
curses.close()
conn.close() # 将连接还给连接池
return result
except Exception as e:
print(e)
return None
def select_all(sql, params):
try :
conn = POOL.connection()
curses = conn.cursor(cursor=pymysql.cursors.DictCursor)
curses.execute(sql, params)
result = curses.fetchall()
curses.close()
conn.close()
return result
except Exception as e:
print(e)
return None
def insert(sql, params):
try :
conn = POOL.connection()
curses = conn.cursor(cursor=pymysql.cursors.DictCursor)
curses.execute(sql, params)
conn.commit()
curses.close()
conn.close()
return curses.lastrowid
except Exception as e:
print(e)
def update(sql, params):
try :
conn = POOL.connection()
curses = conn.cursor(cursor=pymysql.cursors.DictCursor)
res = curses.execute(sql, params)
conn.commit()
curses.close()
conn.close()
return res
except Exception as e:
print(e)
def delete(sql, params):
try :
conn = POOL.connection()
curses = conn.cursor(cursor=pymysql.cursors.DictCursor)
res = curses.execute(sql, params)
conn.commit()
curses.close()
conn.close()
return res
except Exception as e:
print(e)
import redis
REDIS_CONN_PARAMS = {
"host" : "10.100.2.230",
"password" : "123456",
"port" : 6379,
"db" : 1,
"encoding" : "utf-8",
"max_connections" : 100
}
REDIS_POOL = redis.ConnectionPool(**REDIS_CONN_PARAMS)
TASK_QUEUE = "order_queue"
def push_queue(value):
conn = redis.Redis(connection_pool=REDIS_POOL)
if value:
conn.lpush(TASK_QUEUE, value)
def push_queues(value):
conn = redis.Redis(connection_pool=REDIS_POOL)
if value:
conn.lpush(TASK_QUEUE, *value)
def pop_queue():
conn = redis.Redis(connection_pool=REDIS_POOL)
data = conn.brpop(TASK_QUEUE, timeout=10)
if not data:
return
return data[1].decode('utf-8')
def select_all():
conn = redis.Redis(connection_pool=REDIS_POOL)
total_count = conn.llen(TASK_QUEUE)
cache_list = conn.lrange(TASK_QUEUE, 0, total_count)
cache_no_list = { item.decode('utf-8') for item in cache_list }
return cache_no_list
import time
def longtime2string(longtime):
return time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(longtime))
def getcurrentlongsecondtime():
return time.time()
import uuid
def getuuid():
return str(uuid.uuid4()).replace('-', '')
from order import create_app
app = create_app()
if __name__ == '__main__':
app.run(host='0.0.0.0', port=80, debug=True)
CREATE TABLE `p_user` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`user_no` varchar(32) NOT NULL COMMENT '用户编号',
`mobile` varchar(11) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '手机号,用于登录',
`password` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL,
`real_name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '用户真实姓名',
`role` tinyint(1) DEFAULT '1' COMMENT '角色 1普通用户 2管理员',
`c_time` bigint(20) DEFAULT NULL COMMENT '创建时间',
`m_time` bigint(20) DEFAULT NULL COMMENT '修改时间',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=13 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
INSERT INTO `p_user` (`user_no`, `mobile`, `password`, `real_name`, `role`, `c_time`, `m_time`) VALUES ( 'aa16f307ee7641bc9d391c8613c42ff1', '130', '202cb962ac59075b964b07152d234b70', '管理员', 2, 1768492800, 1768869917);
CREATE TABLE `p_order` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`user_no` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '用户id',
`url` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '链接',
`count` int(11) DEFAULT NULL COMMENT '数量',
`order_no` varchar(32) NOT NULL COMMENT '订单号',
`status` tinyint(1) DEFAULT NULL COMMENT '0待执行 1正在执行 2完成 3失败',
`active` tinyint(1) DEFAULT '1' COMMENT '是否存在 0已删除 1存在',
`c_time` bigint(20) DEFAULT '0' COMMENT '创建时间',
`m_time` bigint(20) DEFAULT '0' COMMENT '修改时间',
PRIMARY KEY (`id`),
KEY `idx_user_no` (`user_no`)
) ENGINE=InnoDB AUTO_INCREMENT=22 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
asgiref==3.11.0
blinker==1.9.0
Booktype==1.5
click==8.3.1
colorama==0.4.6
DBUtils==3.1.2
Django==6.0.1
Flask==3.1.2
itsdangerous==2.2.0
Jinja2==3.1.6
MarkupSafe==3.0.3
PyMySQL==1.1.2
redis==7.1.0
setuptools==80.9.0
simplejson==3.20.2
sqlparse==0.5.5
tzdata==2025.3
Werkzeug==3.1.5
评论