feat: init

master
xjs 2025-11-14 14:56:53 +08:00
parent 48fe17a4cb
commit de83d62659
12 changed files with 170 additions and 61 deletions

View File

@ -1,4 +1,4 @@
# 六维小助手
# 六维小助手 初中版本
六维小助手

View File

@ -6,7 +6,7 @@ export const fetchQuestions = async ({
options?: { signal?: AbortSignal,};
}) => {
const response = await getRequest(
"https://api.v3.ycymedu.com/api/zhiYuan/aigcquestionswords?",
"https://senior.ycymedu.com/api/zhiYuan/aigcquestionswords?",
{},
options
);

View File

@ -6,7 +6,7 @@ export const fetchUserToken = async ({
options?: { signal?: AbortSignal; headers?: Record<string, string> };
}) => {
const response = await getRequest(
"https://api.v3.ycymedu.com/api/sysOnlineUser/hasitexpired",
"https://senior.ycymedu.com/api/sysOnlineUser/hasitexpired",
{},
options
);
@ -26,7 +26,7 @@ export const fetchReport = async ({
options?: { signal?: AbortSignal; headers?: Record<string, string> };
}) => {
const response = await getRequest(
"https://api.v3.ycymedu.com/api/busScale/GetBusAIReportKeyWord",
"https://senior.ycymedu.com/api/busScale/GetBusAIReportKeyWord",
params,
options
);
@ -46,7 +46,7 @@ export const fetchFile = async ({
options?: { signal?: AbortSignal; headers?: Record<string, string> };
}) => {
const response = await getRequest(
"https://api.v3.ycymedu.com/api/volunTb/downloadpdfUrl",
"https://senior.ycymedu.com/api/volunTb/downloadpdfUrl",
params,
options
);
@ -63,10 +63,10 @@ export const fetchWishList = async ({
params,
options,
}: {
params: { locationCode: string; };
params: {};
options?: { signal?: AbortSignal; headers?: Record<string, string> };
}) => {
const response = await getRequest("https://api.v3.ycymedu.com/api/volunTb/v2/list", params, options);
const response = await getRequest("https://senior.ycymedu.com/api/busStudentMiddleFill/list", params, options);
if (response.code === 200) {
return { result: response.result };
@ -74,3 +74,40 @@ export const fetchWishList = async ({
return { result: [], message: response.message };
}
};
export const getCozeToken = async({
options,
}: {
options?: { signal?: AbortSignal; headers?: Record<string, string> };
}) => {
const response = await getRequest("https://senior.ycymedu.com/api/sysDictData/secrtToken", {Id:740405293269061}, options);
if (response.code === 200) {
return { result: response.result };
} else {
return { result: [], message: response.message };
}
}
export const fetchSpecialFile = async ({
params,
options,
}: {
params: { Type: string,UserId:string };
options?: { signal?: AbortSignal; headers?: Record<string, string> };
}) => {
const response = await getRequest(
"https://senior.ycymedu.com/api/volunTb/yITICreate",
params,
options
);
if (response.code === 200) {
return { result: response.result };
} else {
return { result: "", message: response.message };
}
};

View File

@ -1,6 +1,6 @@
import AntechamberHeader from "@/components/AntechamberHeader";
import InvokeButton from "@/components/AntechamberButton";
import AntechamberScore from "@/components/AntechamberScore";
// import AntechamberScore from "@/components/AntechamberScore";
import { useContext, useEffect, useState } from "react";
import { RealtimeClientContext } from "@/components/Provider/RealtimeClientProvider";
@ -10,10 +10,11 @@ import { useToast } from "@/hooks/use-toast";
import { useAbortController } from "@/hooks/useAbortController";
import AntechamberFile from "@/components/AntechamberFile";
import AntechamberReport from "@/components/AntechamberReport";
import AntechamberWishList from "@/components/AntechamberWishList";
// import AntechamberWishList from "@/components/AntechamberWishList";
import { ReportContext } from "@/components/Provider/ReportResolveProvider";
// import ReceiveTime from "/icons/receive-time.png";
import { ReceiveDialog } from "@/components/ReceiveDialog";
import AntechamberTalentFile from "@/components/AntechamberTalentFile";
export default function Antechamber() {
const { handleConnect } = useContext(RealtimeClientContext);
@ -76,9 +77,10 @@ export default function Antechamber() {
return (
<div className="flex flex-col items-center h-full overflow-y-auto relative">
<AntechamberHeader />
<AntechamberScore />
<AntechamberWishList handleLoading={setIsLoading} />
{/* <AntechamberScore />
<AntechamberWishList handleLoading={setIsLoading} /> */}
<AntechamberFile handleLoading={setIsLoading} />
<AntechamberTalentFile handleLoading={setIsLoading}/>
<AntechamberReport handleLoading={setIsLoading} />
<InvokeButton disable={disable} onClick={() => toRoom({})} />
{/* <img

View File

@ -20,7 +20,7 @@ export default function MyInput() {
const handleQuestion = async () => {
handleConnect({initMessage:`我的考地点在${provinceName},我选择的科目是${subjectGroup},我的高考分数为${expectedScore}分。帮我出一个科学的参考志愿表`});
handleConnect({initMessage:`我的考地点在${provinceName},我选择的科目是${subjectGroup},我的高考分数为${expectedScore}分。帮我出一个科学的参考志愿表`});
};
return (

View File

@ -0,0 +1,57 @@
import { fetchSpecialFile } from "@/apis/user";
import { useToast } from "@/hooks/use-toast";
import { useAbortController } from "@/hooks/useAbortController";
import { useContext, useEffect } from "react";
import { useSearchParams } from "react-router-dom";
import { ReportContext } from "../Provider/ReportResolveProvider";
import { FileInfo, RealtimeClientContext } from "../Provider/RealtimeClientProvider";
type Props = {
handleLoading:(val:boolean) => void
}
export default function AntechamberFile({handleLoading}:Props) {
const [searchParams] = useSearchParams();
const talentTypeId = searchParams.get("talentTypeId") || "";
const userId = searchParams.get("userId") || "";
const token = searchParams.get("token") || "";
const { toast } = useToast();
const { getSignal } = useAbortController();
const { setHasHandledReport,hasHandledReport } = useContext(ReportContext);
const { handleConnect } = useContext(RealtimeClientContext);
const useFileFetch = async () => {
handleLoading(true)
const result = await fetchSpecialFile({
params: { Type:talentTypeId, UserId:userId},
options: {
signal: getSignal(),
headers: { Authorization: `Bearer ${token}` },
},
});
if (result.message) {
toast({
title: result.message,
});
}
let resp = result.result as FileInfo;
handleConnect({
fileInfo: {url: resp.url,tableName:"我的特长报告"},
}).then(() => {
setHasHandledReport(true);
});
};
useEffect(() => {
if (talentTypeId && userId && !hasHandledReport) {
useFileFetch();
}
}, [talentTypeId, userId,hasHandledReport]);
return <></>;
}

View File

@ -91,23 +91,17 @@ export default function AntechamberWishList({handleLoading}:Props) {
</DrawerTitle>
<div className="grid gap-[12px] px-[15px] pb-[15px] bg-[#F4F6FA] pt-[16px] max-h-[50vh] overflow-y-auto">
{wishList.map((item: any) => (
<div className="w-full bg-white" key={item.vId}>
<div className="w-full bg-white" key={item.id}>
<div className="py-[10px] pl-[16px] pr-[13px] rounded-[8px] flex items-center">
<div className="flex flex-col">
<div className="flex items-center mb-[8px]">
<span className="text-[15px] font-[600] text-[#303030] mr-[5px]">
{item.tableName}
{item.title}
</span>
<div className="text-[10px] px-[4px] py-[2px] rounded-[4px] text-[#636363] bg-[#fff]">
{item.type}
</div>
</div>
<div className="text-[#303030] text-[11px] flex items-center">
<span>
{item.locationName}·{item.score}
</span>
<span className="ml-[8px]">
{item.subjectClaim.split(",").join("/")}
{wishList[0].batchName}·{wishList[0].totalScore}
</span>
</div>
</div>
@ -148,18 +142,13 @@ export default function AntechamberWishList({handleLoading}:Props) {
<div className="flex flex-col">
<div className="flex items-center mb-[5px]">
<span className="text-[15px] font-[600] text-[#303030] mr-[5px]">
{wishList[0].tableName}
{wishList[0].title}
</span>
<div className="text-[10px] px-[4px] py-[2px] rounded-[4px] text-[#636363] bg-[#fff]">
{wishList[0].type}
</div>
</div>
<div className="text-[#303030] text-[11px] flex items-center">
<span>
{wishList[0].locationName}·{wishList[0].score}
</span>
<span className="ml-[8px]">
{wishList[0].subjectClaim.split(",").join("/")}
{wishList[0].batchName}·{wishList[0].totalScore}
</span>
</div>
</div>

View File

@ -15,6 +15,9 @@ import {
} from "react";
import { useToast } from "@/hooks/use-toast";
import { MessageHandlerStrategy } from "@/hooks/useRealtimeClient";
import { getCozeToken } from "@/apis/user";
import { useAbortController } from "@/hooks/useAbortController";
import { useSearchParams } from "react-router-dom";
type RoomInfo = {
appId: string;
@ -24,11 +27,11 @@ type RoomInfo = {
};
export type FileInfo = {
type: string;
type?: string;
url: string;
tableName: string;
provinceName: string;
subjectClaim: string;
tableName?: string;
provinceName?: string;
subjectClaim?: string;
};
export const RealtimeClientContext = createContext<{
@ -69,7 +72,7 @@ export const RealtimeClientProvider = ({
}: {
children: ReactNode;
}) => {
const token = import.meta.env.VITE_COZE_TOKEN;
let token = "";
const botId = import.meta.env.VITE_COZE_BOT_ID;
const voiceId = import.meta.env.VITE_COZE_VOICE_ID;
const connectorId = "1024";
@ -97,6 +100,10 @@ export const RealtimeClientProvider = ({
const messageHandlerStrategy = useRef(new MessageHandlerStrategy());
const { getSignal } = useAbortController();
const [searchParams]= useSearchParams();
const userToken = searchParams.get("token") || '';
/** 初始化客户端并设置监听 */
const initClient = async ({
initMessage,
@ -105,7 +112,10 @@ export const RealtimeClientProvider = ({
initMessage?: string;
fileInfo?: FileInfo;
}) => {
if(!token){
const resp = await getCozeToken({options:{signal:getSignal(),headers: { Authorization: `Bearer ${userToken}` }}})
token = resp.result as string
}
const client = new RealtimeClient({
accessToken: token,
@ -115,7 +125,7 @@ export const RealtimeClientProvider = ({
allowPersonalAccessTokenInBrowser: true,
suppressStationaryNoise: true,
suppressNonStationaryNoise: true,
debug: false,
debug: true,
});
clientRef.current = client;
@ -228,7 +238,7 @@ export const RealtimeClientProvider = ({
content: JSON.stringify([
{
type: "text",
text: "帮我解读这个文件,结合当下的专业行情以及对该专业未来的发展趋势,简介的给出志愿建议",
text: "帮我解读这个文件,根据济南市初升高相关政策要求和情况,给出合理的升学规划建议",
},
{ type: "image", file_url: fileInfo.url },
]),

View File

@ -3,7 +3,7 @@ import { RealtimeClientContext } from "../Provider/RealtimeClientProvider";
import ReactMarkdown from "react-markdown";
import gfm from "remark-gfm";
import { RoleType } from "@coze/api";
import { Loader } from 'lucide-react';
import { Loader } from "lucide-react";
export default function RoomConversation() {
const { messageList } = useContext(RealtimeClientContext);
@ -54,7 +54,8 @@ export default function RoomConversation() {
: "bg-blue-500 text-white rounded-tr-none"
}`}
>
{typeof message.fileParseStatus === "undefined" && typeof message.fileInfo === 'undefined' ? (
{typeof message.fileParseStatus === "undefined" &&
typeof message.fileInfo === "undefined" ? (
<ReactMarkdown remarkPlugins={[gfm]}>
{message.content}
</ReactMarkdown>
@ -70,22 +71,36 @@ export default function RoomConversation() {
<span className="text-[15px] text-[#303030] mr-[8px] leading-[1]">
{message?.fileInfo?.tableName}
</span>
<div className="bg-[#F4F6FA] rounded-[4px] w-[48px] h-[16px] px-[4px] py-[2px] text-[10px]">
{message?.fileInfo?.type}
</div>
{message?.fileInfo?.type ? (
<div className="bg-[#F4F6FA] rounded-[4px] w-[48px] h-[16px] px-[4px] py-[2px] text-[10px]">
{message.fileInfo.type}
</div>
) : (
<></>
)}
</div>
<div className="text-[12px] text-[#303030] mt-[6px] flex items-center">
<span className="mr-[10px]">
{message?.fileInfo?.provinceName}·{message?.fileInfo?.score}
</span>
<span>
{message?.fileInfo?.subjectClaim?.split(",").join("/")}
</span>
{
message.fileParseStatus < 2 && (
<Loader className="w-[12px] h-[12px] animate-spin ml-[6px]" />
)
}
{message.fileInfo.provinceName ? (
<span className="mr-[10px]">
{message?.fileInfo?.provinceName}·
{message?.fileInfo?.score}
</span>
) : (
<></>
)}
{message?.fileInfo?.subjectClaim ? (
<span>
{message?.fileInfo?.subjectClaim
?.split(",")
.join("/")}
</span>
) : (
<></>
)}
{message.fileParseStatus < 2 && (
<Loader className="w-[12px] h-[12px] animate-spin ml-[6px]" />
)}
</div>
</div>
</div>
@ -95,7 +110,9 @@ export default function RoomConversation() {
))}
<div ref={messagesEndRef} />
</div>
<div className="text-[12px] text-[#999] font-[400] text-center mt-auto mb-[7px]">AI</div>
<div className="text-[12px] text-[#999] font-[400] text-center mt-auto mb-[7px]">
AI
</div>
{/* <div className="mx-[8px] rounded-full bg-[#ffeede] py-[7px] pl-[18px] pr-[3px] flex items-center justify-between">
<span className="text-[#FA8E23] text-[12px]">3~</span>
<button className="rounded-full bg-[#FA8E23] py-[6px] px-[13px] text-[13px] text-white font-[500]"></button>

View File

@ -21,7 +21,7 @@ export const useSignalRConnection = (params: {
.withServerTimeout(30000)
.withAutomaticReconnect()
.withUrl(
`https://api.v3.ycymedu.com/hubs/weminpro?access_token=${params.access_token}&roomId=${params.roomId}`
`https://senior.ycymedu.com/hubs/weminpro?access_token=${params.access_token}&roomId=${params.roomId}`
)
.configureLogging(signalR.LogLevel.Information)
.build();

View File

@ -8,20 +8,17 @@ export const useWishList = () => {
const { getSignal } = useAbortController();
const [searchParams]= useSearchParams();
const locationCode = searchParams.get("locationCode") || '';
const token = searchParams.get("token") || '';
const getWishList = async () => {
const res = await fetchWishList({params:{locationCode:locationCode},options:{signal:getSignal(),headers: { Authorization: `Bearer ${token}` },}});
const res = await fetchWishList({params:{},options:{signal:getSignal(),headers: { Authorization: `Bearer ${token}` },}});
const _wishList = res.result as any[];
setWishList(_wishList);
};
useEffect(() => {
if(locationCode){
getWishList();
}
}, [locationCode]);
}, []);
return {
wishList,

View File

@ -3,7 +3,7 @@
# 服务器信息
SERVER_USER="root"
SERVER_HOST="106.14.30.150"
SERVER_PATH="/opt/1panel/apps/openresty/openresty/www/sites/chat.ycymedu.com/index"
SERVER_PATH="/opt/1panel/apps/openresty/openresty/www/sites/m.senior.ycymedu.com/index"
PRIVATE_KEY="ALIYUN.pem"
BACKUP_PATH="${SERVER_PATH}-backup-$(date +%Y%m%d%H%M%S).zip"
DINGDING_WEBHOOK="https://oapi.dingtalk.com/robot/send?access_token=fca104958fea6273c9c7ef3f08b3d552645c214f929066785e8caf6e1885a5a6"