From 5ec303610ce1de4fcb563bc04531ca6c24ad8e0a Mon Sep 17 00:00:00 2001 From: archer <545436317@qq.com> Date: Wed, 22 Mar 2023 12:20:27 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E6=94=AF=E4=BB=98?= =?UTF-8?q?=E5=8F=AF=E8=83=BD=E5=AD=98=E5=9C=A8=E7=9A=84=E7=BC=BA=E9=99=B7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/user.ts | 9 +-- src/components/WxConcat/index.tsx | 49 +++++++++++++ src/pages/api/user/checkPayResult.ts | 76 +++++++++++--------- src/pages/api/user/getBill.ts | 2 +- src/pages/api/user/getPayCode.ts | 14 +++- src/pages/api/user/getPayOrders.ts | 31 ++++++++ src/pages/chat/components/SlideBar.tsx | 31 +------- src/pages/number/components/PayModal.tsx | 34 +++++---- src/pages/number/setting.tsx | 90 ++++++++++++++++++++++-- src/service/models/pay.ts | 12 +++- src/service/models/user.ts | 2 +- src/types/mongoSchema.d.ts | 9 +++ 12 files changed, 266 insertions(+), 93 deletions(-) create mode 100644 src/components/WxConcat/index.tsx create mode 100644 src/pages/api/user/getPayOrders.ts diff --git a/src/api/user.ts b/src/api/user.ts index d568860ae..c16310de3 100644 --- a/src/api/user.ts +++ b/src/api/user.ts @@ -4,7 +4,7 @@ import { ResLogin } from './response/user'; import { EmailTypeEnum } from '@/constants/common'; import { UserType, UserUpdateParams } from '@/types/user'; import type { PagingData, RequestPaging } from '@/types'; -import { BillSchema } from '@/types/mongoSchema'; +import { BillSchema, PaySchema } from '@/types/mongoSchema'; import { adaptBill } from '@/utils/adapt'; export const sendCodeToEmail = ({ email, type }: { email: string; type: `${EmailTypeEnum}` }) => @@ -56,11 +56,12 @@ export const getUserBills = (data: RequestPaging) => data: res.data.map((bill) => adaptBill(bill)) })); +export const getPayOrders = () => GET(`/user/getPayOrders`); + export const getPayCode = (amount: number) => GET<{ codeUrl: string; - orderId: string; + payId: string; }>(`/user/getPayCode?amount=${amount}`); -export const checkPayResult = (orderId: string) => - GET(`/user/checkPayResult?orderId=${orderId}`); +export const checkPayResult = (payId: string) => GET(`/user/checkPayResult?payId=${payId}`); diff --git a/src/components/WxConcat/index.tsx b/src/components/WxConcat/index.tsx new file mode 100644 index 000000000..7e3bbb66f --- /dev/null +++ b/src/components/WxConcat/index.tsx @@ -0,0 +1,49 @@ +import React from 'react'; +import { + Box, + Button, + Modal, + ModalOverlay, + ModalContent, + ModalHeader, + ModalFooter, + ModalBody, + ModalCloseButton, + useColorModeValue +} from '@chakra-ui/react'; +import Image from 'next/image'; + +const WxConcat = ({ onClose }: { onClose: () => void }) => { + return ( + + + + wx交流群 + + + + + 微信号:{' '} + + YNyiqi + + + + + + + + + + ); +}; + +export default WxConcat; diff --git a/src/pages/api/user/checkPayResult.ts b/src/pages/api/user/checkPayResult.ts index 5b3a98488..b7cf945fb 100644 --- a/src/pages/api/user/checkPayResult.ts +++ b/src/pages/api/user/checkPayResult.ts @@ -3,56 +3,68 @@ import { jsonRes } from '@/service/response'; import axios from 'axios'; import { connectToDatabase, User, Pay } from '@/service/mongo'; import { authToken } from '@/service/utils/tools'; -import { formatPrice } from '@/utils/user'; +import { PaySchema } from '@/types/mongoSchema'; export default async function handler(req: NextApiRequest, res: NextApiResponse) { try { const { authorization } = req.headers; - let { orderId } = req.query as { orderId: string }; + let { payId } = req.query as { payId: string }; const userId = await authToken(authorization); + await connectToDatabase(); + + // 查找订单记录校验 + const payOrder = await Pay.findById(payId); + + if (!payOrder) { + throw new Error('订单不存在'); + } + if (payOrder.status !== 'NOTPAY') { + throw new Error('订单已结算'); + } + const { data } = await axios.get( - `https://sif268.laf.dev/wechat-order-query?order_number=${orderId}&api_key=${process.env.WXPAYCODE}` + `https://sif268.laf.dev/wechat-order-query?order_number=${payOrder.orderId}&api_key=${process.env.WXPAYCODE}` ); if (data.trade_state === 'SUCCESS') { - await connectToDatabase(); - - // 重复记录校验 - const count = await Pay.count({ - orderId - }); - - if (count > 0) { - throw new Error('订单重复,请刷新'); - } - - // 计算实际充值。把分转成数据库的值 - const price = data.amount.total * 0.01 * 100000; - let payId; + // 订单已支付 try { - // 充值记录 +1 - const payRecord = await Pay.create({ - userId, - price, - orderId - }); - payId = payRecord._id; - // 充钱 - await User.findByIdAndUpdate(userId, { - $inc: { balance: price } - }); + // 更新订单状态 + const updateRes = await Pay.updateOne( + { + _id: payId, + status: 'NOTPAY' + }, + { + status: 'SUCCESS' + } + ); + if (updateRes.modifiedCount === 1) { + // 给用户账号充钱 + await User.findByIdAndUpdate(userId, { + $inc: { balance: payOrder.price } + }); + jsonRes(res, { + data: 'success' + }); + } } catch (error) { - payId && Pay.findByIdAndDelete(payId); + await Pay.findByIdAndUpdate(payId, { + status: 'NOTPAY' + }); + console.log(error); } - - jsonRes(res, { - data: 'success' + } else if (data.trade_state === 'CLOSED') { + // 订单已关闭 + await Pay.findByIdAndUpdate(payId, { + status: 'CLOSED' }); } else { throw new Error(data.trade_state_desc); } + throw new Error('订单已过期'); } catch (err) { console.log(err); jsonRes(res, { diff --git a/src/pages/api/user/getBill.ts b/src/pages/api/user/getBill.ts index 126a4f9ba..dec69fd28 100644 --- a/src/pages/api/user/getBill.ts +++ b/src/pages/api/user/getBill.ts @@ -25,7 +25,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) const bills = await Bill.find({ userId }) - .sort({ createdAt: -1 }) // 按照创建时间倒序排列 + .sort({ time: -1 }) // 按照创建时间倒序排列 .skip((pageNum - 1) * pageSize) .limit(pageSize); diff --git a/src/pages/api/user/getPayCode.ts b/src/pages/api/user/getPayCode.ts index 1a2c391b3..8a5b17fc9 100644 --- a/src/pages/api/user/getPayCode.ts +++ b/src/pages/api/user/getPayCode.ts @@ -4,6 +4,8 @@ import { jsonRes } from '@/service/response'; import axios from 'axios'; import { authToken } from '@/service/utils/tools'; import { customAlphabet } from 'nanoid'; +import { connectToDatabase, Pay } from '@/service/mongo'; + const nanoid = customAlphabet('abcdefghijklmnopqrstuvwxyz1234567890', 20); export default async function handler(req: NextApiRequest, res: NextApiResponse) { @@ -15,9 +17,10 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) if (!authorization) { throw new Error('缺少登录凭证'); } - await authToken(authorization); + const userId = await authToken(authorization); const id = nanoid(); + await connectToDatabase(); const response = await axios({ url: 'https://sif268.laf.dev/wechat-pay', @@ -29,9 +32,16 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) } }); + // 充值记录 + 1 + const payOrder = await Pay.create({ + userId, + price: amount * 100000, + orderId: id + }); + jsonRes(res, { data: { - orderId: id, + payId: payOrder._id, codeUrl: response.data?.code_url } }); diff --git a/src/pages/api/user/getPayOrders.ts b/src/pages/api/user/getPayOrders.ts new file mode 100644 index 000000000..892fcd706 --- /dev/null +++ b/src/pages/api/user/getPayOrders.ts @@ -0,0 +1,31 @@ +import type { NextApiRequest, NextApiResponse } from 'next'; +import { jsonRes } from '@/service/response'; +import { authToken } from '@/service/utils/tools'; +import { connectToDatabase, Pay } from '@/service/mongo'; + +export default async function handler(req: NextApiRequest, res: NextApiResponse) { + try { + const { authorization } = req.headers; + + if (!authorization) { + throw new Error('缺少登录凭证'); + } + const userId = await authToken(authorization); + + await connectToDatabase(); + + const records = await Pay.find({ + userId + }).sort({ createTime: -1 }); + + jsonRes(res, { + data: records + }); + } catch (err) { + console.log(err); + jsonRes(res, { + code: 500, + error: err + }); + } +} diff --git a/src/pages/chat/components/SlideBar.tsx b/src/pages/chat/components/SlideBar.tsx index d29f5cd18..154b6ae85 100644 --- a/src/pages/chat/components/SlideBar.tsx +++ b/src/pages/chat/components/SlideBar.tsx @@ -32,7 +32,7 @@ import { useCopyData } from '@/utils/tools'; import Markdown from '@/components/Markdown'; import { shareHint } from '@/constants/common'; import { getChatSiteId } from '@/api/chat'; -import Image from 'next/image'; +import WxConcat from '@/components/WxConcat'; const SlideBar = ({ name, @@ -305,34 +305,7 @@ const SlideBar = ({ {/* wx 联系 */} - - - - wx交流群 - - - - - 微信号:{' '} - - YNyiqi - - - - - - - - - + {isOpenWx && } ); }; diff --git a/src/pages/number/components/PayModal.tsx b/src/pages/number/components/PayModal.tsx index 5a6f0e5dc..903ec39ea 100644 --- a/src/pages/number/components/PayModal.tsx +++ b/src/pages/number/components/PayModal.tsx @@ -15,14 +15,14 @@ import { import { getPayCode, checkPayResult } from '@/api/user'; import { useToast } from '@/hooks/useToast'; import { useQuery } from '@tanstack/react-query'; -import { useUserStore } from '@/store/user'; +import { useRouter } from 'next/router'; const PayModal = ({ onClose }: { onClose: () => void }) => { + const router = useRouter(); const { toast } = useToast(); - const { initUserInfo } = useUserStore(); const [inputVal, setInputVal] = useState(''); const [loading, setLoading] = useState(false); - const [orderId, setOrderId] = useState(''); + const [payId, setPayId] = useState(''); const handleClickPay = useCallback(async () => { if (!inputVal || inputVal <= 0 || isNaN(+inputVal)) return; @@ -38,7 +38,7 @@ const PayModal = ({ onClose }: { onClose: () => void }) => { colorLight: '#ffffff', correctLevel: QRCode.CorrectLevel.H }); - setOrderId(res.orderId); + setPayId(res.payId); } catch (error) { toast({ title: '出现了一些意外...', @@ -50,21 +50,20 @@ const PayModal = ({ onClose }: { onClose: () => void }) => { }, [inputVal, toast]); useQuery( - [orderId], + [payId], () => { - if (!orderId) return null; - return checkPayResult(orderId); + if (!payId) return null; + return checkPayResult(payId); }, { refetchInterval: 2000, onSuccess(res) { if (!res) return; - onClose(); - initUserInfo(); toast({ title: '充值成功', status: 'success' }); + router.reload(); } } ); @@ -74,17 +73,17 @@ const PayModal = ({ onClose }: { onClose: () => void }) => { { - if (orderId) return; + if (payId) return; onClose(); }} > 充值 - {!orderId && } + {!payId && } - {!orderId && ( + {!payId && ( <> {[5, 10, 20, 50].map((item) => ( @@ -112,18 +111,23 @@ const PayModal = ({ onClose }: { onClose: () => void }) => { )} {/* 付费二维码 */} - {orderId && 请微信扫码支付: {inputVal}元,请勿关闭页面} + {payId && 请微信扫码支付: {inputVal}元,请勿关闭页面} - {!orderId && ( + {!payId && ( <> - diff --git a/src/pages/number/setting.tsx b/src/pages/number/setting.tsx index c0c9557a2..701c4f87e 100644 --- a/src/pages/number/setting.tsx +++ b/src/pages/number/setting.tsx @@ -13,12 +13,13 @@ import { TableContainer, Select, Input, - IconButton + IconButton, + useDisclosure } from '@chakra-ui/react'; import { DeleteIcon } from '@chakra-ui/icons'; import { useForm, useFieldArray } from 'react-hook-form'; import { UserUpdateParams } from '@/types/user'; -import { putUserInfo, getUserBills } from '@/api/user'; +import { putUserInfo, getUserBills, getPayOrders, checkPayResult } from '@/api/user'; import { useToast } from '@/hooks/useToast'; import { useGlobalStore } from '@/store/global'; import { useUserStore } from '@/store/user'; @@ -27,6 +28,10 @@ import { usePaging } from '@/hooks/usePaging'; import type { UserBillType } from '@/types/user'; import { useQuery } from '@tanstack/react-query'; import dynamic from 'next/dynamic'; +import { PaySchema } from '@/types/mongoSchema'; +import dayjs from 'dayjs'; +import { formatPrice } from '@/utils/user'; +import WxConcat from '@/components/WxConcat'; const PayModal = dynamic(() => import('./components/PayModal')); @@ -37,6 +42,7 @@ const NumberSetting = () => { defaultValues: userInfo as UserType }); const [showPay, setShowPay] = useState(false); + const { isOpen: isOpenWx, onOpen: onOpenWx, onClose: onCloseWx } = useDisclosure(); const { toast } = useToast(); const { fields: accounts, @@ -50,6 +56,7 @@ const NumberSetting = () => { api: getUserBills, pageSize: 30 }); + const [payOrders, setPayOrders] = useState([]); const onclickSave = useCallback( async (data: UserUpdateParams) => { @@ -69,6 +76,31 @@ const NumberSetting = () => { useQuery(['init'], initUserInfo); + useQuery(['initPayOrder'], getPayOrders, { + onSuccess(res) { + setPayOrders(res); + } + }); + + const handleRefreshPayOrder = useCallback( + async (payId: string) => { + try { + setLoading(true); + await checkPayResult(payId); + const res = await getPayOrders(); + setPayOrders(res); + } catch (error: any) { + toast({ + title: error?.message, + status: 'warning' + }); + console.log(error); + } + setLoading(false); + }, + [setLoading, toast] + ); + return ( <> @@ -161,11 +193,55 @@ const NumberSetting = () => { + + + + 充值记录 + + + + + + + + + + + + + + + + {payOrders.map((item) => ( + + + + + + + + ))} + +
订单号时间金额消费
{item.orderId} + {item.createTime ? dayjs(item.createTime).format('YYYY/MM/DD HH:mm:ss') : '-'} + + {formatPrice(item.price)}元 + {item.status} + {item.status === 'NOTPAY' && ( + + )} +
+
+
- 使用记录 + 使用记录(最新30条) - + @@ -177,8 +253,8 @@ const NumberSetting = () => { {bills.map((item) => ( - - + @@ -189,6 +265,8 @@ const NumberSetting = () => { {showPay && setShowPay(false)} />} + {/* wx 联系 */} + {isOpenWx && } ); }; diff --git a/src/service/models/pay.ts b/src/service/models/pay.ts index f93374656..9ca375318 100644 --- a/src/service/models/pay.ts +++ b/src/service/models/pay.ts @@ -6,9 +6,9 @@ const PaySchema = new Schema({ ref: 'user', required: true }, - time: { - type: Number, - default: () => Date.now() + createTime: { + type: Date, + default: () => new Date() }, price: { type: Number, @@ -17,6 +17,12 @@ const PaySchema = new Schema({ orderId: { type: String, required: true + }, + status: { + // 支付的状态 + type: String, + default: 'NOTPAY', + enum: ['SUCCESS', 'REFUND', 'NOTPAY', 'CLOSED'] } }); diff --git a/src/service/models/user.ts b/src/service/models/user.ts index 3cf5a250e..b54e5fa94 100644 --- a/src/service/models/user.ts +++ b/src/service/models/user.ts @@ -16,7 +16,7 @@ const UserSchema = new Schema({ }, balance: { type: Number, - default: 0.5 + default: 0.5 * 100000 }, accounts: [ { diff --git a/src/types/mongoSchema.d.ts b/src/types/mongoSchema.d.ts index 91e4fd64b..7b8c0bf93 100644 --- a/src/types/mongoSchema.d.ts +++ b/src/types/mongoSchema.d.ts @@ -85,3 +85,12 @@ export interface BillSchema { textLen: number; price: number; } + +export interface PaySchema { + _id: string; + userId: string; + createTime: Date; + price: number; + orderId: string; + status: 'SUCCESS' | 'REFUND' | 'NOTPAY' | 'CLOSED'; +}
{item.time} + {item.time} {item.textLen} {item.price}元