# 项目长期记忆 ## 项目概述 - 项目名:xinlong-shop-taro(Taro 微信小程序商城) - 技术栈:Taro + React + TypeScript + Tailwind CSS - 后端 API 前缀:`/shop/`、`/api/` - 门店店员身份判断:通过 `getMyClerk()` API (`/shop/shop-store-user/my`) 返回值判断 - VIP/分销商申请记录表:`ShopDealerApply`,状态码 10待审核/20通过/30驳回 ## 用户状态与禁用机制 - 用户状态字段:`User.status`,`0` = 正常,`1` = 禁用 - 禁用接口:`updateUserStatus(userId, status)` → PUT `/system/user/status` - 禁用拦截工具:`src/utils/auth.ts` 中 `isUserDisabled(user)` 和 `blockDisabledUser(user)` - 拦截层级:UserContext 启动异步校验 + refreshUser/syncFromStorage/loginUser 检查 + 登录 API 和登录页检查 - 用户管理页:`src/pages/store/users/index.tsx`(门店中心入口,支持搜索/筛选/查看详情/禁用启用) ## VIP 会员机制 - 用户在 `vip-upgrade` 页面提交门店名称和地址申请 - 门店店员在 `vip-review` 页面审核(通过/驳回) - VIP 会员特权:商品显示 `dealerPrice` 价格,下单使用该价格结算 - VIP 状态判断:`isVipMember()` 从 localStorage 缓存读取(同步),`checkAndCacheVipStatus(userId)` 异步更新 - **响应式 VIP 状态**:使用 `useVipStatus` hook(`src/hooks/useVipStatus.ts`),组件挂载时异步校验并更新缓存 + React state,isVip 变化触发重渲染 - **已接入 `useVipStatus` 的页面/组件**:商品详情、结算、商城首页、分类页、购物车页、CartContext、SkuSelector、ProductCard - `OrderGoodsItem` 接口新增 `price` 字段,VIP 下单时传 dealerPrice 给后端 ## 商品价格字段约定 - `product.price` - 到手价(主价格) - `product.salePrice` - 市场价(划掉的) - `product.dealerPrice` - VIP 会员专享价 - `product.memberStorePrice` - 会员价 ## 关键文件位置 - VIP 工具:`src/utils/vip.ts` - VIP Hook:`src/hooks/useVipStatus.ts` - VIP 审核页:`src/pages/user/vip-review/index.tsx` - VIP 升级页:`src/pages/user/vip-upgrade/index.tsx` - 门店中心:`src/pages/store/center/index.tsx` - 购物车 API:`src/api/shop/shopCart/index.ts`(批量删除接口 `/shop/shop-cart/batch` 后端期望直接接收 `ArrayList` 即 `[1,2,3]`,不要用 `{ ids }` 包裹) - 购物车 Context:`src/contexts/CartContext.tsx` ## 订阅消息 - 模板 ID:`sh1K9iK7vZjebUNFu6OsMsnsJxm4whThWGrhN7I4zVg`(交易提醒) - 字段:订单号(`character_string1`)、商品名称(`thing4`)、联系人(`thing10`)、联系电话(`phone_number7`)、送货地址(`thing8`) - 场景说明:下单提醒 - 新订单检测 Hook:`src/hooks/useNewOrderDetector.ts`(30秒轮询,页面隐藏暂停) ## 微信隐私协议(基础库 3.16.1+ 强制) - 报错特征:`chooseImage:fail api scope is not declared in the privacy agreement` + `errno:112` - 两处代码修复: 1. 调用 `Taro.chooseImage` 前必须先 `getPrivacySetting` → `requirePrivacyAuthorize` 预检(封装在 `src/api/system/file/index.ts` 的 `ensurePrivacyAuthorized()` 里) 2. `src/app.tsx` 的 `useLaunch` 中注册 `Taro.onNeedPrivacyAuthorization` 回调(用 `showModal` 自定义弹窗) - 一处手动配置:**小程序管理后台** → 设置 → 第三方设置 → 用户隐私保护指引 → 添加「开发者收集你的相册/摄像头」声明 - ⚠️ **`requiredPrivateInfos` 字段只接受位置类 API 白名单**(chooseAddress/chooseLocation/choosePoi/getFuzzyLocation/getLocation/onLocationChange/startLocationUpdate/startLocationUpdateBackground),不要加 `chooseImage`/`chooseMedia` 等非位置类,会导致 app.json 解析失败 - 直接调 `Taro.chooseImage` 的页面:`pages/order/evaluate/index.tsx`、`pages/after-sale/apply/index.tsx`、`pages/store/orders/index.tsx` —— 这些页面绕过了 `uploadFile`,需要单独加 `ensurePrivacyAuthorized` 预检 ## 分享 / 朋友圈能力(2026-07-13 接入) - 统一封装:`src/hooks/useShare.ts`(`useShare({title, path?, query?, imageUrl?, enableTimeline?, enableCopyUrl?})`,一次注册 `useShareAppMessage` + `useShareTimeline` + 可选 `onCopyUrl` 复制链接;自动在 path/query 追加 `inviter=${当前用户id}` 做裂变归因;`isTimelineSinglePage()` 判断朋友圈单页模式 scene===1154) - 海报组件:`src/components/SharePoster/index.tsx`(Canvas 2D 绘制封面+标题+价格+小程序码,返回临时图路径作 imageUrl) - 小程序码接口:`src/api/share.ts` `generateShareCode(page, inviterId)` 复用 `generateMiniProgramCode`(scene 用 `uid_${inviterId}`,page 透传但后端需读取转发才生效) - 裂变约定:分享链接 query 用 `inviter`,与 `src/utils/invite.ts` 的 `parseInviteParams` 读取字段一致 - 已接入页面:商品详情、积分商品、拼团、秒杀、活动、赛事(带 canvas 海报);邀请下级、分销推广、升级VIP、分享赚佣金(仅 enableShare + useShare,无海报);首页(好友+朋友圈+复制链接三按钮全亮) - 注意:tabBar 页**也支持朋友圈分享**(之前误判已纠正)——只要 config 开 enableShareTimeline + 定义 onShareTimeline 即显示按钮;"tabBar 不渲染"仅指朋友圈打开后的单页模式里底部 tabBar 不显示。朋友圈/复制链接开发者工具可能看不到入口,必须真机测试。复制链接是 Beta(基础库 2.14.3+)。 ## 订单备注字段约定(2026-07-15) - `buyerRemarks` — 买家备注:用户下单时填写的订单备注(checkout 页提交) - `merchantRemarks` — 商户备注:门店修改金额时填写的原因记录 - `comments` — 系统/其他备注:保留给系统内部使用 - `ShopOrder` 和 `OrderCreateRequest` 接口均已包含 `buyerRemarks` 和 `merchantRemarks` 字段