请优化以下前后端代码,我手动修改数据库内容,后端未同步到前端。前端代码:// pages/order-detail/order-detail.js
Page({
data: {
activeIndex: 0, // 当前选中的状态索引
statusList: [‘新订单’, ‘备货中’, ‘待配送’, ‘配送中’, ‘已完成’, ‘已取消’], // 订单状态列表
showActionSheet: false, // 是否显示操作面板
currentOrderId: null, // 当前操作的订单ID
cancelOrderCount: 0, // 取消订单次数
lastCancelDate: null, // 最后一次取消订单的日期
searchText: ‘’, // 搜索关键字
orders: [], // 所有订单数据
filteredOrders: [], // 筛选后的订单数据
isLoading: false, // 是否正在加载数据
page: 1, // 当前页码
pageSize: 10, // 每页大小
hasMore: true ,// 是否还有更多数据
socketOpen: false, // WebSocket 是否已打开
socketTask: null, // WebSocket 实例
socketRetryCount: 0, // WebSocket 重试次数
},
// onLoad() {
// this.loadOrders(); // 加载订单数据
// this.loadCancelInfo(); // 加载取消订单信息
// },
onReachBottom() {
if (this.data.hasMore && !this.data.isLoading) {
this.setData({ page: this.data.page + 1 }, () => {
this.loadOrders();
});
}
},
loadCancelInfo() {
const cancelInfo = wx.getStorageSync(‘cancelOrderInfo’) || {};
this.setData({
cancelOrderCount: cancelInfo.count || 0,
lastCancelDate: cancelInfo.date || null
});
},
onLoad() {
this.loadOrders(); // 加载订单数据
this.initWebSocket(); // 初始化 WebSocket
},
onUnload() {
this.closeWebSocket(); // 页面卸载时关闭 WebSocket
},
// 初始化 WebSocket
// 修改后的 initWebSocket 方法
// 修改后的 initWebSocket 方法
initWebSocket() {
const that = this;
// 先关闭现有连接
this.closeWebSocket();
// 检查是否超过最大重试次数
if (this.data.socketRetryCount >= 5) {
console.log(‘已达到最大重连次数,停止尝试’);
wx.showToast({
title: ‘网络连接不稳定,请检查网络后重试’,
icon: ‘none’
});
return;
}
const socketTask = wx.connectSocket({
url: ‘ws://127.0.0.1:8001/ws/orders/’,
header: {
‘Authorization’: 'Bearer ’ + wx.getStorageSync(‘token’)
},
success: () => {
console.log(‘WebSocket连接初始化成功’);
},
fail: (err) => {
console.error(‘WebSocket连接初始化失败:’, err);
that.handleSocketError();
}
});
socketTask.onOpen(() => {
console.log(‘WebSocket已连接’);
// 发送加入组请求(使用统一组名)
socketTask.send({
data: JSON.stringify({
type: "join_group",
group: "order_updates"
})
});
// 启动心跳
that.heartbeatInterval = setInterval(() => {
if (that.data.socketOpen && socketTask) {
console.log('发送消息:', JSON.stringify({ type: 'heartbeat' }));
socketTask.send({
data: JSON.stringify({ type: 'heartbeat' }),
success: () => console.log('心跳发送成功'),
fail: (err) => {
console.log('心跳发送失败', err);
that.handleSocketError();
}
});
}
}, 25000);
});
socketTask.onMessage((res) => {
try {
const message = JSON.parse(res.data);
console.log(‘收到消息:’, message);
switch (message.type) {
case 'order_update':
this.handleOrderUpdate(message.data);
break;
case 'heartbeat_ack':
console.log('心跳响应:', message.timestamp);
break;
case 'system':
console.log('系统消息:', message.message);
break;
case 'error':
console.error('服务器返回错误:', message);
wx.showToast({
title: message.message || '服务器错误',
icon: 'none'
});
break;
default:
console.warn('未知消息类型:', message.type);
}
} catch (e) {
console.error(‘消息处理错误:’, e);
}
});
socketTask.onError((err) => {
console.error(‘WebSocket错误:’, err);
that.handleSocketError();
});
socketTask.onClose(() => {
console.log(‘WebSocket关闭’);
that.handleSocketClose();
});
this.setData({ socketTask, socketOpen: true });
},
handleSocketError() {
this.setData({ socketOpen: false });
clearInterval(this.heartbeatInterval);
// 增加重连延迟
setTimeout(() => {
this.setData(prev => ({
socketRetryCount: prev.socketRetryCount + 1
}));
console.log(尝试第 ${this.data.socketRetryCount} 次重连...);
this.initWebSocket();
}, 5000); // 5秒后重连
},
handleSocketClose() {
this.setData({ socketOpen: false });
clearInterval(this.heartbeatInterval);
if (!this.data.isManualClose) {
this.handleSocketError();
}
},
closeWebSocket() {
if (this.data.socketTask) {
this.setData({ isManualClose: true });
this.data.socketTask.close();
this.setData({ socketTask: null });
}
clearInterval(this.heartbeatInterval);
},
handleOrderUpdate(data) {
console.log(‘处理订单更新:’, data);
const { id, orderNo, status } = data;
// 更新本地订单状态
const updatedOrders = this.data.orders.map(order => {
if (order.orderId === orderNo) {
return { …order, status };
}
return order;
});
this.setData({
orders: updatedOrders,
filteredOrders: this.filterOrders(updatedOrders)
});
},
filterOrders(orders = this.data.orders) {
const { activeIndex, statusList, searchText } = this.data;
const currentStatus = statusList[activeIndex];
let filtered = orders.filter(order => {
if (currentStatus !== ‘全部’ && order.status !== currentStatus) {
return false;
}
if (searchText && !order.orderId.includes(searchText) &&
!order.customerName.includes(searchText) &&
!order.customerPhone.includes(searchText)) {
return false;
}
return true;
});
filtered.sort((a, b) => new Date(b.createTime) - new Date(a.createTime));
return filtered;
},
async loadOrders() {
if (this.data.isLoading || !this.data.hasMore) return;
this.setData({ isLoading: true });
try {
const { statusList, activeIndex, searchText, page, pageSize } = this.data;
const currentStatus = statusList[activeIndex];
const user = wx.getStorageSync('userInfo');
const shopId = user.id; // 确保已存储shopId
const res = await new Promise((resolve, reject) => {
wx.request({
url: 'http://127.0.0.1:8000/api/gouw/merchant/orders/',
method: 'GET',
header: {
'Authorization': 'Bearer ' + wx.getStorageSync('token'),
'Content-Type': 'application/json'
},
data: {
shop_id: shopId,
status: currentStatus,
search: searchText,
page: page,
page_size: pageSize
},
success: (res) => {
if (res.statusCode === 200) {
resolve(res.data);
} else {
reject(new Error(`HTTP ${res.statusCode}: ${res.data?.message || '请求失败'}`));
}
},
fail: (err) => {
reject(new Error('网络请求失败'));
}
});
});
console.log('API响应数据:', JSON.stringify(res, null, 2));
if (res.code === 200) {
const newOrders = res.data.orders.map(order => ({
id: order.orderId,
orderId: order.orderNo,
customerName: order.customerName,
customerPhone: order.customerPhone,
status: order.status,
statusCode: order.statusCode,
createTime: order.createTime,
totalAmount: order.totalAmount,
products: order.products,
deliveryFee: order.deliveryFee
}));
this.setData({
orders: page === 1 ? newOrders : [...this.data.orders, ...newOrders],
filteredOrders: page === 1 ? newOrders : [...this.data.orders, ...newOrders],
hasMore: page < res.data.pagination.totalPages,
isLoading: false
});
} else {
throw new Error(res.message || '加载订单失败');
}
} catch (err) {
console.error('加载订单错误:', err.message, err);
wx.showToast({
title: err.message || '加载订单失败',
icon: 'none',
duration: 2000
});
this.setData({ isLoading: false });
}
},
// 加载订单数据
// async loadOrders() {
// try {
// const user = wx.getStorageSync(‘userInfo’);
// const shopId = user.id;
// const res = await new Promise((resolve, reject) => {
// wx.request({
// url: ‘http://127.0.0.1:8000/api/gouw/merchant/orders/’,
// method: ‘GET’,
// header: {
// ‘Authorization’: 'Bearer ’ + wx.getStorageSync(‘token’),
// ‘Content-Type’: ‘application/json’
// },
// data: {
// shop_id: shopId,
// status: ‘新订单’,
// page: 1,
// page_size: 10
// },
// success: (res) => {
// if (res.statusCode === 200) {
// resolve(res.data);
// } else {
// reject(new Error(HTTP ${res.statusCode}: ${res.data?.message || '请求失败'}));
// }
// },
// fail: (err) => {
// reject(new Error(‘网络请求失败’));
// }
// });
// });
// if (res.code === 200) {
// const orders = res.data.orders.map(order => ({
// id: order.orderId,
// orderId: order.orderNo,
// customerName: order.customerName,
// customerPhone: order.customerPhone,
// status: order.status,
// statusCode: order.statusCode,
// createTime: order.createTime,
// totalAmount: order.totalAmount,
// products: order.products,
// deliveryFee: order.deliveryFee
// }));
// this.setData({
// orders,
// filteredOrders: orders
// });
// }
// } catch (err) {
// console.error(‘加载订单错误:’, err.message);
// }
// },
// 切换订单状态
switchStatus(e) {
const index = e.currentTarget.dataset.index;
this.setData({
activeIndex: index,
searchText: '',
page: 1,
hasMore: true
}, () => {
this.loadOrders();
});
},
// filterOrders() {
// const { activeIndex, statusList, orders, searchText } = this.data;
// const currentStatus = statusList[activeIndex];
// let filtered = orders.filter(order => {
// if (currentStatus !== ‘全部’ && order.status !== currentStatus) {
// return false;
// }
// if (searchText && !order.orderId.includes(searchText) &&
// !order.customerName.includes(searchText) &&
// !order.customerPhone.includes(searchText)) {
// return false;
// }
// return true;
// });
// filtered.sort((a, b) => new Date(b.createTime) - new Date(a.createTime));
// this.setData({ filteredOrders: filtered });
// },
handleSearchInput(e) {
this.setData({
searchText: e.detail.value
});
},
handleSearch() {
this.setData({
page: 1,
hasMore: true
}, () => {
this.loadOrders();
});
},
showActionSheet(e) {
const orderId = e.currentTarget.dataset.orderid;
this.setData({
showActionSheet: true,
currentOrderId: orderId
});
},
hideActionSheet() {
this.setData({
showActionSheet: false,
currentOrderId: null
});
},
handleCancelOrder(e) {
const orderId = e.currentTarget.dataset.orderid;
const today = new Date().toDateString();
if (this.data.lastCancelDate === today && this.data.cancelOrderCount >= 3) {
wx.showModal({
title: '提示',
content: '当天取消接单次数已经用完,如需继续,请点击申诉',
showCancel: false
});
this.hideActionSheet();
return;
}
let newCount = this.data.cancelOrderCount;
let newDate = this.data.lastCancelDate;
if (newDate !== today) {
newCount = 0;
newDate = today;
}
newCount++;
this.setData({
cancelOrderCount: newCount,
lastCancelDate: newDate
});
wx.setStorageSync('cancelOrderInfo', {
count: newCount,
date: newDate
});
this.updateOrderStatus(orderId, '已取消');
wx.showToast({
title: '订单已取消',
icon: 'success'
});
this.hideActionSheet();
},
handleAppeal(e) {
const orderId = e.currentTarget.dataset.orderid;
this.hideActionSheet();
wx.navigateTo({
url: `/pages/appeal/appeal?orderId=${orderId}`,
success: () => {
console.log('跳转到申诉页面');
},
fail: (err) => {
console.error('跳转失败:', err);
wx.showToast({
title: '跳转失败,请重试',
icon: 'none'
});
}
});
},
handleAcceptOrder(e) {
const orderId = e.currentTarget.dataset.orderid;
this.updateOrderStatus(orderId, ‘备货中’);
wx.showToast({
title: ‘接单成功’,
icon: ‘success’
});
},
handleReadyForDelivery(e) {
const orderId = e.currentTarget.dataset.orderid;
this.updateOrderStatus(orderId, ‘待配送’);
wx.showToast({
title: ‘备货完成’,
icon: ‘success’
});
},
handleStartDelivery(e) {
const orderId = e.currentTarget.dataset.orderid;
this.updateOrderStatus(orderId, ‘配送中’);
wx.showToast({
title: ‘开始配送’,
icon: ‘success’
});
},
handleCompleteDelivery(e) {
const orderId = e.currentTarget.dataset.orderid;
this.updateOrderStatus(orderId, ‘已完成’);
wx.showToast({
title: ‘配送完成’,
icon: ‘success’
});
},
// 更新订单状态
async updateOrderStatus(orderId, newStatus) {
const token = wx.getStorageSync(‘token’);
if (!token) {
wx.showToast({
title: ‘未登录,请重新登录’,
icon: ‘none’
});
return;
}
wx.showLoading({ title: '处理中...', mask: true });
try {
const res = await new Promise((resolve, reject) => {
wx.request({
url: 'http://127.0.0.1:8000/api/gouw/update-order-status/',
method: 'POST',
header: {
'Authorization': 'Bearer ' + token,
'Content-Type': 'application/json'
},
data: {
order_no: orderId,
status: newStatus
},
success: (res) => {
if (res.statusCode === 200 && res.data.code === 200) {
resolve(res.data);
} else {
reject(new Error(res.data?.message || '更新失败'));
}
},
fail: (err) => {
reject(new Error('网络请求失败'));
}
});
});
wx.hideLoading();
wx.showToast({
title: '操作成功',
icon: 'success'
});
// 从本地数据中移除已处理的订单
// 核心修改:更新本地订单状态并重新过滤
const updatedOrders = this.data.orders.map(order => {
if (order.orderId === orderId) {
return { ...order, status: newStatus };
}
return order;
});
this.setData({
// 从完整订单列表移除
orders: this.data.orders.filter(order => order.orderId !== orderId),
// 从当前视图列表直接移除
filteredOrders: this.data.filteredOrders.filter(order => order.orderId !== orderId)
});
wx.hideLoading();
wx.showToast({ title: '操作成功', icon: 'success' });
// this.loadOrders();
// 如果需要重新加载数据,可以调用 this.loadOrders();
} catch (err) {
console.error('更新订单状态失败:', err.message);
wx.hideLoading();
wx.showToast({
title: err.message || '操作失败,请重试',
icon: 'none'
});
}
},
// 辅助方法:匹配搜索条件
matchSearchCriteria(order) {
const searchText = this.data.searchText.toLowerCase();
return !searchText ||
order.orderId.toLowerCase().includes(searchText) ||
order.customerName.toLowerCase().includes(searchText) ||
order.customerPhone.includes(searchText);
},
sj_mb(e) {
wx.navigateTo({
url: “/pages/gr_xx/gr_xx”,
success: () => {
console.log(‘成功跳转到商家信息页面’);
},
fail: (err) => {
console.error(‘跳转失败:’, err);
wx.showToast({
title: ‘跳转失败,请重试’,
icon: ‘none’
});
}
});
},
dp_zt(e) {
wx.navigateTo({
url: “/pages/dp_zt/dp_zt”,
success: () => {
console.log(‘成功跳转到商家信息页面’);
},
fail: (err) => {
console.error(‘跳转失败:’, err);
wx.showToast({
title: ‘跳转失败,请重试’,
icon: ‘none’
});
}
});
},
tk_dd(e) {
wx.navigateTo({
url: “/pages/tk_dd/tk_dd”,
success: () => {
console.log(‘成功跳转到退款订单页面’);
},
fail: (err) => {
console.error(‘跳转失败:’, err);
wx.showToast({
title: ‘跳转失败,请重试’,
icon: ‘none’
});
}
});
}
});
前端运行日志:WebSocket已连接
index.js:116 收到消息: {type: “system”, message: “连接成功”, timestamp: “2025-05-18T15:12:08.829969”}
index.js:126 系统消息: 连接成功
index.js:149 WebSocket关闭
index.js? [sm]:166 尝试第 0 次重连…
index.js? [sm]:78 WebSocket连接初始化成功
index.js? [sm]:87 WebSocket已连接
index.js? [sm]:116 收到消息: {type: “system”, message: “连接成功”, timestamp: “2025-05-18T15:12:13.772510”}
index.js? [sm]:126 系统消息: 连接成功
index.js:166 尝试第 0 次重连…
index.js:78 WebSocket连接初始化成功
index.js:87 WebSocket已连接
index.js:116 收到消息: {type: “system”, message: “连接成功”, timestamp: “2025-05-18T15:12:14.184758”}
index.js:126 系统消息: 连接成功
index.js? [sm]:149 WebSocket关闭
index.js:149 WebSocket关闭
[worker] reportRealtimeAction:fail not support
U @ WAWorker.js:1
invoke @ WAWorker.js:1
invoke @ WAWorker.js:1
G @ WAWorker.js:1
(anonymous) @ WAWorker.js:1
$e @ WAWorker.js:1
Z @ WAWorker.js:1
p @ WAWorker.js:1
(anonymous) @ WAWorker.js:1
(anonymous) @ WAWorker.js:1
setTimeout (async)
globalThis.setTimeout @ WAWorker.js:1
Y @ WAWorker.js:1
Re @ WAWorker.js:1
(anonymous) @ WAWorker.js:1
r @ WAWorker.js:1
s @ WAWorker.js:1
callAndRemove @ WAWorker.js:1
invokeCallbackHandler @ WAWorker.js:1
eval @ VM16:1 后端代码:# DjangoProject1/asgi.py
import os
from django.core.asgi import get_asgi_application
from channels.routing import ProtocolTypeRouter, URLRouter
from channels.auth import AuthMiddlewareStack
from gouw.routing import websocket_urlpatterns
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'DjangoProject1.settings')
application = ProtocolTypeRouter({
"http": get_asgi_application(),
"websocket": AuthMiddlewareStack(
URLRouter(
websocket_urlpatterns
)
),
}) # DjangoProject1/routing.py
from django.urls import re_path, path
from channels.routing import ProtocolTypeRouter, URLRouter
from gouw.routing import websocket_urlpatterns as gouw_websockets
application = ProtocolTypeRouter({
"websocket": URLRouter([
*gouw_websockets, # 将 gouw 的 WebSocket 路由引入
]),
}) # gouw/apps.py
from django.apps import AppConfig
class GouwConfig(AppConfig):
name = 'gouw'
def ready(self):
# 确保信号被加载
import gouw.signals # 这行必须执行
print("信号已加载!") # 添加这行用于调试 # gouw/consumers.py
from channels.generic.websocket import AsyncWebsocketConsumer
import json
from datetime import datetime
import logging
from asgiref.sync import async_to_sync
from channels.layers import get_channel_layer
logger = logging.getLogger(__name__)
# 常量定义
ORDER_GROUP_NAME = "order_updates" # 统一定义组名
def broadcast_order_update(order_data):
"""广播订单更新到所有客户端"""
try:
channel_layer = get_channel_layer()
async_to_sync(channel_layer.group_send)(
ORDER_GROUP_NAME,
{
"type": "order.update", # 注意这里使用点号
"data": { # 添加data层保持结构一致
"id": order_data["id"],
"orderNo": order_data["order_no"],
"status": order_data["status"]
}
}
)
logger.info(f"广播成功: {order_data}")
except Exception as e:
logger.error(f"广播失败: {str(e)}")
class OrderConsumer(AsyncWebsocketConsumer):
groups = [ORDER_GROUP_NAME] # 添加这行确保组名一致
async def connect(self):
"""开放连接(带基本验证)"""
try:
await self.accept()
logger.info(f"新客户端连接: {self.channel_name}")
await self.send(text_data=json.dumps({
"type": "system",
"message": "连接成功",
"timestamp": datetime.now().isoformat()
}))
except Exception as e:
logger.error(f"连接处理错误: {str(e)}")
await self.close(code=4001)
async def disconnect(self, close_code):
"""断开连接处理"""
try:
await self.channel_layer.group_discard(
ORDER_GROUP_NAME,
self.channel_name
)
logger.info(f"客户端断开: {close_code}")
except Exception as e:
logger.error(f"断开连接错误: {str(e)}")
async def receive(self, text_data=None, bytes_data=None):
"""消息处理(带错误保护)"""
try:
data = json.loads(text_data)
logger.debug(f"收到消息: {data}")
# 加入组处理
if data.get("type") == "join_group":
group_name = data.get("group")
if group_name:
await self.channel_layer.group_add(
group_name,
self.channel_name
)
await self.send(text_data=json.dumps({
"type": "system",
"message": f"成功加入组: {group_name}"
}))
else:
await self.send(text_data=json.dumps({
"type": "error",
"code": 4003,
"message": "缺少必要字段: group"
}))
return
# 心跳处理
if data.get("type") == "heartbeat":
await self.send(text_data=json.dumps({
"type": "heartbeat_ack",
"timestamp": datetime.now().isoformat()
}))
return
# 处理其他消息类型
message_type = data.get("type", "unknown")
if message_type == "order_update":
order_id = data.get("id")
order_status = data.get("status")
logger.info(f"收到订单更新: ID={order_id}, 状态={order_status}")
else:
await self.send(text_data=json.dumps({
"type": "error",
"code": 4002,
"message": f"未知的消息类型: {message_type}"
}))
except json.JSONDecodeError as e:
logger.error(f"消息解析失败: {str(e)}")
await self.send(text_data=json.dumps({
"type": "error",
"code": 4001,
"message": "无效的JSON格式"
}))
except KeyError as e:
logger.error(f"缺少必要字段: {str(e)}")
await self.send(text_data=json.dumps({
"type": "error",
"code": 4003,
"message": f"缺少必要字段: {str(e)}"
}))
except Exception as e:
logger.error(f"消息处理异常: {str(e)}")
await self.send(text_data=json.dumps({
"type": "error",
"code": 5000,
"message": "服务器内部错误"
}))
async def order_update(self, event):
"""处理订单更新广播"""
await self.send(text_data=json.dumps({
"type": "order_update",
"data": {
"id": event["data"]["id"],
"orderNo": event["data"]["orderNo"],
"status": event["data"]["status"]
}
})) # gouw/routing.py
from django.urls import re_path
from .consumers import OrderConsumer
websocket_urlpatterns = [
re_path(r'ws/orders/$', OrderConsumer.as_asgi()),
] # # signals.py
# from django.db.models.signals import post_save, post_delete
# from django.dispatch import receiver
# from .models import Order
# from .consumers import broadcast_order_update
# import logging
# logger = logging.getLogger(__name__)
# @receiver(post_save, sender=Order)
# def order_updated(sender, instance, created, **kwargs):
# print(f"订单更新信号触发!订单ID: {instance.id}") # 调试日志
# try:
#
# # 构建标准化消息数据
# order_data = {
# "id": instance.id,
# "order_no": instance.order_no,
# "status": instance.status,
# # "statusCode": instance.status,
# "createTime": instance.create_time.isoformat(),
# "totalAmount": float(instance.total_amount),
# # "customerName": instance.customername,
# # "customerPhone": instance.customerPhone,
# # "products": [
# # {
# # "name": item.product.name,
# # "quantity": item.quantity,
# # "price": float(item.price)
# # } for item in instance.items.all()
# # ]
# }
#
# # 强制广播消息
# broadcast_order_update(order_data)
# print(f"广播订单更新: {instance.id} 状态: {instance.status}")
#
# except Exception as e:
# print(f"订单更新信号处理错误: {str(e)}")
#
# @receiver(post_save, sender=Order)
# def order_updated(sender, instance, **kwargs):
# broadcast_order_update({
# "id": instance.id,
# "order_no": instance.order_no, # 使用下划线命名
# "status": instance.status
# })
# @receiver(post_save, sender=Order)
# def handle_order_update(sender, instance, **kwargs):
# try:
# broadcast_order_update({
# "id": instance.id,
# "order_no": instance.order_no, # 使用下划线命名
# "status": instance.status
# })
# logger.info(f"信号处理完成 - 订单ID: {instance.id} 状态: {instance.status}")
# except Exception as e:
# logger.error(f"信号处理错误: {str(e)}")
from django.db.models.signals import post_save
from django.dispatch import receiver
from .models import Order
from .consumers import broadcast_order_update
import logging
logger = logging.getLogger(__name__)
@receiver(post_save, sender=Order)
def handle_order_update(sender, instance, **kwargs):
"""统一处理订单更新信号"""
try:
logger.info(f"订单更新信号触发 - 订单ID: {instance.id} 状态: {instance.status}")
# 构建标准化消息数据
order_data = {
"id": instance.id,
"order_no": instance.order_no,
"status": instance.status
}
# 广播消息
broadcast_order_update(order_data)
logger.info(f"广播订单更新成功 - 订单ID: {instance.id}")
except Exception as e:
logger.error(f"订单更新信号处理错误: {str(e)}", exc_info=True)
"""
Django settings for DjangoProject1 project.
Generated by 'django-admin startproject' using Django 5.1.6.
For more information on this file, see
https://docs.djangoproject.com/en/5.1/topics/settings/
For the full list of settings and their values, see
https://docs.djangoproject.com/en/5.1/ref/settings/
"""
import os
from datetime import timedelta
from pathlib import Path
# import AES
# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent
# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/5.1/howto/deployment/checklist/
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = 'django-insecure-7f5y$m$)-y81w5h0q*o12ohp$douez_97h-x_!daldl@9%7kvr'
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True
ALLOWED_HOSTS = []
# Application definition
INSTALLED_APPS = [
# 通用
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'rest_framework_simplejwt', # 新增
# 功能
'rest_framework',
'corsheaders',
'django_filters',
'rest_framework.authtoken',
'channels',
# app
'swiper',
'search', # 正确的应用名称
'merchant', # 正确的应用名称
'shop',
"product",
# "gouw",
'gouw.apps.GouwConfig',
"xiad",
"zhif",
"dd",
'wod_denglu',
"dingd",
]
MIDDLEWARE = [
'corsheaders.middleware.CorsMiddleware',
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
# ASGI_APPLICATION = "DjangoProject1.asgi.application" # 确保存在
# ASGI_APPLICATION = 'DjangoProject1.routing.application'
ASGI_APPLICATION = "DjangoProject1.asgi.application"
CHANNEL_LAYERS = {
"default": {
"BACKEND": "channels_redis.core.RedisChannelLayer",
"CONFIG": {
"hosts": [("127.0.0.1", 6379)],
"channel_capacity": {
"http.request": 200,
"websocket.send*": 200,
}
},
},
}
# 信号配置
SIGNAL_HANDLERS = True
TIME_ZONE = 'Asia/Shanghai' # 确保时区正确
USE_TZ = True
CORS_ALLOW_ALL_ORIGINS = True
ROOT_URLCONF = 'DjangoProject1.urls'
# TEMPLATES = [
# { 'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
# 'BACKEND': 'django.template.backends.django.DjangoTemplates',
# 'DIRS': [BASE_DIR / 'templates']
# ,
# 'APP_DIRS': True,
# 'OPTIONS': {
# 'context_processors': [
# 'django.template.context_processors.debug',
# 'django.template.context_processors.request',
# 'django.contrib.auth.context_processors.auth',
# 'django.contrib.messages.context_processors.messages',
# ],
# },
# },
# ]
CORS_ALLOWED_ORIGINS = [
'http://127.0.0.1:8000', # 替换为实际的前端地址
]
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [os.path.join(BASE_DIR, 'templates')],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
WSGI_APPLICATION = 'DjangoProject1.wsgi.application'
CORS_ORIGIN_ALLOW_ALL = True
# Database
# https://docs.djangoproject.com/en/5.1/ref/settings/#databases
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME':"db_44",
'USER': 'root',
'PASSWORD': '123456',
'HOST': 'localhost',
'PORT': '3306',
'charset': 'utf8mb4',
}
}
# Password validation
# https://docs.djangoproject.com/en/5.1/ref/settings/#auth-password-validators
AUTH_PASSWORD_VALIDATORS = [
{
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
},
]
CORS_ALLOW_CREDENTIALS = True
# DRF配置
REST_FRAMEWORK = {
'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
'PAGE_SIZE': 20,
'ORDERING_PARAM': 'ordering',
'DEFAULT_RENDERER_CLASSES': [
'rest_framework.renderers.JSONRenderer',
'rest_framework.authentication.BasicAuthentication',
# 'rest_framework.authentication.SessionAuthentication',
],
# "DEFAULT_AUTHENTICATION_CLASSES": (
# 'rest_framework.permissions.IsAuthenticated',
# ),
# "DEFAULT_PERMISSION_CLASSES": ("rest_framework.permissions.IsAuthenticated",),
#
# 'DEFAULT_AUTHENTICATION_CLASSES': [
# 'rest_framework.authentication.TokenAuthentication',
# ],
'DEFAULT_PERMISSION_CLASSES': [
'rest_framework.permissions.AllowAny',
# 'rest_framework.permissions.IsAuthenticated',
],
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework_simplejwt.authentication.JWTAuthentication',
# 'rest_framework_simplejwt.authentication.JWTAuthentication', # 替换原来的 JWT 认证
# #Token认证
# 'rest_framework.authentication.TokenAuthentication',
# 'rest_framework.authentication.BasicAuthentication',
),
'EXCEPTION_HANDLER': 'rest_framework.views.exception_handler',
}
AUTH_USER_MODEL = 'wod_denglu.User'
# AUTH_USER_MODEL = None # 替换为实际应用名
# 配置文件上传路径
MEDIA_ROOT = os.path.join(BASE_DIR, "media")
MEDIA_URL = "/media/"
# 在settings.py 中启用调试模式
DEBUG = True
# Internationalization
# https://docs.djangoproject.com/en/5.1/topics/i18n/
LANGUAGE_CODE = 'en-us'
TIME_ZONE = 'UTC'
USE_I18N = True
USE_TZ = True
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/5.1/howto/static-files/
STATIC_URL = 'static/'
# Default primary key field type
# https://docs.djangoproject.com/en/5.1/ref/settings/#default-auto-field
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
# #
# # 日志配置
LOGGING = {
'version': 1,
'handlers': {
'console': {
'class': 'logging.StreamHandler',
},
},
'loggers': {
'gouw': {
'handlers': ['console'],
'level': 'DEBUG',
},
},
}
# 允许所有域名(开发环境临时方案)
CORS_ORIGIN_ALLOW_ALL = True
# 配置允许的请求头
CORS_ALLOW_HEADERS = [
'accept',
'accept-encoding',
'authorization',
'content-type',
'dnt',
'origin',
'user-agent',
'x-csrftoken',
'x-requested-with',
]
# 配置允许的HTTP方法
CORS_ALLOW_METHODS = [
'GET',
'POST',
'PUT',
'DELETE',
'OPTIONS'
]
#
# # 增强型JWT配置
# JWT_SETTINGS = {
# 'SECRET_KEY': os.getenv('JWT_SECRET'), # 32位随机字符串
# 'ALGORITHM': 'HS256',
# 'ACCESS_TOKEN_LIFETIME': timedelta(hours=2),
# 'REFRESH_TOKEN_LIFETIME': timedelta(days=7),
# 'AUTH_HEADER_PREFIX': 'Bearer'
# }
# # 微信配置
# WECHAT = {
# 'APP_ID': os.environ.get('WECHAT_APP_ID'),
# 'APP_SECRET': os.environ.get('WECHAT_APP_SECRET'),
# 'LOGIN_URL': 'https://api.weixin.qq.com/sns/jscode2session'
# }
#
# # AES加密配置(用于手机号解密)
# AES_SETTINGS = {
# 'MODE': AES.MODE_CBC,
# 'BLOCK_SIZE': 32,
# 'PADDING': '\0'
# }
# 微信配置
WX_APPID= 'wx5bb48b2fd7ec3e54'
WX_SECRET = '6ba10b53c9bdf24aaa0f026f147e0200'
WX_MCHID = '1230004567' # 新增
WX_API_KEY = 'aBcDeFgHiJkLmNoPqRsTuVwXyZ' # 新增
WX_NOTIFY_URL = 'http:/http://127.0.0.1:8000/api/zhif/wx-notify/'
WECHAT_APPID = 'wx5bb48b2fd7ec3e54'
WECHAT_SECRET = '6ba10b53c9bdf24aaa0f026f147e0200'
WECHAT_APP_ID = 'wx5bb48b2fd7ec3e54'
WECHAT_APP_SECRET = '6ba10b53c9bdf24aaa0f026f147e0200'
WECHAT_MINI_PROGRAM = {
'APPID': 'wx5bb48b2fd7ec3e54',
'SECRET': '6ba10b53c9bdf24aaa0f026f147e0200'
}
WECHAT_TOKEN = "90_RzkT6EObTjmOA4E0O2ouANm7iYvTauxbwnTFkcTEu-3ggitiQnpzpvHE6-JUfbGThiqvmOdHBF4TEE3rf9GE_1MvDoBzhPEIj24OWmT0ulQkJ4iq95JweKmIBa8OQZfAGASUD"
# 默认头像配置
DEFAULT_AVATAR_URL = 'https://tse2-mm.cn.bing.net/th/id/OIP-C.1eW3_u1T8He1QmfKH_ctngAAAA?rs=1&pid=ImgDetMain'
# JWT配置
JWT_EXPIRATION_DAYS = 7
SIMPLE_JWT = {
# 'AUTH_TOKEN_CLASSES': ('shop.auth.ShopJWTAuthentication',),
# # 'TOKEN_USER_CLASS': 'shop.models.Shop',
# 'USER_ID_FIELD': 'id',
# 'USER_ID_CLAIM': 'user_id',
'USER_AUTHENTICATION_RULE': 'shop.auth.allow_all_users', # 新增规则
# 添加商家用户类型支持
'TOKEN_USER_CLASSES': (
'wod_denglu.models.User',
'shop.models.Shop'
),
# 'AUTH_TOKEN_CLASSES': ('shop.auth.ShopJWTAuthentication',),
# 'USER_ID_FIELD': 'uuid', # 使用唯一标识字段
'TOKEN_USER_CLASS': 'wod_denglu.models.User', # 主用户类
'TOKEN_OBTAIN_SERIALIZER': 'wod_denglu.serializers.CustomTokenObtainPairSerializer',
# 'USER_AUTHENTICATION_RULE': 'rest_framework_simplejwt.authentication.default_user_authentication_rule',
'ACCESS_TOKEN_LIFETIME': timedelta(days=7), # 按需配置有效期
'REFRESH_TOKEN_LIFETIME': timedelta(days=30),
'ROTATE_REFRESH_TOKENS': False,
'BLACKLIST_AFTER_ROTATION': True,
# 调试专用配置
'VERIFYING_KEY': None,
'AUDIENCE': None,
'ISSUER': None,
'JWK_URL': None,
'LEEWAY': 0,
'AUTH_HEADER_TYPES': ('Bearer',),
'AUTH_HEADER_NAME': 'HTTP_AUTHORIZATION',
'USER_ID_FIELD': 'id', # 确保与用户模型匹配
'USER_ID_CLAIM': 'user_id',
# 'USER_AUTHENTICATION_RULE': 'rest_framework_simplejwt.authentication.default_user_authentication_rule',
'AUTH_TOKEN_CLASSES': ('rest_framework_simplejwt.tokens.AccessToken','shop.auth.ShopJWTAuthentication'),
'TOKEN_TYPE_CLAIM': 'token_type',
# 开启详细错误
'SHOW_AUTHENTICATION_ERRORS': True,
'SIGNING_KEY': '6ba10b53c9bdf24aaa0f026f147e0200', # 替换为强密钥
}
# 安全配置
SECRET_KEY = '6ba10b53c9bdf24aaa0f026f147e0200'
# AUTHENTICATION_BACKENDS = [
# 'django.contrib.auth.backends.ModelBackend',
# 'shop.backends.ShopBackend', # 如果你使用自定义后端
# ]
# # 指定自定义用户模型
# AUTH_USER_MODEL = 'shop.Shop' # 根据应用名调整
# 配置认证后端
AUTHENTICATION_BACKENDS = [
'shop.backends.ShopAuthBackend', # 商家认证
'wod_denglu.backends.CustomerAuthBackend', # 消费者认证
'django.contrib.auth.backends.ModelBackend',
]
# JWT配置
# SIMPLE_JWT = {
# 'USER_ID_FIELD': 'id',
# 'USER_ID_CLAIM': 'user_id',
# 'AUTH_TOKEN_CLASSES': ('rest_framework_simplejwt.tokens.AccessToken',),
# }
#
# # 认证后端配置
# AUTHENTICATION_BACKENDS = [
# 'django.contrib.auth.backends.ModelBackend',
# 'shop.backends.ShopBackend',
# ]
#
# # 用户模型配置
# AUTH_USER_MODEL = 'shop.Shop'
#
# # JWT配置
# SIMPLE_JWT = {
# 'ACCESS_TOKEN_LIFETIME': timedelta(days=1),
# 'REFRESH_TOKEN_LIFETIME': timedelta(days=7),
# 'ROTATE_REFRESH_TOKENS': False,
# 'BLACKLIST_AFTER_ROTATION': True,
# 'ALGORITHM': 'HS256',
# 'SIGNING_KEY': SECRET_KEY,
# 'VERIFYING_KEY': None,
# 'AUTH_HEADER_TYPES': ('Bearer',),
# 'USER_ID_FIELD': 'id',
# 'USER_ID_CLAIM': 'user_id',
# 'AUTH_TOKEN_CLASSES': ('rest_framework_simplejwt.tokens.AccessToken',),
# }
DATA_UPLOAD_MAX_MEMORY_SIZE = 10 * 1024 * 1024 # 10MB
FILE_UPLOAD_MAX_MEMORY_SIZE = 2 * 1024 * 1024 # 2MB
FILE_UPLOAD_PERMISSIONS = 0o644
# LOGGING = {
# 'version': 1,
# 'disable_existing_loggers': False,
# 'handlers': {
# 'file': {
# 'level': 'DEBUG',
# 'class': 'logging.FileHandler',
# 'filename': 'debug.log',
# },
# 'console': {
# 'level': 'DEBUG',
# 'class': 'logging.StreamHandler',
# },
# },
# 'loggers': {
# 'django': {
# 'handlers': ['file', 'console'],
# 'level': 'DEBUG',
# 'propagate': True,
# },
# },
# } 后端:(.venv) PS D:\ajango后端项目\DjangoProject1> daphne -p 8001 DjangoProject1.asgi:application
信号已加载!
127.0.0.1:64486 - - [18/May/2025:15:12:08] "WSCONNECTING /ws/orders/" - -
127.0.0.1:64486 - - [18/May/2025:15:12:08] "WSCONNECT /ws/orders/" - -
新客户端连接: specific.214396818bd94fb9a6c1eead947f0c54!2d9cb14738ae43b0aa0c3b9b1de9101f
2025-05-18 15:12:08,828 INFO 新客户端连接: specific.214396818bd94fb9a6c1eead947f0c54!2d9cb14738ae43b0aa0c3b9b1de9101f
127.0.0.1:64486 - - [18/May/2025:15:12:09] "WSDISCONNECT /ws/orders/" - -
127.0.0.1:51483 - - [18/May/2025:15:12:13] "WSCONNECTING /ws/orders/" - -
127.0.0.1:51483 - - [18/May/2025:15:12:13] "WSCONNECT /ws/orders/" - -
新客户端连接: specific.214396818bd94fb9a6c1eead947f0c54!28560ecf1ac54d55833d143161e19e72
2025-05-18 15:12:13,772 INFO 新客户端连接: specific.214396818bd94fb9a6c1eead947f0c54!28560ecf1ac54d55833d143161e19e72
127.0.0.1:54431 - - [18/May/2025:15:12:14] "WSCONNECTING /ws/orders/" - -
127.0.0.1:54431 - - [18/May/2025:15:12:14] "WSCONNECT /ws/orders/" - -
新客户端连接: specific.214396818bd94fb9a6c1eead947f0c54!499cf5873e334b06bcbd90d97b2329f0
2025-05-18 15:12:14,183 INFO 新客户端连接: specific.214396818bd94fb9a6c1eead947f0c54!499cf5873e334b06bcbd90d97b2329f0
127.0.0.1:51483 - - [18/May/2025:15:12:14] "WSDISCONNECT /ws/orders/" - -
127.0.0.1:54431 - - [18/May/2025:15:12:14] "WSDISCONNECT /ws/orders/" - -