更新索引文档,新增2026-04-23的全屏直播与点卡商城及直播房间详情联调记录,同时在会议记录中添加相关会议主题,确保文档内容的完整性与一致性。
This commit is contained in:
8
.cursor/agent/开发助理/evolution/2026-04-23-玩值三端直播点卡.md
Normal file
8
.cursor/agent/开发助理/evolution/2026-04-23-玩值三端直播点卡.md
Normal file
@@ -0,0 +1,8 @@
|
||||
# 开发助理 - 2026-04-23
|
||||
|
||||
## 跨端摘要
|
||||
|
||||
- **玩值** 直播:App 全屏页对接既有 `/api/app/live/*`;**点卡**:新增 App 只读目录 + 下单写 `pointCardOrders`,管理端仍管 SKU/福禄;**管理端** 直播详情节省列表全量拉取。
|
||||
- 文档与索引:`开发文档/玩值/迁移-API对照表.md`、`进度-看板.md`、项目索引、会议纪要已同步。
|
||||
|
||||
详见各 `agent/玩值*/evolution/2026-04-23-*.md`。
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
| 日期 | 主题 |
|
||||
|------|------|
|
||||
| 2026-04-23 | [全屏直播与点卡商城](2026-04-23-全屏直播与点卡商城.md) |
|
||||
| 2026-04-22 | [wz-app Uno 与 custom-tabbar](2026-04-22-wz-app-Uno与custom-tabbar.md) |
|
||||
| 2026-04-22 | [TS原生编译跨平台吸收(Perry)](2026-04-22-TS原生编译跨平台吸收.md) |
|
||||
| 2026-04-20 | [玩值迁移-吸收](2026-04-20-玩值迁移-吸收.md) |
|
||||
|
||||
@@ -2,5 +2,6 @@
|
||||
|
||||
| 日期 | 主题 |
|
||||
|------|------|
|
||||
| 2026-04-23 | [直播房间详情联调](2026-04-23-直播房间详情联调.md) |
|
||||
| 2026-04-22 | [TS原生编译跨平台吸收(Perry)](2026-04-22-TS原生编译跨平台吸收.md) |
|
||||
| 2026-04-20 | [玩值迁移-吸收](2026-04-20-玩值迁移-吸收.md) |
|
||||
|
||||
31
.cursor/meeting/2026-04-23_玩值三端直播点卡打通.md
Normal file
31
.cursor/meeting/2026-04-23_玩值三端直播点卡打通.md
Normal file
@@ -0,0 +1,31 @@
|
||||
# 会议纪要 - 2026-04-23 | 玩值三端:直播 + 点卡 接口与功能打通
|
||||
|
||||
## 基本信息
|
||||
|
||||
- **时间**:2026-04-23
|
||||
- **议题**:wz-api 补 C 端点卡与订单、管理端直播房间详情;wz-app 全屏直播页与商城点卡;三端与《迁移-API对照表》同步
|
||||
- **参与角色**:产品(验收口径)、玩值 API、玩值管理端、玩值移动端、测试(联调项)
|
||||
|
||||
## 会议决议
|
||||
|
||||
1. C 端点卡仅走 **`/api/app/mall/*`** 与 **`/api/app/orders/point-card`**,与 admin 的 `/api/admin/wz/points/*` 分离;订单首版 `pending`、不接支付。
|
||||
2. 观众侧直播使用 **`/live/:streamerId`(24 位 hex)**,与 `GET /api/app/live/*` 对齐;动态页用 API 主播列表映射,避免假 `roomId` 直跳。
|
||||
3. 管理端直播详情优先 **`GET /api/admin/wz/live/rooms/:roomId`**,无则列表兜底。
|
||||
|
||||
## 问题与作答区
|
||||
|
||||
| 问题 | 责任角色 | 作答 |
|
||||
|------|----------|------|
|
||||
| 点卡支付何时接入? | 产品 / API | (待排期,当前仅建单) |
|
||||
| 库存是否下单后扣减? | API | 首版不扣,避免与支付未闭环冲突 |
|
||||
|
||||
## 实现摘要(会后落地)
|
||||
|
||||
- wz-api:`playhub_wz_app_mall.go`;`RegisterPlayhubWzAdminRoutes` 增加 `GET .../live/rooms/:roomId`
|
||||
- wz-app:`LivePage`、`MallPointsPage`、`api/live.ts`、`api/mall.ts`、路由结构调整
|
||||
- wz-admin:`fetchLiveRoomById`、`LiveDetailPage` 优先直查
|
||||
- 文档:`开发文档/玩值/迁移-API对照表.md`、`进度-看板.md` 更新
|
||||
|
||||
---
|
||||
|
||||
各角色经验见 `agent/玩值*/evolution/2026-04-23-*.md`。
|
||||
@@ -58,6 +58,7 @@ YYYY-MM-DD_会议主题.md
|
||||
|
||||
| 日期 | 主题 | 参与角色 | 文件 |
|
||||
|------|------|---------|------|
|
||||
| 2026-04-23 | 玩值三端:直播 + 点卡 接口与功能打通 | 玩值 API、管理端、移动端、产品 | [2026-04-23_玩值三端直播点卡打通.md](2026-04-23_玩值三端直播点卡打通.md) |
|
||||
| 2026-02-27 | 开发进度同步会议 | 产品、后端、管理端、小程序 | [2026-02-27_开发进度同步会议.md](2026-02-27_开发进度同步会议.md) |
|
||||
| 2026-02-28 | 临时需求池 stitch_soul 需求评审(含页面重构专项·10 张图全覆盖) | 产品、后端、管理端、小程序、测试 | [2026-02-28_临时需求池stitch_soul需求评审.md](2026-02-28_临时需求池stitch_soul需求评审.md) |
|
||||
| 2026-02-28 | 个人资料页实现评估(profile-show / profile-edit) | 产品、后端、管理端、小程序、测试 | [2026-02-28_个人资料页实现评估.md](2026-02-28_个人资料页实现评估.md) |
|
||||
|
||||
@@ -6,6 +6,10 @@
|
||||
|
||||
---
|
||||
|
||||
## 2026-04-23
|
||||
|
||||
- 玩值三端直播+点卡打通:纪要 `meeting/2026-04-23_玩值三端直播点卡打通.md`;经验入库玩值 API/管理端/移动端;项目索引玩值.md、迁移对照表、进度看板、会议 README 索引
|
||||
|
||||
## 2026-04-21
|
||||
|
||||
- wz-app 迁移计划续:`src/` 目录、Vite 脚本、路由映射与 tabBar+占位页、鉴权与首页/我的对接;看板、真源 README、2026-04-21 日志、项目索引/玩值.md
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
# 生产/预览:填写 wz 网关根地址,无尾斜杠
|
||||
# VITE_API_BASE_URL=https://你的域名
|
||||
#
|
||||
# 开发:可留空,使用 Vite 代理到 http://localhost:48080
|
||||
# 开发:可留空,使用 Vite 代理到 http://localhost:48080(见 vite.config.ts → /api)
|
||||
# 鉴权:登录成功后 token 存 localStorage(wz_app_token),请求头 Authorization: Bearer
|
||||
VITE_API_BASE_URL=
|
||||
# 可选别名,与 VITE_API_BASE_URL 二选一
|
||||
# VITE_WZ_API_BASE=
|
||||
|
||||
4
new/wz-app/dist/index.html
vendored
4
new/wz-app/dist/index.html
vendored
@@ -11,8 +11,8 @@
|
||||
rel="stylesheet"
|
||||
/>
|
||||
<title>玩值电竞 - WanZhi Esports</title>
|
||||
<script type="module" crossorigin src="/assets/index-Xl-bD0z5.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/assets/index-BAB65AsD.css">
|
||||
<script type="module" crossorigin src="/assets/index-B7BS2MS9.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/assets/index-CxS0zmvh.css">
|
||||
</head>
|
||||
<body class="min-h-screen antialiased">
|
||||
<div id="root"></div>
|
||||
|
||||
@@ -12,6 +12,7 @@ import { MallPointsPage } from "@/pages/MallPointsPage"
|
||||
import { ProfilePage } from "@/pages/ProfilePage"
|
||||
import { NotFoundPage } from "@/pages/NotFoundPage"
|
||||
import { LivePage } from "@/pages/LivePage"
|
||||
import { AuthSessionListener } from "@/components/auth/AuthSessionListener"
|
||||
|
||||
/** 带底栏 Tab 的主区(与全屏直播页互斥,避免底栏与输入条叠层) */
|
||||
function TabLayout() {
|
||||
@@ -26,6 +27,7 @@ export default function App() {
|
||||
return (
|
||||
<BrowserRouter>
|
||||
<AppProvider>
|
||||
<AuthSessionListener />
|
||||
<Routes>
|
||||
<Route path="/login" element={<LoginPage />} />
|
||||
<Route
|
||||
|
||||
26
new/wz-app/src/components/auth/AuthSessionListener.tsx
Normal file
26
new/wz-app/src/components/auth/AuthSessionListener.tsx
Normal file
@@ -0,0 +1,26 @@
|
||||
import { useEffect } from "react"
|
||||
import { useNavigate } from "react-router-dom"
|
||||
import { useAppContext } from "@/components/providers/app-provider"
|
||||
|
||||
/**
|
||||
* 与 `request.ts` 中 401 时派发的 `wz:auth-lost` 联动:清空上下文并回登录页(避免仅清 token 但 UI 仍显示已登录)。
|
||||
*/
|
||||
export function AuthSessionListener() {
|
||||
const navigate = useNavigate()
|
||||
const { logout } = useAppContext()
|
||||
|
||||
useEffect(() => {
|
||||
const onLost = () => {
|
||||
void (async () => {
|
||||
await logout()
|
||||
if (window.location.pathname !== "/login") {
|
||||
navigate("/login", { replace: true })
|
||||
}
|
||||
})()
|
||||
}
|
||||
window.addEventListener("wz:auth-lost", onLost)
|
||||
return () => window.removeEventListener("wz:auth-lost", onLost)
|
||||
}, [logout, navigate])
|
||||
|
||||
return null
|
||||
}
|
||||
@@ -60,6 +60,11 @@ export async function request<T = unknown>(path: string, options: RequestOptions
|
||||
|
||||
if (res.status === 401) {
|
||||
clearToken()
|
||||
try {
|
||||
window.dispatchEvent(new CustomEvent("wz:auth-lost"))
|
||||
} catch {
|
||||
/* ignore */
|
||||
}
|
||||
throw new Error(typeof json.message === "string" ? json.message : "未登录")
|
||||
}
|
||||
if (!res.ok) {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useState } from "react"
|
||||
import { useState, useEffect } from "react"
|
||||
import { useNavigate } from "react-router-dom"
|
||||
import { Button } from "@/components/ui/button"
|
||||
import { Input } from "@/components/ui/input"
|
||||
@@ -10,7 +10,7 @@ import { setToken } from "@/lib/token"
|
||||
|
||||
export function LoginPage() {
|
||||
const navigate = useNavigate()
|
||||
const { login, syncFromDatabase } = useAppContext()
|
||||
const { login, syncFromDatabase, isLoggedIn, authChecked } = useAppContext()
|
||||
const [phone, setPhone] = useState("")
|
||||
const [name, setName] = useState("")
|
||||
const [password, setPassword] = useState("")
|
||||
@@ -20,6 +20,12 @@ export function LoginPage() {
|
||||
const [agreed, setAgreed] = useState(false)
|
||||
const [error, setError] = useState("")
|
||||
|
||||
useEffect(() => {
|
||||
if (authChecked && isLoggedIn) {
|
||||
navigate("/", { replace: true })
|
||||
}
|
||||
}, [authChecked, isLoggedIn, navigate])
|
||||
|
||||
const handleSubmit = async () => {
|
||||
if (!agreed) {
|
||||
setError("请先阅读并同意用户协议与隐私政策")
|
||||
@@ -188,12 +194,15 @@ export function LoginPage() {
|
||||
<span className="text-foreground/30 text-xs">其他登录方式</span>
|
||||
<div className="bg-foreground/10 h-px flex-1" />
|
||||
</div>
|
||||
<div className="flex justify-center gap-8">
|
||||
<p className="text-foreground/40 mb-2 text-center text-xs">以下方式需微信开放平台等配置,暂不可用</p>
|
||||
<div className="flex justify-center gap-8 opacity-50">
|
||||
{["微信", "QQ", "微博"].map((n) => (
|
||||
<button
|
||||
key={n}
|
||||
type="button"
|
||||
className="text-foreground/50 flex h-12 w-12 items-center justify-center rounded-full bg-foreground/5 text-xs"
|
||||
disabled
|
||||
title="暂未接入"
|
||||
className="text-foreground/50 flex h-12 w-12 cursor-not-allowed items-center justify-center rounded-full bg-foreground/5 text-xs"
|
||||
>
|
||||
{n}
|
||||
</button>
|
||||
|
||||
@@ -22,6 +22,11 @@ import { Button } from "@/components/ui/button"
|
||||
import { useAppContext } from "@/components/providers/app-provider"
|
||||
import { useState } from "react"
|
||||
|
||||
function maskPhone(p?: string): string {
|
||||
if (!p || p.length < 7) return p || "—"
|
||||
return `${p.slice(0, 3)}****${p.slice(-4)}`
|
||||
}
|
||||
|
||||
export function ProfilePage() {
|
||||
const { user, wallet, logout } = useAppContext()
|
||||
const navigate = useNavigate()
|
||||
@@ -100,7 +105,7 @@ export function ProfilePage() {
|
||||
</Link>
|
||||
</div>
|
||||
<p className="text-xs text-foreground/60 bg-foreground/10 px-2 py-0.5 rounded-full inline-block mb-2 truncate max-w-full">
|
||||
ID: {user.odlId || "888888"}
|
||||
ID: {user.id || user.odlId || "—"} · {user.phone ? maskPhone(user.phone) : "手机未绑定"}
|
||||
</p>
|
||||
<p className="text-xs text-foreground/40 truncate">电竞达人 | 王者荣耀主播</p>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user