update package store

This commit is contained in:
Zhang Minghan 2023-09-07 22:43:01 +08:00
parent 8d111a0a31
commit f9aac47b67
11 changed files with 218 additions and 10 deletions

View File

@ -60,8 +60,9 @@ func TextChat(db *sql.DB, user *auth.User, conn *websocket.Conn, instance *conve
SendSegmentMessage(conn, types.ChatGPTSegmentResponse{
Message: defaultErrorMessage,
Quota: buffer.GetQuota(),
End: false,
End: true,
})
return defaultErrorMessage
}
// collect quota

View File

@ -29,6 +29,7 @@ import {login, tokenField} from "./conf.ts";
import { useTranslation } from "react-i18next";
import Quota from "./routes/Quota.tsx";
import {openDialog} from "./store/quota.ts";
import Package from "./routes/Package.tsx";
function Settings() {
const { t } = useTranslation();
@ -113,6 +114,7 @@ function App() {
<RouterProvider router={router} />
<Toaster />
<Quota />
<Package />
</Provider>
);
}

View File

View File

@ -30,6 +30,7 @@
.buy-button {
width: 100%;
transition: .25s;
}
}

View File

@ -0,0 +1,34 @@
import axios from "axios";
type QuotaResponse = {
status: boolean;
error: string;
}
type PackageResponse = {
status: boolean;
cert: boolean;
teenager: boolean;
}
export async function buyQuota(
quota: number,
): Promise<QuotaResponse> {
try {
const resp = await axios.post(`/buy`, { quota });
return resp.data as QuotaResponse;
} catch (e) {
console.debug(e);
return { status: false, error: "network error" };
}
}
export async function getPackage(): Promise<PackageResponse> {
try {
const resp = await axios.get(`/package`);
return resp.data as PackageResponse;
} catch (e) {
console.debug(e);
return { status: false, cert: false, teenager: false };
}
}

View File

@ -66,6 +66,23 @@ const resources = {
"output": "Output",
"tip": "Prices have been aligned (or lower) to OpenAI models",
"learn-more": "Learn more",
"dialog-title": "Buy Points",
"dialog-desc": "Are you sure you want to buy {{amount}} points?",
"dialog-cancel": "Cancel",
"dialog-buy": "Buy",
"success": "Purchase successful",
"success-prompt": "You have successfully purchased {{amount}} points.",
"failed": "Purchase failed",
"failed-prompt": "Failed to purchase points. Please make sure you have enough balance, you will soon jump to deeptrain wallet to pay balance.",
},
pkg: {
"title": "Packages",
"go": "Go to",
"verify": "Verify",
"cert": "Certification Package",
"cert-desc": "After real-name certification, you can get 50 points (worth 5 CNY)",
"teen": "Teenager Package",
"teen-desc": "After real-name certification, teenagers (18 years old and below) can get an additional 150 points (worth 15 CNY)",
}
},
},
@ -124,6 +141,23 @@ const resources = {
"output": "输出",
"tip": "价格已对齐OpenAI模型或低于官方价格",
"learn-more": "了解更多",
"dialog-title": "购买点数",
"dialog-desc": "您确定要购买 {{amount}} 点数吗?",
"dialog-cancel": "取消",
"dialog-buy": "购买",
"success": "购买成功",
"success-prompt": "您已成功购买 {{amount}} 点数。",
"failed": "购买失败",
"failed-prompt": "购买点数失败。请确保您有足够的余额,您即将跳转到 deeptrain 钱包支付余额。",
},
pkg: {
"title": "礼包",
"go": "前往",
"verify": "实名认证",
"cert": "实名认证礼包",
"cert-desc": "实名认证后可获得 50 点数 (价值 5 元)",
"teen": "未成年人福利",
"teen-desc": "实名认证后未成年人18 周岁及以下)可额外获得 150 点数 (价值 15 元)",
}
},
},

View File

@ -0,0 +1,41 @@
import {
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
} from "../components/ui/dialog.tsx";
import {Button} from "../components/ui/button.tsx";
import "../assets/package.less";
import {useTranslation} from "react-i18next";
import {useDispatch, useSelector} from "react-redux";
import {dialogSelector, refreshPackage} from "../store/package.ts";
import {useEffect} from "react";
function Package() {
const { t } = useTranslation();
const dispatch = useDispatch();
const open = useSelector(dialogSelector);
useEffect(() => {
refreshPackage(dispatch);
}, []);
return (
<Dialog>
<DialogContent>
<DialogHeader>
<DialogTitle>Edit profile</DialogTitle>
<DialogDescription>
Make changes to your profile here. Click save when you're done.
</DialogDescription>
</DialogHeader>
<DialogFooter>
<Button>Save changes</Button>
</DialogFooter>
</DialogContent>
</Dialog>
)
}
export default Package;

View File

