feat: 支付闭环 + 运营助手 Tool Calling + 管理后台完善
- 支付系统:微信支付 mock 自动完成、NATIVE 扫码支付、JSAPI 集成 - 运营助手:Tool Calling 架构,19 个可执行工具,AI 驱动操作 - 角色管理:表格布局 + Dialog 表单 + 权限勾选 - 配置统一:config.ts 单一数据源 - API 审计:补齐 status toggle / comments 端点 - 暗黑模式硬件编码颜色全部替换为 CSS 变量
This commit is contained in:
@@ -4,7 +4,19 @@ import { useEffect, useState } from 'react';
|
||||
import Link from 'next/link';
|
||||
import { apiFetch } from '../../../lib/auth';
|
||||
import { Skeleton } from '@/components/ui/skeleton';
|
||||
import PaymentModal from '@/components/ui/payment-modal';
|
||||
import { useT } from '@/i18n';
|
||||
import { isWeChatBrowser, getOpenidFromUrl, isMiniProgram } from '@/lib/wechat';
|
||||
|
||||
interface PayResult {
|
||||
prepay_id?: string;
|
||||
nonceStr?: string;
|
||||
timeStamp?: string;
|
||||
package?: string;
|
||||
paySign?: string;
|
||||
signType?: string;
|
||||
codeUrl?: string;
|
||||
}
|
||||
|
||||
interface Subscription {
|
||||
id: number; plan: string; startDate: string; endDate: string; status: string;
|
||||
@@ -29,6 +41,9 @@ export default function MemberPage() {
|
||||
const [quota, setQuota] = useState<{ used: number; remaining: number; dailyLimit: number } | null>(null);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [payLoading, setPayLoading] = useState<string | null>(null);
|
||||
const [paymentModal, setPaymentModal] = useState<{
|
||||
open: boolean; orderNo: string; payResult: PayResult; tradeType: 'JSAPI' | 'NATIVE';
|
||||
}>({ open: false, orderNo: '', payResult: {}, tradeType: 'NATIVE' });
|
||||
|
||||
useEffect(() => { loadData(); }, []);
|
||||
|
||||
@@ -51,22 +66,43 @@ export default function MemberPage() {
|
||||
async function handleSubscribe(planType: string) {
|
||||
setPayLoading(planType);
|
||||
try {
|
||||
const inWeChat = isWeChatBrowser();
|
||||
const openid = getOpenidFromUrl();
|
||||
const useJsapi = inWeChat && openid && isMiniProgram();
|
||||
const tradeType = useJsapi ? 'JSAPI' : 'NATIVE';
|
||||
|
||||
const body: Record<string, any> = {
|
||||
amount: planType === 'MONTHLY' ? 29.9 : 199,
|
||||
planType, payChannel: 'wxpay', tradeType,
|
||||
};
|
||||
if (useJsapi && openid) body.openid = openid;
|
||||
|
||||
const res = await apiFetch('/orders/create', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
amount: planType === 'MONTHLY' ? 29.9 : 199,
|
||||
planType, payChannel: 'wxpay',
|
||||
}),
|
||||
body: JSON.stringify(body),
|
||||
});
|
||||
const data = await res.json();
|
||||
|
||||
if (data.order && data.payResult) {
|
||||
alert('订单创建成功,请扫码支付(模拟模式)');
|
||||
loadData();
|
||||
// Mock mode → auto-completed by backend
|
||||
if (data.payResult.codeUrl === 'mock://pay') {
|
||||
loadData();
|
||||
} else {
|
||||
setPaymentModal({
|
||||
open: true, orderNo: data.order.orderNo,
|
||||
payResult: data.payResult, tradeType,
|
||||
});
|
||||
}
|
||||
}
|
||||
} catch (e) { console.error(e) }
|
||||
setPayLoading(null);
|
||||
}
|
||||
|
||||
function handlePaymentPaid() {
|
||||
setPaymentModal(prev => ({ ...prev, open: false }));
|
||||
loadData();
|
||||
}
|
||||
|
||||
if (loading) return (
|
||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-12">
|
||||
<Skeleton className="h-8 w-48 mb-2" />
|
||||
@@ -161,6 +197,14 @@ export default function MemberPage() {
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<PaymentModal
|
||||
open={paymentModal.open}
|
||||
orderNo={paymentModal.orderNo}
|
||||
payResult={paymentModal.payResult}
|
||||
tradeType={paymentModal.tradeType}
|
||||
onPaid={handlePaymentPaid}
|
||||
onClose={() => setPaymentModal(prev => ({ ...prev, open: false }))}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user