diff --git a/backend/gateway/router/router.go b/backend/gateway/router/router.go index 6bba92f..6b07494 100644 --- a/backend/gateway/router/router.go +++ b/backend/gateway/router/router.go @@ -323,6 +323,7 @@ func SetupRouter(userClient *client.Client, socialClient *client.Client, assetCl materials := assets.Group("/materials") materials.POST("/upload", assetCtrl.UploadMaterial) assets.POST("/:asset_id/materials", assetCtrl.BindAssetMaterials) + assets.GET("/:asset_id/materials", assetCtrl.GetAssetMaterials) // 获取资产素材列表(修复:原 handler 存在但未注册) } // 分享相关路由(需要认证)— 用 assetCtrl(spec 规定 /api/v1/share/* 前缀) diff --git a/docs/superpowers/plans/2026-07-06-remove-hardware-gyro.md b/docs/superpowers/plans/2026-07-06-remove-hardware-gyro.md new file mode 100644 index 0000000..86c7c5b --- /dev/null +++ b/docs/superpowers/plans/2026-07-06-remove-hardware-gyro.md @@ -0,0 +1,1013 @@ +# 移除光栅卡硬件陀螺仪 · 实施计划 + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** 彻底移除光栅卡/全息卡对硬件陀螺仪/加速度计的依赖(含 Android 原生插件),改用触摸拖拽 + 手动滑块双输入,简化 ~28KB 硬件传感器代码、消除 iOS/Android 权限摩擦。 + +**Architecture:** 在 `useLenticularCraftTiltPreview` 暴露 `simulateTilt(dx, dy)`(度)作为唯一倾斜入口;触摸拖拽通过 `simulateTiltFromNormalized` 转换;新增独立组件 `` 通过卡片内置 `showSlider` prop 透出;v-model:sliderValue 双向绑定。所有硬件相关代码、依赖、权限、原生插件全部删除。 + +**Tech Stack:** uni-app 3.x + Vue 3 组合式 API + uni-app 原生 `` 组件;H5 Chrome 作为开发期验证平台。 + +## Global Constraints + +- **测试原则**:前端不写 Vitest(memory `share-impl-test-policy.md`),所有验证通过 H5 Chrome 手动 + DevTools console + `console.log` 完成 +- **平台目标**:以 app-plus(Android + iOS)为主,H5 同样支持;H5 验证为开发期主线 +- **包大小目标**:删除 ≥28KB 硬件代码 + 1 个原生插件目录 +- **提交规范**:每个 Task 一个 commit,commit 末尾追加 `Co-Authored-By: Claude ` +- **分支**:当前在 `feat/actvity`(或显示为 `feat/len`),**不要** checkout 到 main +- **数值约定**(spec §3.5): + - 触摸 emit 实际 [-1.1, +1.1],有效 [-1, +1] + - 滑块 value: 0..1,中位 0.5 = 0° + - `simulateTilt(dx, dy)`: -45..+45 度 + - 滑块→度: `dx = (v - 0.5) * 90` + - 触摸→度: `dx = x * 45` +- **错误处理**(spec §4.2):`simulateTilt` 入口 `Number.isFinite()` 校验;`layersRef.length < 2` 兜底保留 +- **Phase 5 grep 校验**(必须 0 命中):`useLenticularStudioTilt` / `useOneEuroFilter` / `imengyu-UniAndroidGyro` / `BODY_SENSORS` / `NSMotionUsageDescription` + +--- + +## File Structure + +| 文件 | 操作 | 职责 | +|------|------|------| +| `frontend/composables/useLenticularCraftTiltPreview.js` | 改 | 新增 `simulateTilt*` 入口;最终删除 `useLenticularStudioTilt` 引用 | +| `frontend/composables/useLenticularStudioTilt.js` | 删 | 28KB 硬件传感器模块 | +| `frontend/composables/useOneEuroFilter.js` | 删 | 仅被 StudioTilt 使用 | +| `frontend/composables/useHolographicPreview.js` | 改 | 删 DeviceOrientationEvent 路径;保留 `simulate(x, y)` | +| `frontend/components/lenticular/ManualTiltSlider.vue` | 新增 | 独立滑块组件 | +| `frontend/components/lenticular/LenticularCard.vue` | 改 | 加 slider 接入 + touchend 不发 (0,0) | +| `frontend/components/lenticular/HolographicCard.vue` | 改 | 同上 | +| `frontend/components/lenticular/CastloveLenticularPreview.vue` | 改 | 透传 show-slider prop | +| `frontend/pages/castlove/lenticular/lenticular-result.vue` | 改 | 加 show-slider + 改 simulate 接收器 + 删 scheduleTiltStart | +| `frontend/nativePlugins/imengyu-UniAndroidGyro/` | 删 | 原生插件目录(AAR + iOS Framework) | +| `frontend/manifest.json` | 改 | 删 nativePlugins 段 + BODY_SENSORS + NSMotionUsageDescription | + +--- + +## Task 1: 在 useLenticularCraftTiltPreview 新增手动倾斜入口 + +**Files:** +- Modify: `frontend/composables/useLenticularCraftTiltPreview.js:176-213` + +**Interfaces:** +- Consumes: 现有的 `useLenticularStudioTilt`(仍保留运行,本 Task 不动) +- Produces: 新增 3 个公共方法 `simulateTilt(dx, dy)` / `simulateTiltFromNormalized(x, y)` / `simulateTiltReset()` + +- [ ] **Step 1: 在 useLenticularCraftTiltPreview 顶部添加数值常量** + +打开 `frontend/composables/useLenticularCraftTiltPreview.js`,在第 12 行附近(`FLOW_PHYSICS` 之前)插入: + +```js +// 手动模拟倾斜:归一化 → 度数 转换系数(spec §3.5) +const NORMALIZED_TO_DEG = 45 +``` + +- [ ] **Step 2: 在 `useLenticularCraftTiltPreview` 函数体内(`return` 语句之前)新增 3 个方法** + +定位到文件末尾 `return { ... }` 之前(大约 line 210),在 `stopTiltPreview` 函数定义之后插入: + +```js + // ==================================================================== + // 手动模拟倾斜入口(spec §3.1) + // ==================================================================== + + /** + * 滑块/外部入口:直接传度数(-45..+45) + * 内部转给 simulateFromSignedDegrees 走现有离散档位逻辑 + */ + function simulateTilt(dx, dy) { + if (!Number.isFinite(dx) || !Number.isFinite(dy)) { + console.warn('[CraftTilt] simulateTilt: non-finite input', dx, dy) + return + } + simulateFromSignedDegrees(dx, dy) + } + + /** + * 触摸入口:-1..+1 归一化转度数(系数 45) + * 由 LenticularCard.vue / HolographicCard.vue 的 @simulate 事件触发 + */ + function simulateTiltFromNormalized(x, y) { + const dx = x * NORMALIZED_TO_DEG + const dy = (y != null ? y : 0) * NORMALIZED_TO_DEG + simulateTilt(dx, dy) + } + + /** + * 滑块点"复位"时调用:归零 + 清空离散档位状态 + */ + function simulateTiltReset() { + lockPreviewStill() + } +``` + +- [ ] **Step 3: 在 return 对象中暴露这 3 个方法** + +修改文件末尾的 `return { ... }`(约 line 215-225),添加这 3 个键: + +```js + return { + physics, + layerTransforms, + simulate, + relax, + gyroSourceLabel, + scheduleTiltStart, + stopTiltPreview, + lockPreviewStill, + // 新增:手动模拟入口(spec §3.1) + simulateTilt, + simulateTiltFromNormalized, + simulateTiltReset, + } +``` + +- [ ] **Step 4: H5 Chrome 手动验证(在 DevTools console)** + +启动 H5 dev server(具体命令见项目 README),打开 `lenticular-result` 页面,浏览器 DevTools console 执行: + +```js +// 从 Vue 组件实例拿 useLenticularCraftTiltPreview 返回的 simulateTilt +//(具体取法:组件实例 proxy 上找,或者在 setup 顶层临时挂到 window 调试) +window.__test_simulateTilt?.(45, 0) // 应触发右边硬切 +window.__test_simulateTilt?.(0, 0) // 应触发归中 +window.__test_simulateTilt?.(NaN, 0) // 应 console.warn 不崩 +``` + +> 注:如果没有 `window.__test_*` 暴露,可以临时在组件 setup 末尾加 `window.__test_simulateTilt = simulateTilt` 调试,验证完删除。 + +**预期**: +- 前两次调用:卡片视觉有响应(γ → ±1) +- 第三次:DevTools console 出现 `[CraftTilt] simulateTilt: non-finite input` 警告,卡片无变化 + +- [ ] **Step 5: 提交** + +```bash +git add frontend/composables/useLenticularCraftTiltPreview.js +git commit -m "feat(craft-tilt): 新增 simulateTilt 手动模拟入口 + +- simulateTilt(dx, dy) 单位度,校验 NaN/Infinity +- simulateTiltFromNormalized(x, y) 触摸归一化转度(系数 45) +- simulateTiltReset() 滑块复位用 + +Co-Authored-By: Claude " +``` + +--- + +## Task 2: 新增 ManualTiltSlider.vue 独立组件 + +**Files:** +- Create: `frontend/components/lenticular/ManualTiltSlider.vue` + +**Interfaces:** +- Consumes: 无(独立组件) +- Produces: `` + +- [ ] **Step 1: 创建组件文件,写入完整模板和 script** + +新建 `frontend/components/lenticular/ManualTiltSlider.vue`: + +```vue + + + + + +``` + +- [ ] **Step 2: 临时挂载到 H5 测试页面验证** + +找一个测试用页面(如 `pages/index/index.vue`)临时挂载: + +```vue + +``` + +H5 Chrome 打开该页面,验证: +- 拖动滑块 → console 打印 `change 0.42` 之类的数值 +- 点击 ↺ 按钮 → console 打印 `reset` +- 滑块无法拖到 0..1 范围外(uniapp `` 组件自带 clamp) + +- [ ] **Step 3: 还原测试挂载** + +如果步骤 2 加了临时代码,删除或还原。 + +- [ ] **Step 4: 提交** + +```bash +git add frontend/components/lenticular/ManualTiltSlider.vue +git commit -m "feat(lenticular): 新增 ManualTiltSlider 独立组件 + +- uniapp 包装,0..1 双向绑定 +- 内置 ↺ 复位按钮 +- value validator 拦截 0..1 范围外输入 + +Co-Authored-By: Claude " +``` + +--- + +## Task 3: LenticularCard.vue 加 slider 接入 + 改 touchend 行为 + +**Files:** +- Modify: `frontend/components/lenticular/LenticularCard.vue:54-63, 209-212` + +**Interfaces:** +- Consumes: Task 2 产出的 `` +- Produces: 新增 props `showSlider` / `sliderValue`;新增 emit `update:sliderValue` / `sliderReset`;touchend 不再 emit (0,0) + +- [ ] **Step 1: 添加 showSlider / sliderValue props** + +在 `frontend/components/lenticular/LenticularCard.vue` 的 `defineProps({...})` 块(line 54-63)末尾追加: + +```js + // 新增:手动滑块接入(spec §3.3) + showSlider: { type: Boolean, default: false }, + sliderValue: { type: Number, default: 0.5 }, +``` + +- [ ] **Step 2: 在 defineEmits 中添加 update:sliderValue / sliderReset** + +找到 `defineEmits`(如果有)或 `