Files
xinlong-shop-taro/.workbuddy/memory/MEMORY.md
赵忠林 f1c0349f64 feat(api): 新增 cms-api API 域名支持并切换幻灯片广告接口域名
- 配置文件 env.js 中新增 CMS_API_URL,三个环境均统一配置
- app.ts 导出 CmsBaseUrl,用于 cms-api 相关请求
- cmsAd 模块接口调用统一改为通过 CmsBaseUrl 拼接完整 cms-api 路径
- 幻灯片广告相关接口走独立 cms-api 域名,保证数据隔离
- 更新工作备忘录,补充 API 域名配置与幻灯片广告调用说明
2026-07-27 12:42:27 +08:00

12 KiB
Raw Blame History

项目长期记忆

项目概述

  • 项目名xinlong-shop-taroTaro 微信小程序商城)
  • 技术栈Taro + React + TypeScript + Tailwind CSS
  • 后端 API 前缀:/shop//api/
  • 后端项目路径:/Users/gxwebsoft/JAVA/guilixu-java⚠️ 不要找 paopao-java 或 websopy-java那是旧的
  • 后端 Maven 编译命令cd /Users/gxwebsoft/JAVA/guilixu-java && "/Applications/IntelliJ IDEA Ultimate.app/Contents/plugins/maven/lib/maven3/bin/mvn" compileguilixu 的 mvnw 损坏,用 IntelliJ 自带的 mvn
  • API 域名配置config/env.js 三环境一致):API_BASE_URL=https://shop-api.websoft.top/apiSERVER_API_URL=https://server.websoft.top/apiCMS_API_URL=https://cms-api.websoft.top/apiconfig/app.ts 导出 BaseUrl/ServerBaseUrl/CmsBaseUrl
  • 幻灯片广告(轮播图)调用已切到 cms-apisrc/api/cms/cmsAd/index.ts 的读接口(listCmsAd/getCmsAd/getCmsAdByCode/pageCmsAd)通过 cmsUrl() 拼接 CmsBaseUrl,首页轮播 listCmsAd({ adType: 'banner', status: 0 })https://cms-api.websoft.top/api/cms/cms-ad;写接口仍走 shop-api
  • 门店店员身份判断:通过 getMyClerk() API (/shop/shop-store-user/my) 返回值判断
  • VIP/分销商申请记录表:ShopDealerApply,状态码 10待审核/20通过/30驳回

用户状态与禁用机制

  • 用户状态字段:User.status0 = 正常,1 = 禁用
  • 禁用接口:updateUserStatus(userId, status) → PUT /system/user/status
  • 禁用拦截工具:src/utils/auth.tsisUserDisabled(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 hooksrc/hooks/useVipStatus.ts),组件挂载时异步校验并更新缓存 + React stateisVip 变化触发重渲染
  • 已接入 useVipStatus 的页面/组件商品详情、结算、商城首页、分类页、购物车页、CartContext、SkuSelector、ProductCard
  • OrderGoodsItem 接口新增 price 字段VIP 下单时传 dealerPrice 给后端

商品价格字段约定

  • product.price - 到手价(主价格)
  • product.salePrice - 市场价(划掉的)
  • product.dealerPrice - VIP 会员专享价
  • product.memberStorePrice - 会员价

关键文件位置

  • VIP 工具:src/utils/vip.ts
  • VIP Hooksrc/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
  • 购物车 APIsrc/api/shop/shopCart/index.ts(批量删除接口 /shop/shop-cart/batch 后端期望直接接收 ArrayList<Integer>[1,2,3],不要用 { ids } 包裹)
  • 购物车 Contextsrc/contexts/CartContext.tsx
  • 购物车合并加购:后端 ShopCartServiceImpl.addToCart() 按 user_id+goods_id+sku_id 判重,已存在则累加 cart_num不存在才新增skuId 为 null/0单规格匹配 IS NULL 或 =0。ShopCart.userId 是 Integer非 Long。历史重复数据清理脚本 sql/shop_cart_merge_duplicates.sql

