- useRequest 改为 stale-while-revalidate 机制,缓存命中时立即展示数据并后台刷新 - 增加本地存储持久化,冷启动时先展示存储数据,避免骨架屏 - user 页面使用 mutate 结合本地缓存即时更新界面 - 网络请求失败时保留缓存数据,仅清除 loading 状态 - 优化加载逻辑,保障 30 秒缓存内数据可快速响应且自动刷新
9.4 KiB
9.4 KiB
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,附带驳回原因)
- 使用
updateShopDealerApplyAPI 进行审核操作 - 与 vip-upgrade/index.tsx 配套:用户提交申请 → 店员实地考察 → 审核通过/驳回
3. VIP会员价格特权(dealerPrice)
- 创建了 VIP 状态工具
src/utils/vip.ts:isVipMember()- 同步从缓存读取 VIP 状态checkAndCacheVipStatus(userId)- 异步检查并缓存(查询 ShopDealerApply applyStatus=20)getDisplayPrice(product)- 获取商品对当前用户的实际价格
- 创建了
src/hooks/useVipStatus.tsHook - 修改了以下页面/组件支持 VIP 显示 dealerPrice:
ProductCard- 商品列表卡片价格product-detail.tsx- 商品详情页价格 + 立即购买传参SkuSelector- SKU 选择弹窗价格 + 确认时 fakeSku 价格cart.tsx- 购物车商品价格 + 推荐商品价格CartContext.tsx-calcPrice计算总价时使用 dealerPricecheckout.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.name、storeInfo.phone) - TypeScript 类型检查通过
涉及文件(补充)
- 修改:
src/pages/store/center/index.tsx
9. 商品详情页价格下方 0 值隐藏
- 修改
product-detail.tsx价格区域 - 给
dealerPrice、salePrice、memberStorePrice增加Number(value) > 0判断 - 0 或无效值时不再渲染,避免价格下方出现单独的 "0"
- TypeScript 类型检查通过
涉及文件(补充)
- 修改:
src/pages/shop/product-detail.tsx
10. 新订单提醒功能(前端轮询 + 订阅消息)
10.1 创建 useNewOrderDetector Hook
- 新建
src/hooks/useNewOrderDetector.ts - 封装新订单轮询检测逻辑:
- 首次加载自动建立订单 ID 基准线,不触发提醒
- 后续轮询对比发现新订单后触发
onNewOrders回调 - 页面隐藏时自动暂停轮询,显示时恢复
- 提供
clearUnread()方法清除未读计数
- 导出类型:
NewOrderItem、UseNewOrderDetectorOptions、UseNewOrderDetectorReturn - 注册到
src/hooks/index.ts
10.2 改造订单管理页(前端轮询)
- 修改
src/pages/store/orders/index.tsx - 集成
useNewOrderDetector:30 秒轮询,静默检测新订单 - 发现新订单时:震动提醒(
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.tsx的SUBSCRIBE_TMPL_IDS数组 - 后端在订单创建成功后调用
subscribeMessage.sendAPI 向店员推送
涉及文件
- 新建:
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 - 功能:客户下单后自动推送微信小程序订阅消息给门店所有店员
- 流程:
- 根据
storeId查询shop_store_user表获取店员 userId 列表 - 批量查询
sys_user表获取店员小程序 openid - 调用
WxMiniappAccessTokenService获取 access_token - 调用微信
subscribeMessage.sendAPI 逐个发送
- 根据
- 容错设计:
- 订单无 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 填入 + 错误提示优化
- 模板 ID:
sh1K9iK7vZjebUNFu6OsMsnsJxm4whThWGrhN7I4zVg(交易提醒) - 字段:订单号(
character_string1)、商品名称(thing4)、联系人(thing10)、联系电话(phone_number7)、送货地址(thing8) - 优化
requestSubscribeMessagefail 回调:errCode 20001 提示模板未配置,20002/20004 提示已取消
13. 门店中心页 loading 状态修复
store/center/index.tsx的useEffect遗漏.finally(() => setLoading(false)),导致页面卡在"加载中"- 已修复
14. 分类页改造为左侧分类 + 右侧商品列表布局
- 重写
src/pages/shop/category.tsx - 左侧:一级分类列表(绿色高亮选中态,保持不变)
- 右侧:
- 排序栏:默认/销量/最新/价格(可切换升降序)/筛选
- 商品卡片:横向布局(左图 + 右侧标题/积分标签/价格/销量 + 加购按钮)
- 滚动触底分页加载
- 点击商品跳转详情页,点击加购按钮调用
addToCartAPI - 复用现有 VIP 价格逻辑(
isVipMember+dealerPrice) - 编译通过
15. 用户页余额/积分/优惠券/订单数加载优化
问题分析
useRequest的缓存是内存级Map,冷启动丢失 → 永远显示骨架屏- 缓存命中后直接
return不发起网络请求 → 30s 内数据不更新(注释说"静默刷新"但实际没做) - 无 localStorage 持久化 → 每次打开页面都要等网络
修复 useRequest 缓存为 stale-while-revalidate
- 修改
src/hooks/useRequest.ts的doRequest方法 - 缓存命中时:立即展示缓存数据(loading=false),不 return,继续发网络请求后台刷新
- 无缓存时:正常显示 loading skeleton
- 网络失败时:有缓存则保留缓存数据不清除,仅清 loading 状态
添加 localStorage 持久化
- 修改
src/pages/user/user.tsx useRequest增加onSuccess回调:请求成功后Taro.setStorageSync持久化useDidShow和useEffect(isLoggedIn)中:先读Taro.getStorageSync并用mutate即时展示,再调用run后台刷新- 效果:冷启动 0ms 展示上次数据 → 后台静默更新
涉及文件
- 修改:
src/hooks/useRequest.ts - 修改:
src/pages/user/user.tsx