重构项目结构,将数据层集中在 lib 包中;resource 类型更新,支持多个子套餐类型分别表示;新增长效套餐的购买流程,以及已购查询功能

This commit is contained in:
2025-05-22 14:59:22 +08:00
parent 9652181fe4
commit dc83c83cfb
29 changed files with 1827 additions and 1143 deletions

View File

@@ -21,7 +21,8 @@ type PageRecord<T = unknown> = {
list: T[]
}
type ExtractData<T extends (...args: never) => unknown> = Awaited<ReturnType<T>> extends ApiResponse<infer D> ? D : never
type ExtraReq<T extends (...args: never) => unknown> = T extends (...args: infer P) => unknown ? P[0] : never
type ExtraResp<T extends (...args: never) => unknown> = Awaited<ReturnType<T>> extends ApiResponse<infer D> ? D : never
// 预定义错误
const UnauthorizedError = new Error('未授权访问')
@@ -32,6 +33,7 @@ export {
CLIENT_SECRET,
type ApiResponse,
type PageRecord,
type ExtractData,
type ExtraReq,
type ExtraResp,
UnauthorizedError,
}

2
src/lib/models/index.ts Normal file
View File

@@ -0,0 +1,2 @@
export * from './models'
export * from './resource'

View File

@@ -1,3 +1,5 @@
import {Resource} from '@/lib/models'
export type User = {
id: number
admin_id: number
@@ -20,45 +22,6 @@ export type User = {
updated_at: Date
}
export type Resource = {
id: number
user_id: number
resource_no: string
active: boolean
type: number
created_at: Date
updated_at: Date
short: ResourceShort
}
export function name(obj: Resource) {
switch (obj.type) {
case 1:
switch (obj.short.type) {
case 1:
return `包时 ${obj.short.live / 60}分钟`
case 2:
return `包量 ${obj.short.live / 60}分钟`
}
break
}
}
export type ResourceShort = {
id: number
resource_id: number
type: number
live: number
expire: Date
quota: number
used: number
daily_limit: number
daily_used: number
daily_last: Date
created_at: Date
updated_at: Date
}
export type Bill = {
id: number
user_id: number

View File

@@ -0,0 +1,47 @@
type ResourceShort = {
id: number
resource_id: number
type: number
live: number
expire: Date
quota: number
used: number
daily_limit: number
daily_used: number
daily_last: Date
created_at: Date
updated_at: Date
}
type ResourceLong = {
id: number
resource_id: number
type: number
live: number
expire: Date
quota: number
used: number
daily_limit: number
daily_used: number
daily_last: Date
created_at: Date
updated_at: Date
}
export type Resource<T extends 1 | 2 = 1 | 2> = {
id: number
user_id: number
resource_no: string
active: boolean
created_at: Date
updated_at: Date
} & (
T extends 1 ? {
type: 1
short: ResourceShort
} :
T extends 2 ? {
type: 2
long: ResourceLong
} : {}
)

24
src/lib/stores/layout.ts Normal file
View File

@@ -0,0 +1,24 @@
import {createStore} from 'zustand/vanilla'
export type LayoutStore = LayoutState & LayoutActions
export type LayoutState = {
navbar: boolean
}
export type LayoutActions = {
toggleNavbar: () => void
setNavbar: (navbar: boolean) => void
}
export const createLayoutStore = () => {
return createStore<LayoutStore>()(setState => ({
navbar: true,
toggleNavbar: () => setState(state => {
return {navbar: !state.navbar}
}),
setNavbar: (navbar) => setState(_ => {
return {navbar}
}),
}))
}

26
src/lib/stores/profile.ts Normal file
View File

@@ -0,0 +1,26 @@
import {User} from '@/lib/models'
import {createStore} from 'zustand/vanilla'
import {getProfile} from '@/actions/auth'
export type ProfileStore = ProfileState & ProfileActions
export type ProfileState = {
profile: User | null
}
export type ProfileActions = {
refreshProfile: () => Promise<void>
}
export const createProfileStore = (init: User|null) => {
return createStore<ProfileStore>()(setState => ({
profile: init,
refreshProfile: async () => {
const profile = await getProfile()
if (!profile.success) return
setState({profile: profile.data})
},
}))
}