feat(cache): remove redis caching and add force refresh mechanism
- Remove Redis-based caching logic from uidScoreStore and xcxApi. - Add force refresh support to uidScoreRequest in GroupingPrediction component. - Update server API /api/user/nowScores to accept force parameter. - Always show refresh button in GroupingPrediction regardless of data state. - Change default sort type in FavPlayersPage to SCORE_DOWN. - Clean up unused imports from server.ts. This change ensures user scores are always up-to-date by bypassing cache when needed, preventing issues with stale data during manual sync operations.
This commit is contained in:
parent
8be15d51b1
commit
191906192b
@ -30,9 +30,9 @@ enum OrderScore {
|
|||||||
|
|
||||||
export const GroupingPrediction: React.FC<Props> = props => {
|
export const GroupingPrediction: React.FC<Props> = props => {
|
||||||
const { uidScore } = useLoaderData<{ uidScore: Map<string, string>}>();
|
const { uidScore } = useLoaderData<{ uidScore: Map<string, string>}>();
|
||||||
const uidScoreRequest = useRequest(async () => {
|
const uidScoreRequest = useRequest(async (force?: boolean) => {
|
||||||
const uids = props.players?.map(player => player.uid).filter(Boolean);
|
const uids = props.players?.map(player => player.uid).filter(Boolean);
|
||||||
const data = await fetch(`/api/user/nowScores`, {
|
const data = await fetch(`/api/user/nowScores?force=${force ? 'true' : 'false'}`, {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
body: JSON.stringify({ uids }),
|
body: JSON.stringify({ uids }),
|
||||||
}).then(res => res.json()).catch(() => ({}));
|
}).then(res => res.json()).catch(() => ({}));
|
||||||
@ -86,7 +86,7 @@ export const GroupingPrediction: React.FC<Props> = props => {
|
|||||||
});
|
});
|
||||||
}, [players, grouped, groupLen, maxPlayerSize]);
|
}, [players, grouped, groupLen, maxPlayerSize]);
|
||||||
const handleSyncUidScore = useCallback(() => {
|
const handleSyncUidScore = useCallback(() => {
|
||||||
uidScoreRequest.runAsync();
|
uidScoreRequest.runAsync(true);
|
||||||
}, []);
|
}, []);
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@ -115,7 +115,7 @@ export const GroupingPrediction: React.FC<Props> = props => {
|
|||||||
OrderScore.年度积分,
|
OrderScore.年度积分,
|
||||||
]} />
|
]} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item hidden={uidScore.size > 0 || (uidScoreRequest.data?.size || 0) > 0}>
|
<Form.Item>
|
||||||
<Button loading={uidScoreRequest.loading} onClick={handleSyncUidScore} icon={<SyncOutlined />}>
|
<Button loading={uidScoreRequest.loading} onClick={handleSyncUidScore} icon={<SyncOutlined />}>
|
||||||
刷新当前积分
|
刷新当前积分
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@ -242,7 +242,8 @@ const server = Bun.serve({
|
|||||||
"/api/user/nowScores": {
|
"/api/user/nowScores": {
|
||||||
async POST(req) {
|
async POST(req) {
|
||||||
const { uids } = await req.json();
|
const { uids } = await req.json();
|
||||||
const uidScore = await getUidScore(uids);
|
const force = Boolean(new URL(req.url).searchParams.get('force') === 'true');
|
||||||
|
const uidScore = await getUidScore(uids, force);
|
||||||
return Response.json(uidScore);
|
return Response.json(uidScore);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -250,7 +251,7 @@ const server = Bun.serve({
|
|||||||
async GET(req) {
|
async GET(req) {
|
||||||
const uid = req.params.uid;
|
const uid = req.params.uid;
|
||||||
if (uid === '0') return Response.json(null);
|
if (uid === '0') return Response.json(null);
|
||||||
const profile = await xcxApi.getAdvProfile(uid);
|
const profile = await xcxApi.getAdvProfile(uid, true);
|
||||||
return Response.json(profile);
|
return Response.json(profile);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@ -30,7 +30,7 @@ const StyledContainer = styled.div`
|
|||||||
`;
|
`;
|
||||||
export function FavePlayersPage() {
|
export function FavePlayersPage() {
|
||||||
const { favMap, unFav } = useFavPlayerStore(state => state);
|
const { favMap, unFav } = useFavPlayerStore(state => state);
|
||||||
const [sortType, setSortType] = useState<SortType>(SortType.DEFAULT);
|
const [sortType, setSortType] = useState<SortType>(SortType.SCORE_DOWN);
|
||||||
const [showType, setShowType] = useState(ShowType.LOCAL);
|
const [showType, setShowType] = useState(ShowType.LOCAL);
|
||||||
const headers = useAuthHeaders();
|
const headers = useAuthHeaders();
|
||||||
const { autoSignIn } = useAutoLogin();
|
const { autoSignIn } = useAutoLogin();
|
||||||
@ -110,7 +110,7 @@ export function FavePlayersPage() {
|
|||||||
value={sortType}
|
value={sortType}
|
||||||
onChange={e => setSortType(e)}
|
onChange={e => setSortType(e)}
|
||||||
options={[
|
options={[
|
||||||
SortType.DEFAULT,
|
// SortType.DEFAULT,
|
||||||
SortType.SCORE_UP,
|
SortType.SCORE_UP,
|
||||||
SortType.SCORE_DOWN,
|
SortType.SCORE_DOWN,
|
||||||
]}
|
]}
|
||||||
|
|||||||
@ -1,18 +1,8 @@
|
|||||||
import { redis, REDIS_CACHE_HOUR, xcxApi } from '../utils/server';
|
import { xcxApi } from '../utils/server';
|
||||||
|
|
||||||
const getKey = (uid: string) => `my-kaiqiuwang:uid-score:${uid}`;
|
export const getUidScore = async (uids: string[], force?: boolean): Promise<Record<string, string>> => {
|
||||||
|
|
||||||
const TIMEOUT = 60 * 60 * REDIS_CACHE_HOUR; // 24h;
|
|
||||||
|
|
||||||
export const getUidScore = async (uids: string[]): Promise<Record<string, string>> => {
|
|
||||||
const jobs = uids.map(async uid => {
|
const jobs = uids.map(async uid => {
|
||||||
const key = getKey(uid);
|
const profile = await xcxApi.getAdvProfile(uid, force);
|
||||||
const value = await redis.get(key);
|
|
||||||
if (value) {
|
|
||||||
return [uid, value];
|
|
||||||
}
|
|
||||||
const profile = await xcxApi.getAdvProfile(uid);
|
|
||||||
await redis.setex(key, TIMEOUT, profile?.score ?? '');
|
|
||||||
return [uid, profile?.score ?? ''];
|
return [uid, profile?.score ?? ''];
|
||||||
});
|
});
|
||||||
return Object.fromEntries(await Promise.all(jobs));
|
return Object.fromEntries(await Promise.all(jobs));
|
||||||
|
|||||||
@ -33,12 +33,12 @@ export class XCXAPI {
|
|||||||
return response.data as T;
|
return response.data as T;
|
||||||
}
|
}
|
||||||
|
|
||||||
async getAdvProfile(uid: string): Promise<XCXProfile | null> {
|
async getAdvProfile(uid: string, force?: boolean): Promise<XCXProfile | null> {
|
||||||
const cacheProfile = await redis.get(`my-kaiqiuwang:profile:${uid}`);
|
const cacheProfile = await redis.get(`my-kaiqiuwang:profile:${uid}`);
|
||||||
if (!cacheProfile) {
|
if (!cacheProfile || force) {
|
||||||
const url = `/api/User/adv_profile?uid=${uid}`;
|
const url = `/api/User/adv_profile?uid=${uid}`;
|
||||||
const profile = await this.#fetch<XCXProfile>(url);
|
const profile = await this.#fetch<XCXProfile>(url);
|
||||||
await redis.setex(`my-kaiqiuwang:profile:${uid}`, 60 * 10, JSON.stringify(profile));
|
await redis.setex(`my-kaiqiuwang:profile:${uid}`, 60 * 60 * 24, JSON.stringify(profile));
|
||||||
return profile;
|
return profile;
|
||||||
}
|
}
|
||||||
return JSON.parse(cacheProfile);
|
return JSON.parse(cacheProfile);
|
||||||
|
|||||||
@ -1,7 +1,5 @@
|
|||||||
import type { EventDetail, Player } from "../types";
|
|
||||||
import * as cheerio from "cheerio";
|
|
||||||
import { XCXAPI } from "../services/xcxApi";
|
import { XCXAPI } from "../services/xcxApi";
|
||||||
import { KAIQIU_BASE_URL, LOGTO_DOMAIN } from "./common";
|
import { LOGTO_DOMAIN } from "./common";
|
||||||
import { RedisClient } from "bun";
|
import { RedisClient } from "bun";
|
||||||
import { createRemoteJWKSet, jwtVerify } from 'jose';
|
import { createRemoteJWKSet, jwtVerify } from 'jose';
|
||||||
import { LOGTO_RESOURCE } from "./constants";
|
import { LOGTO_RESOURCE } from "./constants";
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user