Files
xinlong-shop-taro/.workbuddy/memory/2026-07-14.md
赵忠林 dfbcbcf615 feat(payment): 新增线下付款方式及相关订单状态支持
- 支付页面新增线下付款选项,支持微信转账付款方式
- 订单流程新增线下付款待确认(offline_pending)阶段
- 订单详情与订单卡片显示线下付款状态及提示信息
- 支付接口和模型增加线下付款支付方式(payType=9)支持
- 订单列表页区分线下付款待确认与待收款状态
- 地址编辑页修复智能识别按钮无法点击问题,调整布局避免被Textarea遮挡
2026-07-14 22:15:31 +08:00

21 KiB
Raw Blame History

购物车功能修复2026-07-14

问题

购物车页面pages/shop/cart商品显示异常

  • 商品名称/图片/价格信息缺失
  • 加入购物车后无法正确显示

根因

CartContext.refresh() 方法在将后端 API 返回的购物车项映射为前端 CartItem 时,遗漏了 product(商品详情)和 skuSKU 详情)嵌套对象的映射。导致页面中 item.product?.name/image/priceitem.sku?.price/image 均为 undefinedUI 只能显示硬编码的默认值。

修改

  1. src/contexts/CartContext.tsx(核心修复)

    • refresh()mappedItems 映射中补充 productsku 字段
    • 使用 (item as any).product / (item as any).sku 获取后端返回的嵌套对象
  2. src/utils/image.ts(增强健壮性)

    • 新增 ensureFullUrl() 辅助函数:相对路径自动补全 OSS 域名 https://oss.wsdns.cn
    • getCompressedImageUrl() 内部调用 ensureFullUrl 确保 URL 完整性
  3. src/pages/shop/cart.tsx(补充图片回退源)

    • 图片 src 回退链增加 (item.sku as any)?.image
    • 完整回退链:goodsImage -> product.image -> sku.image -> ''

收藏列表图片/价格不显示修复2026-07-14

问题

pages/user/favorite-list/index 收藏列表:商品图片、价格、名称均空白。

根因

后端 /api/shop/goods/favorite/list(前端 /shop/goods/favorite/list)走 ShopGoodsFavoriteServiceImpl.listRelselectListRelSQL 只 SELECT a.* 未关联商品表,返回字段仅 id/userId/goodsId/merchantId/tenantId/createTime,不含商品信息。前端依赖 goodsImage/goodsName/price/salePrice 均为 undefined。

修改方案1后端返回商品信息

  1. 后端 guilixu-java

    • ShopGoodsFavorite.java:新增 4 个 @TableField(exist=false) 非持久化字段 goodsName(String)goodsImage(String)price(BigDecimal)salePrice(BigDecimal),并补 BigDecimal/TableField import。
    • ShopGoodsFavoriteMapper.xmlselectSqlSELECT a.*, g.name AS goods_name, g.image AS goods_image, g.price AS price, g.sale_price AS sale_priceLEFT JOIN shop_goods g ON a.goods_id = g.goods_id(已确认 map-underscore-to-camel-case: true,别名可映射)。
    • 注意:本机无 maven 无法编译,改动为常规 MyBatis/MP 写法,需用户自行编译部署。
  2. 前端

    • src/api/shop/shopGoodsFavorite/model.tsfavoriteId 改为 id(后端实际返回 id);新增 price?: string|numbersalePrice 改为 string|number
    • pages/user/favorite-list/index.tsx:图片用 item.goodsImage;价格改为优先 item.price(到手价),salePrice 作为划线价(salePrice > price 时显示);key 改用 item.id ?? item.goodsId(原 favoriteId 始终 undefined 会导致 key 重复)。
    • 注:src/pages/favorite-list.tsx 为未注册路由的重复文件,未改动。

遗留(非本次范围,供参考)

adapter /list 接口忽略分页,一次返回全部收藏;前端 listShopGoodsFavorite({page,limit:20}) 的无限滚动在收藏>20条时会重复追加。如需彻底解决建议改前端走 /page 接口或后端 /list 支持分页。

购物车更新 API 请求方式修复2026-07-14

问题

PUT /api/shop/shop-cart/{id} 返回 HttpRequestMethodNotSupportedException: Request method 'PUT' not supported

根因

