diff --git a/.ai/product/feature-map.md b/.ai/product/feature-map.md index 1d0027c..804f1e9 100644 --- a/.ai/product/feature-map.md +++ b/.ai/product/feature-map.md @@ -234,7 +234,8 @@ UI **不出现「塔罗」**。禁止神谕吉凶、恐吓话术。 | Phase A `[Ops]` | 登录 · 用户/订单查询 · 会员授予 · 审计 · `apps/admin-h5`(ECR-006 Closed) | | Phase B `[Ops]` | 行为分析:自有埋点 + 管理端「数据」看板(**ECR-007 Implemented** · Spec `ops-analytics.md`) | | Phase C `[Ops]` | 内容:首页宫格 CRUD · 测评上下架(**ECR-008 Implemented** · Spec `ops-content.md`) | -| Phase D+ | 商业加深 · RBAC · 封禁 · 推送(各开独立 ECR) | +| Phase D `[Ops]` | 商业加深:订单筛选 · 展示价 · 退款只读(**ECR-009** · Spec `ops-commerce.md`) | +| Phase E+ | RBAC · 封禁 · 推送(各开独立 ECR) | 不计入 P1 Complete;不进入五 Tab。 diff --git a/.ai/product/feature-spec/README.md b/.ai/product/feature-spec/README.md index 4d1fa52..af03fd4 100644 --- a/.ai/product/feature-spec/README.md +++ b/.ai/product/feature-spec/README.md @@ -22,7 +22,8 @@ | [image-card.md](image-card.md) | 意象卡片 | §2.8 | `/cards` | P2 设计 | | [ops-admin.md](ops-admin.md) | 运营后台 | §7 | `admin-h5` `/` `/users/:id` … | Ops-A | | [ops-analytics.md](ops-analytics.md) | 运营行为分析(埋点+数据看板) | §7 | `admin-h5` `/analytics` · H5 track | Ops-B · ECR-007 Implemented | -| [ops-content.md](ops-content.md) | 运营内容(宫格+测评上下架) | §7 | `admin-h5` `/content` · `GET /home/tools` | Ops-C · ECR-008 Approved | +| [ops-content.md](ops-content.md) | 运营内容(宫格+测评上下架) | §7 | `admin-h5` `/content` · `GET /home/tools` | Ops-C · ECR-008 Implemented | +| [ops-commerce.md](ops-commerce.md) | 运营商业加深(订单筛选+展示价+退款只读) | §7 | `admin-h5` `/orders` `/pricing` | Ops-D · ECR-009 | 新功能:复制 `_TEMPLATE.md` → 填满 → 在本表登记 → 再编码。 diff --git a/.ai/product/feature-spec/ops-commerce.md b/.ai/product/feature-spec/ops-commerce.md new file mode 100644 index 0000000..776fae3 --- /dev/null +++ b/.ai/product/feature-spec/ops-commerce.md @@ -0,0 +1,48 @@ +# Feature Spec: 运营商业加深(Ops-D) + +> Status: `Active` · Map: `§7 Phase D` · Phase: `Ops-D` · ECR: `ECR-009` +> 规范:[../feature-design.md](../feature-design.md) +> 上游:Ops-A/B/C(ECR-006/007/008) + +--- + +## 1. 功能定义 + +| 字段 | 内容 | +|---|---| +| Name | 运营商业加深 Ops-D | +| Purpose | 订单可筛选;套餐展示价可配;退款状态只读可见 | +| Business Goal | 运营无需改库即可查单与维护展示价 | + +| In | Out | +|---|---| +| 订单筛选:status / kind / from–to / user_id | 真支付 · 退款工单流转 | +| `refund_status` 只读字段 | 优惠券 · 发票 | +| 会员套餐展示价 CRUD(分) | RBAC(Ops-E) | + +--- + +## 2. 验收 + +**Given** 库内多状态订单 +**When** `GET /api/v1/admin/orders?status=paid&kind=membership&from=&to=` +**Then** 仅返回匹配项,且含 `refund_status` + +**Given** Admin 配置 `monthly` 展示价 +**When** `PUT /api/v1/admin/membership/plan-prices` +**Then** `GET` 返回新价;不改历史 `orders.amount_cents` + +--- + +## 3. 页面 + +| 路由 | 说明 | +|---|---| +| `/orders` | 增加筛选条 + 退款状态列 | +| `/pricing` | 套餐展示价表 | + +--- + +## 4. 非目标 + +真支付网关 · 退款执行 · Ops-E 权限矩阵 · UGC diff --git a/apps/admin-h5/src/api/client.ts b/apps/admin-h5/src/api/client.ts index d8237c2..792fe89 100644 --- a/apps/admin-h5/src/api/client.ts +++ b/apps/admin-h5/src/api/client.ts @@ -112,8 +112,25 @@ export const adminApi = { request<{ ok: boolean; ask_paid_quota_left: number }>('POST', `/users/${id}/ask-quota/grant`, { delta, }), - orders: () => - request<{ + orders: (params?: { + status?: string + kind?: string + from?: string + to?: string + user_id?: string + limit?: number + offset?: number + }) => { + const q = new URLSearchParams() + if (params?.status) q.set('status', params.status) + if (params?.kind) q.set('kind', params.kind) + if (params?.from) q.set('from', params.from) + if (params?.to) q.set('to', params.to) + if (params?.user_id) q.set('user_id', params.user_id) + if (params?.limit != null) q.set('limit', String(params.limit)) + if (params?.offset != null) q.set('offset', String(params.offset)) + const qs = q.toString() + return request<{ items: Array<{ id: string user_id: string @@ -121,9 +138,18 @@ export const adminApi = { plan?: string amount_cents: number status: string + refund_status: string created_at: string }> - }>('GET', '/orders'), + }>('GET', `/orders${qs ? `?${qs}` : ''}`) + }, + planPrices: () => + request<{ items: Array<{ plan: string; display_cents: number; updated_at: string }> }>( + 'GET', + '/membership/plan-prices', + ), + savePlanPrices: (items: Array<{ plan: string; display_cents: number }>) => + request<{ ok: boolean }>('PUT', '/membership/plan-prices', { items }), audit: () => request<{ items: Array<{ diff --git a/apps/admin-h5/src/layouts/AdminShell.vue b/apps/admin-h5/src/layouts/AdminShell.vue index e945bf6..1c3eac9 100644 --- a/apps/admin-h5/src/layouts/AdminShell.vue +++ b/apps/admin-h5/src/layouts/AdminShell.vue @@ -29,6 +29,7 @@ async function onLogout() { 内容 用户 订单 + 定价 审计
diff --git a/apps/admin-h5/src/pages/OrdersPage.vue b/apps/admin-h5/src/pages/OrdersPage.vue index 63038cb..77d6c0e 100644 --- a/apps/admin-h5/src/pages/OrdersPage.vue +++ b/apps/admin-h5/src/pages/OrdersPage.vue @@ -4,6 +4,11 @@ import { adminApi } from '@/api/client' const loading = ref(false) const error = ref('') +const status = ref('') +const kind = ref('') +const from = ref('') +const to = ref('') +const userId = ref('') const items = ref< Array<{ id: string @@ -12,6 +17,7 @@ const items = ref< plan?: string amount_cents: number status: string + refund_status: string created_at: string }> >([]) @@ -20,7 +26,13 @@ async function load() { loading.value = true error.value = '' try { - const res = await adminApi.orders() + const res = await adminApi.orders({ + status: status.value || undefined, + kind: kind.value || undefined, + from: from.value || undefined, + to: to.value || undefined, + user_id: userId.value || undefined, + }) items.value = res.items || [] } catch (e) { error.value = e instanceof Error ? e.message : '加载失败' @@ -35,13 +47,23 @@ onMounted(load)