@ -1,5 +1,5 @@
import {useDispatch, useSelector} from "react-redux";
import {dialogSelector, refreshQuota, setDialog} from "../store/quota.ts";
import {closeDialog, dialogSelector, refreshQuota, setDialog} from "../store/quota.ts";
import {useTranslation} from "react-i18next";
import {useEffect, useState} from "react";
import {
@ -15,6 +15,16 @@ import {Input} from "../components/ui/input.tsx";
import {testNumberInputEvent} from "../utils.ts";
import {Button} from "../components/ui/button.tsx";
import {Separator} from "../components/ui/separator.tsx";
import {
AlertDialog, AlertDialogAction, AlertDialogCancel,
AlertDialogContent, AlertDialogDescription,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogTrigger
} from "../components/ui/alert-dialog.tsx";
import {AlertDialogTitle} from "@radix-ui/react-alert-dialog";
import {buyQuota} from "../conversation/addition.ts";
import {useToast} from "../components/ui/use-toast.ts";
type AmountComponentProps = {
amount: number;
@ -45,8 +55,9 @@ function AmountComponent({ amount, active, other, onClick }: AmountComponentProp
function Quota() {
const { t } = useTranslation();
const [ current, setCurrent ] = useState(0);
const [ amount, setAmount ] = useState(0);
const { toast } = useToast();
const [ current, setCurrent ] = useState(1);
const [ amount, setAmount ] = useState(10);
const open = useSelector(dialogSelector);
const dispatch = useDispatch();
useEffect(() => {
@ -107,10 +118,43 @@ function Quota() {
}
</div>
<div className={`buy-action`}>
<Button variant={`default`} className={`buy-button`}>
<Plus className={`h-4 w-4 mr-2`} />
{ t('buy.buy', { amount }) }
</Button>
<AlertDialog>
<AlertDialogTrigger asChild>
<Button variant={`default`} className={`buy-button`} disabled={amount === 0}>
<Plus className={`h-4 w-4 mr-2`} />
{ t('buy.buy', { amount }) }
</Button>
</AlertDialogTrigger>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>{ t('buy.dialog-title') }</AlertDialogTitle>
<AlertDialogDescription>{ t('buy.dialog-desc', { amount }) }</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel>{ t('buy.dialog-cancel') }</AlertDialogCancel>
<AlertDialogAction onClick={
async () => {
const res = await buyQuota(amount);
if (res.status) {
toast({
title: t('buy.success'),
description: t('buy.success-prompt', { amount }),
})
dispatch(closeDialog());
} else {
toast({
title: t('buy.failed'),
description: t('buy.failed-prompt', { amount }),
})
setTimeout(() => {
window.open('https://deeptrain.lightxi.com/home/wallet');
}, 2000);
}
}
}>{ t('buy.dialog-buy') }</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
</div>
</div>
<div className={`line`} />

View File

@ -3,6 +3,7 @@ import menuReducer from "./menu";
import authReducer from "./auth";
import chatReducer from "./chat";
import quotaReducer from "./quota";
import packageReducer from "./package";
const store = configureStore({
reducer: {
@ -10,6 +11,7 @@ const store = configureStore({
auth: authReducer,
chat: chatReducer,
quota: quotaReducer,
package: packageReducer,
},
});

47
app/src/store/package.ts Normal file
View File

@ -0,0 +1,47 @@
import {createSlice} from "@reduxjs/toolkit";
import {getPackage} from "../conversation/addition.ts";
export const packageSlice = createSlice({
name: "package",
initialState: {
dialog: false,
cert: false,
teenager: false,
},
reducers: {
toggleDialog: (state) => {
state.dialog = !state.dialog;
},
setDialog: (state, action) => {
state.dialog = action.payload as boolean;
},
openDialog: (state) => {
state.dialog = true;
},
closeDialog: (state) => {
state.dialog = false;
},
refreshState: (state, action) => {
state.cert = action.payload.cert;
state.teenager = action.payload.teenager;
},
}
});
export const {toggleDialog, setDialog, openDialog, closeDialog, refreshState} = packageSlice.actions;
export default packageSlice.reducer;
export const dialogSelector = (state: any): boolean => state.package.dialog;
export const certSelector = (state: any): boolean => state.package.cert;
export const teenagerSelector = (state: any): boolean => state.package.teenager;
export const refreshPackage = (dispatch: any) => {
setInterval(async () => {
const current = new Date().getTime(); //@ts-ignore
if (window.hasOwnProperty("package") && (current - window.package < 2500)) return; //@ts-ignore
window.package = current;
const response = await getPackage();
if (response.status) dispatch(refreshState(response));
}, 10000);
}

View File

@ -75,10 +75,12 @@ func BuyAPI(c *gin.Context) {
}
money := float32(form.Quota) * 0.1
if Pay(user.Username, float32(money)*0.1) {
if Pay(user.Username, money) {
user.IncreaseQuota(db, float32(form.Quota))
c.JSON(200, gin.H{
"status": true,
"data": user.GetQuota(db),
"error": "success",
})
} else {
c.JSON(200, gin.H{