Django + React 電商專案練習 [11] - 後台實作 - 訂單運送狀態更新

Posted by Young on 2022-12-01
Estimated Reading Time 4 Minutes
Words 945 In Total

後端

views

isDelivered 更新為 True 的同時,也要取得現在時間,更新 deliveredAt 欄位

1
2
3
4
5
6
7
8
9
10
11
12
13
# order_views.py
# 更新訂單運送狀態為:已運送
@api_view(['PUT'])
@permission_classes([IsAdminUser])
def updateOrderToDelivered(request, pk):
order = Order.objects.get(id=pk)
order.isDelivered = True
# 取得現在時間
timezone_taipei = pytz.timezone('Asia/Taipei')
now = timezone.now().astimezone(timezone_taipei)
order.deliveredAt = now
order.save()
return Response('訂單狀態已更新為:已運送')

urls

1
2
# order_urls.py
path('<str:pk>/delivered/', views.updateOrderToDelivered, name='order-delivered'),

Postman 測試

老套路,後端寫好 API 後,先用 Postman 測試看是否有成功回傳資料,一樣帶入擁有 admin 權限的 token 來檢視所有訂單資料

orders_postman

前端

constants 定義常數

1
2
3
4
5
// orderConstants.js
export const ORDER_DELIVER_REQUEST = "ORDER_DELIVER_REQUEST";
export const ORDER_DELIVER_SUCCESS = "ORDER_DELIVER_SUCCESS";
export const ORDER_DELIVER_FAIL = "ORDER_DELIVER_FAIL";
export const ORDER_DELIVER_RESET = "ORDER_DELIVER_RESET";

reducers

orderDeliverReducerorderPayReducer 類似,都是更新訂單狀態的 reducer

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// orderReducers.js
// 管理員更新訂單運送狀態為 已運送
export const orderDeliverReducer = (state = {}, action) => {
switch (action.type) {
case ORDER_DELIVER_REQUEST:
return { loading: true };

case ORDER_DELIVER_SUCCESS:
return { loading: false, success: true };

case ORDER_DELIVER_FAIL:
return { loading: false, error: action.payload };

default:
return state;
}
};

好習慣,在 store 註冊完此 reducer 後,都要去前台查看 Redux DevTools 檢查是否有成功註冊

actions

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// orderActions.js
// 管理員更新訂單運送狀態為 已運送
export const deliverOrder = (order) => async (dispatch, getState) => {
try {
dispatch({
type: ORDER_DELIVER_REQUEST,
});

const {
userLogin: { userInfo },
} = getState();

const config = {
headers: {
"Content-type": "application/json",
Authorization: `Bearer ${userInfo.token}`,
},
};

const { data } = await axios.put(`/api/orders/${order.id}/delivered/`, {}, config);

dispatch({
type: ORDER_DELIVER_SUCCESS,
payload: data,
});
} catch (error) {
dispatch({
type: ORDER_DELIVER_FAIL,
payload: error.response && error.response.data.detail ? error.response.data.detail : error.message,
});
}
};

回到訂單明細前端頁面

更新運送狀態按鈕規劃放在訂單明細頁面,所以回到 OrderPage.js,加入觸發 deliverHandler 事件的按鈕

  • 當按下按鈕時,就在 usEffect 中重置 orderDeliver 的狀態
  • 要判斷只有 Admin、且訂單狀態為 已付款未運送 才顯示此按鈕
  • 為此按鈕添加 loading 狀態
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
// OrderListPage.js
const orderDeliver = useSelector((state) => state.orderDeliver);
const { loading: loadingDeliver, success: successDeliver } = orderDeliver;

const { userInfo } = useSelector((state) => state.userLogin);
...
useEffect(() => {
if (!order || successPay || order.id !== Number(orderId) || successDeliver) {
dispatch({ type: ORDER_PAY_RESET }); // 沒 RESET 會一直導致跑 <Loader />
dispatch({ type: ORDER_DELIVER_RESET });

dispatch(getOrderDetails(orderId));
} else if (!order.isPaid) {
setSdkReady(true);
}
}, [dispatch, navigate, order, orderId, successPay, successDeliver]);

...
const deliverOrderHandler = () => {
dispatch(deliverOrder(order));
};

return (
<>
...
{/* 更新訂單運送狀態 按鈕 */}
{loadingDeliver && <Loader />}
{userInfo && userInfo.isAdmin && order.isPaid && !order.isDelivered && (
<ListGroup.Item>
<Button type='button' className='btn btn-block' onClick={deliverOrderHandler}>
更新狀態為已送達
</Button>
</ListGroup.Item>
)}
);
</>

最後一樣至 App.js 加入路由並用 <AdminRoute> 包裹以限制非管理員無法進入此頁面

最後成品畫面如下:

orderlistpage_done

解決無法訪問其他用戶問題

解決非用戶本人,連管理員也沒有權限訪問特定訂單的問題,比如說有另一個用戶的訂單,當初設計時,只有用戶本人可以查看自己的訂單,此邏輯是無誤的

other_user_order

但現在導致管理員也無法查看特定用戶的訂單,就有點不合理,所以這邊回到 order_views.py,在 if order.user == user: 的判斷中加入 or user.is_staff: 的判斷,管理員就能成功查看所有用戶的訂單了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# order_views.py
# 取得單一訂單
@api_view(['GET'])
@permission_classes([IsAuthenticated])
def getOrderById(request, pk):
user = request.user
try:
order = Order.objects.get(id=pk)
if order.user == user or user.is_staff:
serializer = OrderSerializer(order, many=False)
return Response(serializer.data)
else:
return Response({'detail':'你沒有權限查看此訂單'},status=status.HTTP_400_BAD_REQUEST)
except:
return Response({'detail':'訂單不存在'},status=status.HTTP_400_BAD_REQUEST)

若您覺得這篇文章對您有幫助,歡迎分享出去讓更多人看到⊂◉‿◉つ~


留言版