src/api/shop/shopCart/index.ts 中 3 个函数用了非标准 URL 模式 PUT /shop/shop-cart/{id},而项目所有其他 APIshopOrder、shopArticle 等)的更新操作统一走 PUT /shop/shop-cart(基础 URLid 放 body

修改

  1. updateCartNumPUT /shop/shop-cart/{id} + {quantity}PUT /shop/shop-cart + {id, num}
  2. updateCartCheckedPUT /shop/shop-cart/{id} + {selected}PUT /shop/shop-cart + {id, checked}(同时修正字段名 selectedchecked 与 model 一致)
  3. updateCartAllCheckedPUT /shop/shop-cart/selected + {selected}POST /shop/shop-cart/selected + {checked}(改用 POST自定义批量操作更可能支持 POSTCartContext 已有静默 catch 兜底)

购物车后端 SQL 关联查询修复2026-07-14

问题

GET /api/shop/shop-cart 返回的数据没有商品图片、价格等信息。前端 CartContext.refresh() 已映射 goodsName/goodsImage/skuPrice/skuSpec/stock 等字段,但后端根本没返回这些字段。

根因

后端 ShopCartMapper.xml 的 SQL 只做 SELECT a.* FROM shop_cart a,没有 JOIN 商品表和 SKU 表。

后端修改(guilixu-java 项目)

  1. ShopCart.java:新增 7 个 @TableField(exist = false) 非持久化字段:goodsNamegoodsImageskuPriceskuSpecstocksalePricedealerPrice;补充 TableField import
  2. ShopCartMapper.xmlSQL 改为 LEFT JOIN shop_goods g ON a.goods_id = g.goods_id + LEFT JOIN shop_goods_sku s ON a.sku_id = s.idSELECT 别名映射到新字段(COALESCE(s.price, g.price) AS sku_priceCOALESCE(s.stock, g.stock) AS stock);同时修复 userId/goodsId/merchantIdLIKE 改为 =(原 LIKE 会导致 userId=1 匹配到 10/11/21 等)
  3. ShopCartController.javalist() 方法自动按当前登录用户过滤(param.setUserId(loginUser.getUserId().longValue())

前端修改

  1. src/api/shop/shopCart/model/index.tsShopCart 接口新增 salePricedealerPrice 字段
  2. src/contexts/CartContext.tsxCartItem 新增 salePricedealerPricerefresh() 映射这两个字段;calcPrice() VIP 优先用扁平 i.dealerPrice(之前只看 i.product?.dealerPrice
  3. src/pages/shop/cart.tsx + src/pages/shop/checkout.tsx:价格显示逻辑同步更新,优先使用扁平 item.dealerPrice / item.salePrice

购物车前后端字段名对齐修复2026-07-14

问题

修改购物车数量时后端报 UPDATE shop_cart WHERE id=?SET 子句为空MyBatis-Plus 找不到要更新的字段。

根因

前后端字段名不匹配:

  • 前端发送 num,后端实体字段是 cartNum → Jackson 反序列化时 cartNum 为 null → 空 UPDATE
  • 前端发送 checked,后端实体字段是 selected → 同样不匹配
  • 列表返回 cartNum,前端读 item.num → 数量永远为默认值 1

修改文件

  1. src/api/shop/shopCart/model/index.tsShopCart.numcartNum;新增 selected 字段;AddToCartParam.numcartNumUpdateCartNumParam.numcartNum
  2. src/api/shop/shopCart/index.tsupdateCartNum{id, cartNum}updateCartChecked{id, selected}
  3. src/contexts/CartContext.tsxrefresh()item.cartNumitem.selectedaddItem()cartNumupdateQuantity 调用 {id, cartNum}
  4. src/pages/shop/index.tsx + src/pages/shop/category.tsxaddToCart 参数 numcartNum

禁用 loginByOpenId 静默登录2026-07-14

改动

  • src/contexts/UserContext.tsx:移除"情况3"静默登录块(Taro.login + loginByOpenId),无 token 时直接保持未登录状态;清理 loginByOpenIdTenantId 无用导入
  • src/hooks/useUser.ts:移除降级方案中的 loginByOpenId 调用,仅从 storage 读取;清理 loginByOpenIdTenantId 无用导入
  • src/api/layout/index.tsloginByOpenId 函数定义保留,仅不再被调用

商城分类页价格登录可见2026-07-14

改动

  • src/pages/shop/index.tsx:价格显示从手写 Text 改为 Price 组件 + loginMask 属性(与首页一致),游客看到"登录后查看价格"VIP 划线价逻辑保留在 original prop 中;单位 unitName 仅登录后显示

订单列表页未登录可查看订单 Bug 修复2026-07-14

问题

pages/order/list 未登录用户也能看到订单数据。

根因

页面缺少登录校验——useEffect 直接调用 loadOrders,未登录时 user?.userIdundefined,后端未按用户过滤直接返回了订单。

修改

  • src/pages/order/list.tsx
    1. useUser() 获取 isLoggedIn
    2. useEffect 中未登录时调用 requireLogin({ action: 'viewOrder', redirect: '/pages/order/list', content: '登录后才能查看订单,是否前往登录?' }) 弹窗引导,而不是直接跳转
    3. render 中 if (!isLoggedIn) return null 兜底,避免未登录时渲染订单内容
  • src/passport/login.tsx:登录成功后的 tabBarUrls 白名单补全 pages/order/list,确保从订单入口登录后能 switchTab 回订单页

我的页面未登录可点击需登录功能 Bug 修复2026-07-14

问题

pages/user/user 未登录时,点击「我的订单」下的按钮(待付款/待发货/待收货/已完成、「全部订单」、以及菜单中的「升级VIP会员」「我的钱包」「收货地址」「我的收藏」都能直接跳转页面。

修改

  • src/pages/user/user.tsx
    1. 导入 requireLogin from @/utils/login-guard
    2. 给需要登录的菜单项加 requireAuth: true 标记升级VIP会员/门店中心/我的钱包/收货地址/我的收藏)
    3. 新增 handleMenuClick(item)requireAuth 项调用 requireLogin 弹窗拦截,已登录才 navigateTo
    4. 新增 handleOrderTabClick(status)handleViewAllOrders():订单入口 requireLogin 拦截,并加 redirect: '/pages/order/list' 让登录后回跳订单页
    5. 未登录时需登录的菜单项和订单按钮加 opacity-50 视觉降级
    6. 「帮助中心」「设置」不需要登录,保持原样

订单列表页登录后仍看到别人订单 Bug 修复2026-07-14

问题

pages/order/list 刚登录成功后仍能看到别人的订单,刷新重进后才正常。

根因

loadOrdersuseCallback 依赖数组为 [updateTabState],未包含 user,导致闭包过期——登录后 isLoggedIntrue 触发 effect 调用 loadOrders,但闭包中的 user 仍是初始渲染时的 nulluserId: user?.userId 传了 undefined,后端未按用户过滤返回了全部订单。

修改

  • src/pages/order/list.tsx
    1. 新增 userIdRefuseRef(user?.userId)),每渲染同步 userIdRef.current = user?.userId,避免闭包过期
    2. loadOrders 开头从 userIdRef.current 取 userIdif (!userId) return 硬拦截——没有当前登录用户 userId 就禁止加载
    3. useEffect 中增加 if (!user?.userId) return 判断,已登录但 userId 还没拿到时等待下次渲染
    4. useEffect 依赖数组加 user?.userIduserId 变化时重新触发加载

商城列表页浮动购物车按钮2026-07-14

需求

pages/shop/index 页面右下角添加浮动购物车按钮,显示购物车商品数量角标。

实现

  • src/pages/shop/index.tsx
    1. useCartContext() 额外解构 totalCount
    2. 页面末尾添加 fixed 定位浮动按钮绿色圆形48pxbottom:80px right:16px(避开 tabBar
    3. totalCount > 0 时右上角显示红色角标,超过 99 显示 99+
    4. 点击 Taro.navigateTo 跳转购物车页cart 非 tabBar 页)
    5. 数量实时更新:addItem 内部调 refresh()totalCount 派生值自动重渲染

购物车页面进入不刷新 Bug 修复2026-07-14

问题

从商品列表页/分类页加入购物车后,进入购物车页面,新加的商品不显示,只有之前已在购物车里的商品。

根因

  1. 购物车页面缺少 useDidShowcart.tsx 仅在 useEffect([isLoggedIn]) 中调用 refresh(),登录状态不变时不会重新拉取数据。从其他页面返回购物车时,页面不刷新。
  2. 商品列表页/分类页直接调 API 加购shop/index.tsxshop/category.tsx 直接调用 addToCart API未通过 CartContext.addItem,导致 CartContext 状态不同步(addItem 内部会调 refresh())。

修改

  1. src/pages/shop/cart.tsx:导入 useDidShow,添加 useDidShow(() => { if (isLoggedIn) refresh() }),页面每次显示时从服务端拉取最新购物车数据
  2. src/pages/shop/index.tsx:导入 useCartContexthandleAddToCart 改用 addItem(product, undefined, product.step || 1) 代替直接调 addToCart API移除 addToCart 导入
  3. src/pages/shop/category.tsx:同上,改用 useCartContextaddItem

需求

首页顶部搜索栏右侧铃铛图标,门店店员有新订单时能收到提醒。

改动

  • src/pages/index/index.tsx
    1. 导入 useNewOrderDetectorgetMyClerkpageShopOrderuseDidShowuseCallbackuseRef
    2. 登录后调 getMyClerk() 检测店员身份,存 isClerk state + isClerkRef refref 用于轮询回调中读取最新值,避免闭包过期)
    3. useDidShow 时也刷新店员身份
    4. 接入 useNewOrderDetector30秒轮询 pageShopOrder({page:1,limit:5})fetchLatestOrders 内用 isClerkRef.current 网关——非店员直接返回空数组(不建 baseline无副作用
    5. 新订单回调:Taro.vibrateShort 震动 + Taro.showToast 提示"您有 N 条新订单"
    6. 铃铛角标从静态红点改为动态:newOrderCount > 0 显示数字角标(>99 显示 99+=== 0 时显示小圆点
    7. 点击处理:店员 → navigateTo 门店订单页(有新订单时先 clearUnread());普通用户 → 通知页

设计要点

  • isClerkRef 解决 hooks 闭包过期问题——fetchLatestOrdersuseCallback 依赖空数组,但通过 ref 读取最新店员状态
  • 非店员时 fetchLatestOrders 返回 []hook 内 doChecklist.length === 0 时 returnknownOrderIdsRef 保持 null 不建 baseline店员身份确认后首次真实请求才建 baseline不会误报
  • useNewOrderDetector 内部已处理 useDidShow/useDidHide 生命周期tabBar 页切走自动停轮询

商品分类页浮动购物车图标优化2026-07-14

问题

商品分类页pages/shop/index右下角绿色圆形浮动购物车按钮使用 emoji 🛒 作为图标,在绿色背景上对比度不足、线条不清晰。

修改

  1. 新增白色线条购物车 SVG 图标:src/assets/icons/cart.svg
  2. 导出 base64 数据 URI 常量:src/assets/icons/index.tsCART_ICON_WHITE
  3. src/pages/shop/index.tsx 浮动购物车按钮改用 Image 组件加载 CART_ICON_WHITE28×28pxmode='aspectFit'),图标更清晰

商品详情页出现孤立 "0" 排查修复2026-07-14

问题

pages/shop/product-detail 价格区域下方出现一个不归属任何标签的孤立 0

根因

src/pages/shop/product-detail.tsx 中赚取积分行的条件渲染写成:

{product.gainIntegral && Number(product.gainIntegral) > 0 && (...)}

当后端返回 gainIntegral 为数字 0JSX 短路与运算直接返回 0React 会把它渲染成一个纯文本节点。这就是页面上那个孤立的 0

修改

  • src/pages/shop/product-detail.tsx
    • 去掉条件里的 product.gainIntegral &&,改成 {Number(product.gainIntegral) > 0 && (...)},避免 0 泄漏。
    • 顺手把配送区域 goodsWeight 的同类写法也改成 {Number(product.goodsWeight) > 0 && (...)},防止重量为 0 时同样泄漏。
    • 另按用户要求「是 0 就不显示」,把销量行也改为 {Number(product.sales) > 0 && (...)},销量为 0 时整条「销量: 0」不渲染。

订单列表页下单后不自动刷新修复2026-07-14

问题

pages/order/list 下单后从 checkout 页 Taro.switchTab 跳过来,订单列表不刷新,看不到新订单。

根因

订单列表页是 tabBar 页,switchTab 不会重新挂载组件。useEffect 依赖 [tabIndex, isLoggedIn, user?.userId],这些值不变就不会重新触发;且 effect 内有 if (!tabStates[tabIndex].initialized) 守卫,页面已初始化过就直接跳过。

修改

  • src/pages/order/list.tsx:新增 Taro.useDidShow 生命周期钩子
    • isFirstShow ref 跳过首次显示(由 useEffect 处理初始加载)
    • 后续每次显示时:标记所有 tab 为 initialized: false(切 tab 时会重新加载),并立即 loadOrders(tabIndex, 1) 刷新当前 tab
    • 与之前购物车页 useDidShow 刷新是同一模式

商品详情页底部购物车角标2026-07-14

需求

pages/shop/product-detail 底部操作栏的购物车入口需要显示当前购物车商品数量角标。

修改

  • src/pages/shop/product-detail.tsx
    1. useCartContext 额外解构 totalCountrefresh
    2. 引入 useDidShow,页面显示时(已登录)调 refresh() 拉取最新购物车,保证角标准确
    3. 购物车图标外包一层 relative 容器,totalCount > 0 时右上角显示红色圆角角标(>99 显示 99+),样式与商城列表页浮动购物车角标一致

在线客服功能修复2026-07-14

问题

pages/user/customer-service/index 在线客服功能使用不了,用户已在微信后台添加客服人员。

根因(多个问题叠加)

  1. openType="contact" 按钮缺少关键属性:缺少 sessionFrom(会话来源标识)、onContact(客服回调)、showMessageCard/sendMessageTitle(客服消息卡片),且按钮被 Tailwind border-0 类可能干扰原生渲染
  2. 开发者工具限制open-type="contact" 在微信开发者工具中不生效,只能在真机上使用,页面缺少提示
  3. 自定义聊天系统消息发送缺失 conversationIdaddShopChatMessage 调用未传 conversationId,消息无法关联到会话;ShopChatMessage 模型本身也缺少该字段
  4. 消息发送缺失 formUserId:发送人 ID 未设置
  5. conv.lastMessage 字段不存在ShopChatConversation 模型只有 content 字段,代码引用了不存在的 lastMessage
  6. useReachBottom 用错场景:页面使用 ScrollView 组件,useReachBottom 是页面级滚动钩子,对 ScrollView 无效,应改用 onScrollToLower
  7. scrollToBottom ref 设了但从未使用:死代码

修改

  1. src/api/shop/shopChatMessage/model/index.ts
    • ShopChatMessage 接口新增 conversationId?: number
    • ShopChatMessageParam 接口新增 conversationId?: number
  2. src/pages/user/customer-service/index.tsx(全面修复):
    • 微信客服按钮补全 sessionFrom="customer_service"onContactshowMessageCardsendMessageTitle
    • 按钮样式从 Tailwind 类改为 inline style避免 border-0 干扰原生按钮),显式设置 border: 'none'
    • 添加"需真机预览测试"提示文字
    • addShopChatMessage 调用补全 conversationIdformUserId(从 Taro.getStorageSync('UserId') 获取)
    • conv.lastMessage 全部改为 conv.content
    • 会话列表更新 lastMessage: text 改为 content: text
    • 移除 useReachBottom,改用 ScrollView onScrollToLower + lowerThreshold={50}
    • 移除未使用的 scrollToBottom ref
    • pageShopChatMessage 参数去掉 as any 断言(模型已支持 conversationId
    • 新增 getCurrentUserId() 辅助函数
    • handleScrollToLower 增加 showHistory 条件守卫(仅展开历史时才加载更多)
  3. src/components/NavBar/index.tsx(关键修复,导致白屏):
    • Taro 4.1.11 没有 useNavigate hook原代码 import { useNavigate } from '@tarojs/taro' 运行时为 undefined
    • 改为 import Taro from '@tarojs/taro',回退逻辑用 Taro.navigateBack({ delta: 1 })
  4. 删除未注册的死代码
    • src/pages/customer-service.tsx(重复且仍引用 useReachBottom,干扰编译/热更新)
    • src/pages/customer-service.config.ts

验证

  • 全局搜索确认无其他 useNavigateuseReachBottom 引用
  • TypeScript 检查修改文件无错误

客服功能简化21:00

  • 用户反馈客服功能太复杂用不了,改为直接用微信原生 open-type="contact" 按钮
  • src/pages/shop/product-detail.tsx:客服图标从 View + onClick navigateTo 改为 Button openType="contact" sessionFrom="product_detail"
  • 删除了 handleContactService 函数和跳转到 /pages/user/customer-service/index 的逻辑
  • 注意:open-type="contact" 必须真机预览测试,开发者工具中点击无反应
  • pages/user/customer-service/index 页面保留但不再从商品详情页跳转

地址编辑页识别按钮点不中修复2026-07-14

问题

pages/user/address-edit 智能识别区的「识别」按钮点击无反应。

根因

按钮使用 absolute right-2 bottom-2 定位叠在 Textarea 上方。微信小程序中 Textarea 是原生组件,层级高于普通 View,会遮挡 absolute 叠加的按钮,z-index 也无效,导致按钮点击事件被 Textarea 拦截。

修改

  • src/pages/user/address-edit.tsx:识别区布局从 relative + absolute 叠加改为 flex 纵向布局——Textarea 在上,识别按钮 flex justify-end mt-2 在下方右对齐。去掉 relative class按钮不再叠加在 Textarea 上。