AGENTS.md 9.55 KB

AGENTS.md

本文件为 Codex 在 lls_program 仓库中的协作说明。目标不是介绍 Taro 通用知识,而是帮助后续修改尽量贴合这个项目当前的真实实现。

项目概述

lls_program 是一个基于 Taro 4 + Vue 3 + NutUI 的微信小程序,当前核心业务包括:

  • 家庭创建、加入、成员资料维护
  • 活动页、广告页、海报打卡、扫码打卡
  • 积分、奖励、优惠券相关页面
  • 基于微信授权和 sessionid 的登录态流转

最近一轮实现里,扫码打卡链路已经接入真实接口,并补上了注册来源归因、回跳续扫、地理围栏判断等逻辑。后续改动请优先遵守这些既有约定,不要在页面里重新长出一套平行逻辑。

技术栈

  • 框架:Taro 4.1.7
  • UI:Vue 3.3,统一使用 <script setup>
  • UI 组件:NutUI Taro 4.3.13
  • 样式:TailwindCSS 3.4 + Less
  • 状态管理:Pinia 3.0 + taro-plugin-pinia
  • 请求库:axios-miniprogram
  • 构建:Webpack 5
  • 测试:Vitest

常用命令

项目当前 package.json 里是标准 npm scripts,使用 npmpnpm 都可以,但文档和命令示例优先按仓库现状写 npm

# 安装依赖
npm install

# 微信小程序开发
npm run dev:weapp

# 微信小程序打包
npm run build:weapp

# H5 / 支付宝 / 抖音
npm run dev:h5
npm run dev:alipay
npm run dev:tt

# 代码质量
npm run lint
npm run format

# 测试
npm run test
npm run test:run
npm run test:coverage

目录结构

src/
├── api/              # 只放接口调用
├── assets/           # 静态资源
├── components/       # 通用组件
├── pages/            # 页面
├── stores/           # Pinia 状态
├── utils/            # 纯工具逻辑、流程辅助、请求封装
├── app.config.js     # 页面注册、权限声明
└── app.less          # 全局样式

当前和扫码打卡强相关的目录:

  • src/api/map.js
  • src/pages/ScanCheckinList/
  • src/pages/ScanCheckinDetail/
  • src/pages/Welcome/
  • src/pages/AddProfile/
  • src/pages/CreateFamily/
  • src/pages/JoinFamily/
  • src/utils/checkinLocation.js
  • src/utils/scanCheckin.js
  • src/utils/returnUrl.js
  • src/utils/userProfile.js
  • src/components/RichTextRenderer.vue

路径别名

项目已经配置好以下别名,新增代码优先使用别名,不要堆相对路径:

@/utils
@/components
@/assets
@/api
@/stores
@/composables
@/hooks

API 与请求约定

响应判断

所有接口统一按下面结构处理:

{
  code: 1,
  data: {},
  msg: '...'
}

必须显式判断 res.code === 1,不要写成 if (res.code)

sessionid 机制

项目通过 sessionid 做服务端认证:

  1. src/utils/request.js 每次请求前动态从 storage 读取 sessionid
  2. 请求头通过 config.headers.cookie = sessionid 透传
  3. 401 由现有登录/静默授权流程接管

重要约定:

  • 前端不要把 sessionid 当成“能否继续业务流程”的唯一判断条件
  • 是否能继续业务流程,应看接口结果和页面自己的业务条件

API 文件职责边界