订阅消息

  • 模板 IDsh1K9iK7vZjebUNFu6OsMsnsJxm4whThWGrhN7I4zVg(交易提醒)
  • 字段:订单号(character_string1)、商品名称(thing4)、联系人(thing10)、联系电话(phone_number7)、送货地址(thing8)
  • 场景说明:下单提醒
  • 新订单检测 Hooksrc/hooks/useNewOrderDetector.ts30秒轮询页面隐藏暂停

微信隐私协议(基础库 3.16.1+ 强制)

  • 报错特征:chooseImage:fail api scope is not declared in the privacy agreement / getPhoneNumber:fail ... privacy ... + errno:112
  • 代码修复:
    1. 调用 Taro.chooseImage / chooseMedia / getPhoneNumber 等敏感 API 前,先 getPrivacySettingrequirePrivacyAuthorize 预检(封装在 src/api/system/file/index.tsensurePrivacyAuthorized() 里,已 export注意:不应在登录页 useDidShow 中主动预检,避免用户一进入登录页就弹框;应由微信在用户点击 getPhoneNumber 时自行触发 onNeedPrivacyAuthorization
    2. src/app.tsxuseLaunch 中注册 Taro.onNeedPrivacyAuthorization 回调,并通过 src/components/PrivacyModal 展示隐私协议授权弹窗。弹窗内的 Button 必须设置 open-type="agreePrivacyAuthorization",用户点击后触发 onAgreePrivacyAuthorizationresolve({ event: 'agree', button: 'agree' })Taro.showModal 的按钮无法被微信识别为有效的隐私授权
    3. 门店上传图片页面在调用 chooseImage 前主动预检:src/pages/store/orders/index.tsxchooseProofImage 已加 ensurePrivacyAuthorized()src/pages/store/goods/index.tsx 通过 uploadFile() 上传,uploadFile() 内部已预检。
  • 手动配置:小程序管理后台 → 设置 → 第三方设置 → 用户隐私保护指引 → 添加「开发者收集你的相册/摄像头」和「手机号」声明
  • ⚠️ requiredPrivateInfos 字段只接受位置类 API 白名单chooseAddress/chooseLocation/choosePoi/getFuzzyLocation/getLocation/onLocationChange/startLocationUpdate/startLocationUpdateBackground不要加 chooseImage/chooseMedia/getPhoneNumber 等非位置类,会导致 app.json 解析失败
  • 直接调 Taro.chooseImage 的页面:pages/order/evaluate/index.tsxpages/after-sale/apply/index.tsxpages/store/orders/index.tsx —— 这些页面绕过了 uploadFile,需要单独加 ensurePrivacyAuthorized 预检
  • 直接调 getPhoneNumber 的页面:passport/login.tsxpassport/register.tsx —— 不再主动预检,依赖微信点击按钮时自动触发 onNeedPrivacyAuthorization;保留降级短信登录
  • 隐私授权弹窗组件:src/components/PrivacyModal/index.tsx(全局单例,通过 src/utils/privacy.ts 管理显隐与 resolve

手机号授权登录降级方案2026-07-15 ~ 07-16 修复)

  • 业务现实getPhoneNumber 按钮被用户拒绝后,微信会短期"记住"用户的选择,再次点击会直接 failerrMsg: getPhoneNumber:fail user deny)。一旦进入这个状态,前端必须提供降级入口
  • 统一模式passport/login.tsxpassport/register.tsx 都有 getPhoneNumber 回调,不允许只 toast 一句"未授权手机号"就完事
    • 必做:失败时调 showPhoneAuthFailedModal(errMsg),按 errMsg 区分场景弹窗user deny / privacy / no permission / 通用)
    • 弹窗带"短信登录"按钮自动跳 /passport/sms-login(透传 redirect + 邀请参数 inviter/source/t
    • 必做:主登录按钮长期可见地提供"使用短信验证码登录"链接,不要依赖弹窗
  • 接口地址统一login/register 的手机号登录接口统一使用 SERVER_API_URL + '/wx-login/loginByMpWxPhone'register 之前误写 https://shop-api.websoft.top
  • 短信登录页/passport/sms-login(路由表里已有),调 loginBySms + sendSmsCaptcha 接口
  • 已落地页面passport/login.tsxpassport/register.tsx(隐私协议预检 + 统一 request + 降级弹窗)

分享 / 朋友圈能力2026-07-13 接入)

  • 统一封装:src/hooks/useShare.tsuseShare({title, path?, query?, imageUrl?, enableTimeline?, enableCopyUrl?}),一次注册 useShareAppMessage + useShareTimeline + 可选 onCopyUrl 复制链接;自动在 path/query 追加 inviter=${当前用户id} 做裂变归因;isTimelineSinglePage() 判断朋友圈单页模式 scene===1154
  • 海报组件:src/components/SharePoster/index.tsxCanvas 2D 绘制封面+标题+价格+小程序码,返回临时图路径作 imageUrl
  • 小程序码接口:src/api/share.ts generateShareCode(page, inviterId) 复用 generateMiniProgramCodescene 用 uid_${inviterId}page 透传但后端需读取转发才生效)
  • 裂变约定:分享链接 query 用 inviter,与 src/utils/invite.tsparseInviteParams 读取字段一致
  • 已接入页面:商品详情、积分商品、拼团、秒杀、活动、赛事(带 canvas 海报邀请下级、分销推广、升级VIP、分享赚佣金仅 enableShare + useShare无海报首页好友+朋友圈+复制链接三按钮全亮)
  • 注意tabBar 页也支持朋友圈分享(之前误判已纠正)——只要 config 开 enableShareTimeline + 定义 onShareTimeline 即显示按钮;"tabBar 不渲染"仅指朋友圈打开后的单页模式里底部 tabBar 不显示。朋友圈/复制链接开发者工具可能看不到入口,必须真机测试。复制链接是 Beta基础库 2.14.3+)。

