7c7f33fbd6
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Successful in 1m58s
CI/CD Pipeline / Validate - Code Quality (push) Failing after 1m33s
CI/CD Pipeline / Validate - Type Check (mypy) (push) Successful in 44s
CI/CD Pipeline / Validate - Migration (alembic) (push) Successful in 1m1s
CI/CD Pipeline / Unit Tests (push) Successful in 6m19s
CI/CD Pipeline / Integration Tests (push) Successful in 2m17s
CI/CD Pipeline / Frontend Lint (push) Failing after 40s
CI/CD Pipeline / Frontend Unit Tests (push) Successful in 1m35s
CI/CD Pipeline / Build Staging API Image (push) Successful in 13m4s
CI/CD Pipeline / Build Staging Web Image (push) Successful in 2m15s
CI/CD Pipeline / Build Staging Worker Image (push) Successful in 5m13s
CI/CD Pipeline / ACR Image Cleanup (push) Successful in 58s
CI/CD Pipeline / Staging E2E Tests (push) Successful in 2m22s
CI/CD Pipeline / Staging API Integration Tests (push) Successful in 4m55s
CI/CD Pipeline / Canary Release to Production (push) Failing after 1228h47m52s
CI/CD Pipeline / Production Browser E2E (push) Failing after 1229h6m28s
CI/CD Pipeline / Build Production API Image (push) Failing after 1229h8m23s
CI/CD Pipeline / PR Build Worker Image (push) Failing after 1229h8m52s
CI/CD Pipeline / PR Build API Image (push) Failing after 1229h8m54s
CI/CD Pipeline / Check if frontend-only change (push) Failing after 1229h10m29s
CI/CD Pipeline / Deploy Production (push) Failing after 1229h6m35s
CI/CD Pipeline / Build Production Worker Image (push) Failing after 1229h8m19s
CI/CD Pipeline / Build Production Web Image (push) Failing after 1229h41m6s
CI/CD Pipeline / PR Build Web Image (push) Failing after 1229h41m38s
112 lines
3.1 KiB
TypeScript
112 lines
3.1 KiB
TypeScript
import { useState, useEffect, useCallback } from "react"
|
|
import { message } from "antd"
|
|
import {
|
|
getCurrentSubscription,
|
|
changePlan,
|
|
toggleAutoRenew,
|
|
cancelSubscription,
|
|
type SubscriptionInfo,
|
|
type PlanType,
|
|
type BillingCycle,
|
|
} from "@/api/subscription"
|
|
|
|
/**
|
|
* 订阅管理 Hook
|
|
* 封装订阅信息查询、套餐变更、自动续费切换、取消订阅等逻辑
|
|
*/
|
|
export function useSubscription() {
|
|
const [subscription, setSubscription] = useState<SubscriptionInfo | null>(null)
|
|
const [loading, setLoading] = useState(true)
|
|
const [submitting, setSubmitting] = useState(false)
|
|
const [selectedPlan, setSelectedPlan] = useState<PlanType>("standard")
|
|
const [billingCycle, setBillingCycle] = useState<BillingCycle>("monthly")
|
|
|
|
const loadSubscription = useCallback(async () => {
|
|
try {
|
|
const data = await getCurrentSubscription()
|
|
setSubscription(data)
|
|
setSelectedPlan(data.plan_id)
|
|
} catch (err: unknown) {
|
|
if (!(err as { __msgShown?: boolean })?.__msgShown) message.error("获取订阅信息失败")
|
|
} finally {
|
|
setLoading(false)
|
|
}
|
|
}, [])
|
|
|
|
useEffect(() => {
|
|
loadSubscription()
|
|
}, [loadSubscription])
|
|
|
|
const handleUpgrade = useCallback(async () => {
|
|
if (!subscription) return
|
|
if (selectedPlan === subscription.plan_id && billingCycle === subscription.billing_cycle) {
|
|
message.info("当前已是该套餐")
|
|
return
|
|
}
|
|
// 由调用方决定是否弹确认框
|
|
}, [subscription, selectedPlan, billingCycle])
|
|
|
|
const executeChangePlan = useCallback(async () => {
|
|
try {
|
|
setSubmitting(true)
|
|
const res = await changePlan({
|
|
target_plan_id: selectedPlan,
|
|
billing_cycle: billingCycle,
|
|
})
|
|
if (res.success) {
|
|
message.success(res.message)
|
|
setSubscription(res.new_subscription ?? null)
|
|
} else {
|
|
message.error(res.message)
|
|
}
|
|
} catch (err: unknown) {
|
|
if (!(err as { __msgShown?: boolean })?.__msgShown) message.error("套餐变更失败,请重试")
|
|
} finally {
|
|
setSubmitting(false)
|
|
}
|
|
}, [selectedPlan, billingCycle])
|
|
|
|
const handleToggleAutoRenew = useCallback(
|
|
async (enabled: boolean) => {
|
|
try {
|
|
const res = await toggleAutoRenew(enabled)
|
|
message.success(res.message)
|
|
if (subscription) {
|
|
setSubscription({ ...subscription, auto_renew: enabled })
|
|
}
|
|
} catch (err: unknown) {
|
|
if (!(err as { __msgShown?: boolean })?.__msgShown) message.error("操作失败")
|
|
}
|
|
},
|
|
[subscription],
|
|
)
|
|
|
|
const handleCancel = useCallback(async () => {
|
|
try {
|
|
const res = await cancelSubscription()
|
|
message.success(res.message)
|
|
return true
|
|
} catch (err: unknown) {
|
|
if (!(err as { __msgShown?: boolean })?.__msgShown) message.error("取消失败")
|
|
return false
|
|
}
|
|
}, [])
|
|
|
|
return {
|
|
// 状态
|
|
subscription,
|
|
loading,
|
|
submitting,
|
|
selectedPlan,
|
|
billingCycle,
|
|
setSelectedPlan,
|
|
setBillingCycle,
|
|
// 操作
|
|
loadSubscription,
|
|
handleUpgrade,
|
|
executeChangePlan,
|
|
handleToggleAutoRenew,
|
|
handleCancel,
|
|
}
|
|
}
|