src/api/*.js 只放接口定义和请求函数,不要把参数拼装、流程判断、字段归一化 helper 塞进去。

正确做法:

  • API 请求函数放 src/api/
  • payload 构建、字段解析、return_url 处理、地理围栏计算等 helper 放 src/utils/

当前已经落地的例子:

  • src/utils/userProfile.js
    • buildUpdateUserProfilePayload
    • isUserProfileComplete
  • src/utils/scanCheckin.js
    • 负责从二维码内容里解析 activity_id / detail_id
  • src/utils/returnUrl.js
    • 负责 return_url 解码和拼接
  • src/utils/checkinLocation.js
    • 负责地理围栏判断

当前扫码打卡链路

这是现在最需要保持稳定的一条业务链路,后续改动请先看清楚,不要只盯某一个页面。

页面与接口

  • 列表页:src/pages/ScanCheckinList/index.vue
    • 使用 getScanStageListAPI
  • 详情页:src/pages/ScanCheckinDetail/index.vue
    • 使用 getScanStageDetailAPI
    • 使用 submitScanCheckinAPI
  • 接口定义集中在 src/api/map.js

当前流程

  1. 用户从活动或二维码入口进入扫码打卡相关页面
  2. 扫码链接会带 activityId,详情页自身还可能带 reg_sourcereg_stage_id
  3. ScanCheckinDetail 点击“扫码打卡”时,先按现有路线检查“是否已有家庭”
  4. 没有家庭时,不直接扫码,先弹提示,再跳 Welcome
  5. Welcome 再决定是否先补资料、再创建家庭或加入家庭
  6. 完成资料和家庭链路后,通过 return_url 回到原扫码详情页
  7. 用户再次点击“扫码打卡”时:
    • 重新静默获取当前位置
    • 如果该关卡开启地理围栏,则先判断是否在范围内
    • 调起微信扫码
    • 从二维码结果里解析真实的 activity_id / detail_id
    • 调用打卡接口
  8. 打卡成功后跳转到 ScanCheckinList

关键业务约束

  • 打卡提交参数来自二维码内容,不来自详情页当前路由参数
  • 详情页路由参数主要用于展示、回跳和列表跳转
  • 注册来源归因字段当前只保留:
    • reg_source
    • reg_stage_id
  • reg_activity_id 已经不再需要,不要再传
  • “是否补全资料”的判断交给 Welcome 链路,不要在 ScanCheckinDetail 再复制一套资料完整性分支

return_url 回跳约定

扫码打卡目前依赖多页串联回跳,任何一个页面处理不对,都会出现跳错页、路径双重编码、甚至 redirectTo 找不到页面。

当前约定:

  • 统一使用 src/utils/returnUrl.js
  • 页面收到 options.return_url 后,先走 normalizeReturnUrl
  • 页面拼接下一个页面地址时,优先走 appendReturnUrlParam
  • 不要在页面里到处手写 decodeURIComponent(options.return_url || '')

当前受这个约束影响较大的页面:

  • src/pages/Welcome/index.vue
  • src/pages/AddProfile/index.vue
  • src/pages/CreateFamily/index.vue
  • src/pages/JoinFamily/index.vue

注册资料与来源归因

buildUpdateUserProfilePayload 目前已经抽到 src/utils/userProfile.js,专门负责把页面表单转成接口 payload。

当前约定:

  • user.js 里只保留接口调用
  • buildUpdateUserProfilePayload 不回迁到 API 文件
  • 注册来源字段在补资料接口里继续透传:
    • reg_source
    • reg_stage_id
  • reg_stage_id 需要按数值类型传给后端

地理围栏约定

扫码详情页如果 geo_enabled === true,必须做范围判断。

当前实现约定:

  • 判断逻辑统一放 src/utils/checkinLocation.js
  • 页面层只负责调用,不要在页面里自己算经纬度距离
  • 点击“扫码打卡”时重新调用 Taro.getLocation(),不依赖旧缓存
  • 关卡详情接口字段:
    • geo_enabled
    • center_lng
    • center_lat
    • radius_meters

富文本与轮播图约定

扫码详情页当前已经不是单图和原生富文本直出:

  • 顶部 banner 使用 NutUI nut-swiper
  • 富文本显示统一走 src/components/RichTextRenderer.vue

后续如果再改扫码详情页:

  • 不要再改回单张 <image>
  • 不要再临时拼一个简化版富文本组件替代现有 RichTextRenderer

页面开发约定

生命周期

页面优先使用 Taro 生命周期:

import { useLoad, useShow, useDidShow, useReady } from '@tarojs/taro'

尽量避免把页面主流程写到 Vue 的 onMounted / onUnmounted 里。

页面结构

每个页面目录通常包含:

  • index.vue
  • index.config.js
  • index.less

新增页面后必须同步注册到 src/app.config.js

小程序 API 约束

不要在页面或工具函数里使用浏览器 API,例如:

  • window
  • document
  • localStorage
  • 原生 fetch

统一使用 Taro API,例如:

  • Taro.navigateTo
  • Taro.redirectTo
  • Taro.switchTab
  • Taro.getStorage
  • Taro.scanCode
  • Taro.getLocation

样式约定

  • NutUI 组件自动导入,不要手动 import
  • 页面样式继续保持 Tailwind + Less 混合方式
  • Less 样式默认 scoped
  • 深层覆盖 NutUI 时使用 :deep(...)

这个仓库已经有一个和扫码页相关的稳定样式经验:

  • ScanCheckinDetail 底部按钮采用固定定位和安全区留白
  • 如果只是调整按钮视觉位置,优先做 CSS 修改,不要顺手改业务逻辑

权限与配置

src/app.config.js 当前已经声明了位置权限:

  • requiredPrivateInfos: ['getLocation']
  • permission['scope.userLocation']

所以如果你在扫码打卡里继续用定位能力,优先复用现有授权前提,不要再设计一套重复授权流程。

修改建议

适合抽到 utils 的逻辑

  • 二维码参数解析
  • 距离计算和范围判断
  • return_url 编解码
  • 用户资料 payload 构建
  • 纯字段映射或判空逻辑

不适合放进 API 文件的逻辑

  • 表单转 payload
  • 页面跳转流程判断
  • 二维码内容解析
  • 路由参数兼容处理
  • 页面专用字段映射

做改动前先检查

  • 这是页面职责,还是工具职责
  • 这是接口请求定义,还是接口参数拼装
  • 这是详情页展示参数,还是打卡提交真实参数
  • 这是当前页下一跳,还是完整链路里的回跳页面

交付标准

在这个仓库里完成修改时,优先做到:

  • 与当前真实业务链路一致
  • 不在页面里复制已有 helper
  • 不在 API 文件里塞 helper
  • 不破坏 return_url 回跳
  • 不破坏扫码打卡的地理围栏与注册来源归因
  • 不把 Taro 小程序页面写成浏览器页面