订单备注字段约定2026-07-15

  • buyerRemarks — 买家备注用户下单时填写的订单备注checkout 页提交)
  • merchantRemarks — 商户备注:门店修改金额时填写的原因记录
  • comments — 系统/其他备注:保留给系统内部使用
  • ShopOrderOrderCreateRequest 接口均已包含 buyerRemarksmerchantRemarks 字段

用户浏览记录功能2026-07-15

  • 存储模式:去重累加(同一用户+商品只留一条visit_count 累加5分钟内去重
  • 数据库表:shop_goods_browse字段id/user_id/goods_id/tenant_id/merchant_id/visit_count/last_visit_time/browse_source/create_time/update_time
  • 唯一索引:uk_user_goods (user_id, goods_id);查询索引:idx_user_timeidx_goods_time
  • 后端 API 路径:/api/shop/shop-goods-browsePOST 上报 / GET page 我的足迹 / DELETE {id} 删单条 / DELETE 清空 / GET admin/page 后台查询)
  • 前端 APIsrc/api/shop/shopGoodsBrowse/index.tsreportBrowse 静默上报 / pageBrowseHistory / deleteBrowseRecord / clearBrowseHistory
  • 商品详情页 product-detail.tsxaddToHistory() 已接入:已登录时异步调 reportBrowse未登录仅本地 browse_history
  • 浏览历史页 src/pages/user/history-list/index.tsx 已改为服务端分页(登录),未登录回退本地存储
  • 后端文件:ShopGoodsBrowse.java(entity) / ShopGoodsBrowseParam.java / ShopGoodsBrowseMapper.java+XML / ShopGoodsBrowseService.java+Impl / ShopGoodsBrowseController.java
  • 建表 SQL/Users/gxwebsoft/JAVA/guilixu-java/sql/shop_goods_browse.sql
  • 后台管理前端:/Users/gxwebsoft/VUE/guilixu-adminVue3 + Ant Design Vue + Vite非 RuoYi
  • 注意:BaseController.success()success(IPage<T>) 重载,泛型方法返回类型用 ApiResult<?> 而非 ApiResult<PageResult<T>>(否则 fail() 分支类型不兼容)

tabBar 配置2026-07-15

  • 当前 4 个 tabBar/pages/index/index(首页)、/pages/shop/index(分类)、/pages/shop/cart(购物车)、/pages/user/user(我的)
  • 「订单」(pages/order/list) 已从 tabBar 移除,改为普通页,所有进入订单列表的地方用 navigateTo 而非 switchTab
  • 多处 tabBar 判断列表login/register/sms-login/auth-flow/user/index/index/index已统一为实际 4 个 tabBar 路径
  • ⚠️ pages/user/index.tsx:140 仍用 switchTab(points/index),积分页非 tabBar待修