Files
xinlong-shop-taro/.workbuddy/memory/2026-07-04.md
赵忠林 b07a3525db fix(user): 优化用户页面余额积分等数据加载体验
- useRequest 改为 stale-while-revalidate 机制,缓存命中时立即展示数据并后台刷新
- 增加本地存储持久化,冷启动时先展示存储数据,避免骨架屏
- user 页面使用 mutate 结合本地缓存即时更新界面
- 网络请求失败时保留缓存数据,仅清除 loading 状态
- 优化加载逻辑,保障 30 秒缓存内数据可快速响应且自动刷新
2026-07-04 15:34:46 +08:00

9.4 KiB
Raw Blame History

2026-07-04 工作日志

VIP会员审核功能开发

功能概述

为 xinlong-shop-taro 小程序新增了 VIP 会员审核功能,包含以下三个核心模块:

1. VIP会员审核菜单入口user.tsx

  • 在"门店中心"下方添加了"VIP会员审核"菜单项
  • 仅门店店员身份显示(与门店中心相同的 storeInfo 判断条件)
  • 点击跳转到 /pages/user/vip-review/index
  • 同时在页面加载、useDidShow、下拉刷新时调用 checkAndCacheVipStatus 更新当前用户的 VIP 状态缓存

2. VIP会员审核页面vip-review/index.tsx

  • 新建页面 src/pages/user/vip-review/index.tsx
  • 页面进入时验证当前用户是否为店员(通过 getMyClerk()
  • 三个 Tab待审核(applyStatus=10)、已通过(20)、已驳回(30)
  • 展示申请人的门店名称、门店地址、申请时间
  • 店员可"通过"(设 applyStatus=20或"驳回"(设 applyStatus=30附带驳回原因
  • 使用 updateShopDealerApply API 进行审核操作
  • 与 vip-upgrade/index.tsx 配套:用户提交申请 → 店员实地考察 → 审核通过/驳回

3. VIP会员价格特权dealerPrice

  • 创建了 VIP 状态工具 src/utils/vip.ts
    • isVipMember() - 同步从缓存读取 VIP 状态
    • checkAndCacheVipStatus(userId) - 异步检查并缓存(查询 ShopDealerApply applyStatus=20
    • getDisplayPrice(product) - 获取商品对当前用户的实际价格
  • 创建了 src/hooks/useVipStatus.ts Hook
  • 修改了以下页面/组件支持 VIP 显示 dealerPrice
    • ProductCard - 商品列表卡片价格
    • product-detail.tsx - 商品详情页价格 + 立即购买传参
    • SkuSelector - SKU 选择弹窗价格 + 确认时 fakeSku 价格
    • cart.tsx - 购物车商品价格 + 推荐商品价格
    • CartContext.tsx - calcPrice 计算总价时使用 dealerPrice
    • checkout.tsx - 结算页金额计算 + 单价显示

4. 路由注册

  • app.config.ts 中注册了 pages/user/vip-review/index

5. VIP升级页字段来源修正

  • 修改 vip-upgrade/index.tsx
  • 门店名称和门店地址不再从 user 对象读取,统一从 shop-dealer-apply 表最新记录读取
  • 待审核(10) 和 已通过(20) 时只读展示;已驳回(30) 时允许编辑并重新提交
  • TypeScript 类型检查通过

6. 审核通过时添加 VIP 角色

  • vip-review/index.tsx 中新增了 assignVipRole 辅助函数
  • 审核通过applyStatus=20自动调用 addUserRole 给申请人添加 VIP会员 角色roleId=2032, roleCode=vip
  • 先调用 listUserRole({ userId }) 检查用户是否已有 VIP 角色,避免重复绑定
  • TypeScript 类型检查通过

7. 门店中心改造为仪表盘

  • store/center/index.tsx 从订单管理页改造为功能入口仪表盘
  • 新建 store/orders/index.tsx,将原订单管理逻辑完整搬入
  • VIP会员审核入口从 user.tsx 独立菜单移除,统一放入门店中心仪表盘
  • 门店中心仪表盘功能卡片支持扩展FEATURE_CARDS 数组,新增只需加一项)
  • VIP会员审核卡片显示待审核数量角标红底白字
  • TypeScript 类型检查通过

8. 门店中心顶部显示店员头像和门店名称

  • 修改 store/center/index.tsx 顶部信息区
  • 左侧显示圆形店员头像(读取 storeInfo.avatar),无头像时显示默认 👤 图标
  • 右侧显示门店名称(storeInfo.storeName)和店员姓名 + 手机号(storeInfo.namestoreInfo.phone
  • TypeScript 类型检查通过

涉及文件(补充)

  • 修改:src/pages/store/center/index.tsx

9. 商品详情页价格下方 0 值隐藏

  • 修改 product-detail.tsx 价格区域
  • dealerPricesalePricememberStorePrice 增加 Number(value) > 0 判断
  • 0 或无效值时不再渲染,避免价格下方出现单独的 "0"
  • TypeScript 类型检查通过

涉及文件(补充)

  • 修改:src/pages/shop/product-detail.tsx

10. 新订单提醒功能(前端轮询 + 订阅消息)

10.1 创建 useNewOrderDetector Hook

  • 新建 src/hooks/useNewOrderDetector.ts
  • 封装新订单轮询检测逻辑:
    • 首次加载自动建立订单 ID 基准线,不触发提醒
    • 后续轮询对比发现新订单后触发 onNewOrders 回调
    • 页面隐藏时自动暂停轮询,显示时恢复
    • 提供 clearUnread() 方法清除未读计数
  • 导出类型:NewOrderItemUseNewOrderDetectorOptionsUseNewOrderDetectorReturn
  • 注册到 src/hooks/index.ts

10.2 改造订单管理页(前端轮询)

  • 修改 src/pages/store/orders/index.tsx
  • 集成 useNewOrderDetector30 秒轮询,静默检测新订单
  • 发现新订单时:震动提醒(Taro.vibrateShort+ Toast 提示("您有 N 条新订单"
  • "全部" Tab 增加红色角标显示未读新订单数(>99 显示 99+
  • 切换 Tab 时自动清除未读计数

10.3 改造门店中心页

  • 修改 src/pages/store/center/index.tsx
  • 订单管理卡片增加待处理订单角标(查询 statusFilter=1 的 total
  • VIP 审核角标同步增强,统一使用 getBadgeCount() 方法
  • 底部新增"接收新订单提醒"卡片:
    • 调用 Taro.requestSubscribeMessage 请求微信订阅消息授权
    • 模板 ID 通过 SUBSCRIBE_TMPL_IDS 常量配置(待微信公众平台申请后填入)
    • 授权成功后 Toast 提示

待后端配合

  • 微信公众平台申请订阅消息模板("新订单通知"类目)
  • 将模板 ID 填入 store/center/index.tsxSUBSCRIBE_TMPL_IDS 数组
  • 后端在订单创建成功后调用 subscribeMessage.send API 向店员推送

涉及文件

  • 新建:src/hooks/useNewOrderDetector.ts
  • 修改:src/hooks/index.ts
  • 修改:src/pages/store/orders/index.tsx
  • 修改:src/pages/store/center/index.tsx

11. 后端订阅消息发送服务

11.1 新建 WxSubscribeMessageService

  • 接口:/JAVA/shop-api/src/main/java/com/gxwebsoft/shop/service/WxSubscribeMessageService.java
  • 实现:/JAVA/shop-api/src/main/java/com/gxwebsoft/shop/service/impl/WxSubscribeMessageServiceImpl.java
  • 功能:客户下单后自动推送微信小程序订阅消息给门店所有店员
  • 流程:
    1. 根据 storeId 查询 shop_store_user 表获取店员 userId 列表
    2. 批量查询 sys_user 表获取店员小程序 openid
    3. 调用 WxMiniappAccessTokenService 获取 access_token
    4. 调用微信 subscribeMessage.send API 逐个发送
  • 容错设计:
    • 订单无 storeId 跳过
    • 门店无店员/店员无 openid 跳过
    • access_token 获取失败跳过
    • errcode 43101用户未授权订阅正常跳过不报错
    • 所有异常不向上抛出,不影响订单创建

11.2 集成到 OrderBusinessService

  • 修改 OrderBusinessService.createOrder() 方法
  • 在保存订单和商品后、创建微信支付订单前,调用 sendNewOrderNotification
  • 外层 try-catch 确保通知失败不影响主流程

涉及文件

  • 新建:JAVA/shop-api/.../service/WxSubscribeMessageService.java
  • 新建:JAVA/shop-api/.../service/impl/WxSubscribeMessageServiceImpl.java
  • 修改:JAVA/shop-api/.../service/OrderBusinessService.java

12. 订阅消息模板 ID 填入 + 错误提示优化

  • 模板 IDsh1K9iK7vZjebUNFu6OsMsnsJxm4whThWGrhN7I4zVg(交易提醒)
  • 字段:订单号(character_string1)、商品名称(thing4)、联系人(thing10)、联系电话(phone_number7)、送货地址(thing8)
  • 优化 requestSubscribeMessage fail 回调errCode 20001 提示模板未配置20002/20004 提示已取消

13. 门店中心页 loading 状态修复

  • store/center/index.tsxuseEffect 遗漏 .finally(() => setLoading(false)),导致页面卡在"加载中"
  • 已修复

14. 分类页改造为左侧分类 + 右侧商品列表布局

  • 重写 src/pages/shop/category.tsx
  • 左侧:一级分类列表(绿色高亮选中态,保持不变)
  • 右侧:
    • 排序栏:默认/销量/最新/价格(可切换升降序)/筛选
    • 商品卡片:横向布局(左图 + 右侧标题/积分标签/价格/销量 + 加购按钮)
    • 滚动触底分页加载
  • 点击商品跳转详情页,点击加购按钮调用 addToCart API
  • 复用现有 VIP 价格逻辑(isVipMember + dealerPrice
  • 编译通过

15. 用户页余额/积分/优惠券/订单数加载优化

问题分析

  • useRequest 的缓存是内存级 Map,冷启动丢失 → 永远显示骨架屏
  • 缓存命中后直接 return 不发起网络请求 → 30s 内数据不更新(注释说"静默刷新"但实际没做)
  • 无 localStorage 持久化 → 每次打开页面都要等网络

修复 useRequest 缓存为 stale-while-revalidate

  • 修改 src/hooks/useRequest.tsdoRequest 方法
  • 缓存命中时立即展示缓存数据loading=false不 return,继续发网络请求后台刷新
  • 无缓存时:正常显示 loading skeleton
  • 网络失败时:有缓存则保留缓存数据不清除,仅清 loading 状态

添加 localStorage 持久化

  • 修改 src/pages/user/user.tsx
  • useRequest 增加 onSuccess 回调:请求成功后 Taro.setStorageSync 持久化
  • useDidShowuseEffect(isLoggedIn) 中:先读 Taro.getStorageSync 并用 mutate 即时展示,再调用 run 后台刷新
  • 效果:冷启动 0ms 展示上次数据 → 后台静默更新

涉及文件

  • 修改:src/hooks/useRequest.ts
  • 修改:src/pages/user/user.tsx