基于微信小程序的兔子救助、领养、捐换及社区活动平台
兔兔救助小程序是一个专为兔子救助公益组织设计的微信小程序平台,提供:
- 救助发布与审核:志愿者发布流浪/受伤兔子信息,管理员审核后对外展示
- 领养申请:用户可申请领养已救助兔子,管理员处理申请
- 爱兔公益:物资捐换帖子、义卖商店(浏览/联系购买)、寄养家庭招募
- 公益活动:线下聚会报名、云养兔(公益积分支持)
- 聊天:用户与管理员一对一私信
- 消息通知:审核结果、状态变更站内消息推送
救援列表 |
爱兔领养 |
爱兔公益·捐换 |
活动·线下活动 |
救援详情 |
救援地图 |
地图城市统计 |
发布救援 |
兔兔故事 |
领养流程 |
领养礼包 |
领养申请表 |
我的(管理员) |
管理员后台 |
救援管理操作 |
领养申请管理 |
兔兔档案管理 |
用户管理 |
关于爱兔会 |
rabbit_0602_debug/
├── Rabbit_mp/ # 前端 - UniApp(Vue 3)微信小程序
│ ├── pages/ # 主 Tab 页面
│ │ ├── rescue/list # 救援列表
│ │ ├── adoption/index # 领养
│ │ ├── donation/index # 爱兔公益(捐换/义卖/寄养入口)
│ │ ├── activity/index # 活动
│ │ ├── profile/index # 我的
│ │ └── auth/login # 登录
│ ├── subpackages/ # 分包
│ │ ├── rescue/ # 详情、创建、编辑、筛选
│ │ ├── adoption/ # 领养申请、兔子详情
│ │ ├── donation/ # 捐换列表、详情、创建
│ │ ├── charity/ # 义卖商店(商品列表/详情)、寄养(招募/申请)
│ │ ├── activity/ # 云养兔、线下活动详情/创建/编辑
│ │ ├── profile/ # 消息中心、我的帖子、我的领养/寄养申请、设置
│ │ ├── admin/ # 管理后台(待审核/审核历史/领养/寄养/兔子管理/义卖/用户管理)
│ │ ├── chat/ # 聊天页面
│ │ └── about/ # 关于组织
│ ├── components/ # 公共组件(图片上传、状态徽章、双列瀑布流、骨架屏等)
│ ├── services/ # API 请求封装(rescue/adoption/donation/charity/foster/chat/admin 等)
│ ├── stores/ # Pinia 状态管理(app-data/rescue/community/messages)
│ ├── config/ # 环境配置(API 地址、云环境 ID、模板 ID)
│ ├── static/ # 静态资源(TabBar 图标)
│ ├── pages.json # 小程序路由与 TabBar 配置
│ ├── vite.config.ts # Vite 构建配置
│ └── package.json
│
├── rabbit_mp_api/ # 后端 - FastAPI(Python)
│ ├── app/
│ │ ├── main.py # FastAPI 入口,挂载所有路由
│ │ ├── models.py # SQLAlchemy ORM 数据模型
│ │ ├── schemas.py # Pydantic 请求/响应模型
│ │ ├── config.py # 从 .env 读取配置
│ │ ├── database.py # 数据库连接
│ │ ├── auth.py # JWT 认证逻辑
│ │ ├── wechat.py # 微信 API 集成(登录)
│ │ ├── deps.py # 依赖注入(get_db / get_current_user)
│ │ ├── routers/ # 路由处理器
│ │ │ ├── auth.py # 微信登录、首次设置用户名
│ │ │ ├── rescue.py # 救援贴 CRUD、状态管理、志愿者
│ │ │ ├── adoption.py # 领养申请
│ │ │ ├── donation.py # 捐换帖
│ │ │ ├── activity.py # 活动(线下/云养兔)
│ │ │ ├── community.py # 社区帖子与点赞
│ │ │ ├── messages.py # 用户消息通知
│ │ │ ├── profile.py # 用户资料与云币
│ │ │ ├── admin.py # 管理员专属接口
│ │ │ ├── upload.py # 图片上传
│ │ │ ├── subscribe.py # 订阅消息授权
│ │ │ ├── charity_sale.py # 义卖商品 CRUD
│ │ │ ├── foster.py # 寄养申请
│ │ │ ├── rabbits.py # 兔子档案管理
│ │ │ └── chat.py # 聊天(会话/消息)
│ │ └── services/
│ │ └── rescue_state_machine.py # 救援状态机(状态转换校验)
│ ├── alembic/ # 数据库迁移脚本
│ ├── tests/ # pytest 测试套件
│ ├── static/ # 后端静态文件(图片服务)
│ │ └── images/ # 组织 Logo 等图片
│ ├── requirements.txt # Python 依赖
│ └── .env.example # 环境变量模板
│
├── specs/ # 功能规格说明文档
├── docs/ # 其他文档
└── README.md
| 工具 | 版本要求 |
|---|---|
| Python | 3.11+ |
| Node.js | 18+ |
| 微信开发者工具 | 最新稳定版 |
| 微信小程序账号 | 需配置 AppID |
cd rabbit_mp_api
# 1. 创建并激活虚拟环境
python3 -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
# 2. 安装依赖
pip install -r requirements.txt
# 3. 配置环境变量
cp .env.example .env编辑 .env 文件,填入以下配置:
# 微信小程序凭证(在微信公众平台获取)
WECHAT_APPID=your_wechat_appid
WECHAT_SECRET=your_wechat_secret
# JWT 密钥(随机长字符串)
JWT_SECRET=your_very_long_random_jwt_secret_here
JWT_EXPIRE_DAYS=30
# 数据库(开发用 SQLite,生产可换 PostgreSQL)
DATABASE_URL=sqlite:///./rabbit_mp_dev.db
# 超级管理员微信 openid(支持 JSON 数组或逗号分隔,登录时自动提权)
ADMIN_OPENIDS=["openidA","openidB"]
# 微信云环境 ID(可选,用于云存储)
CLOUD_ENV_ID=your_cloud_env_id# 4. 执行数据库迁移
alembic upgrade headcd Rabbit_mp
# 1. 安装依赖
npm install编辑 config/env.ts,配置 API 地址:
// 后端 API 地址
export const API_BASE_URL = 'https://your-domain.com'
// 微信云环境 ID
export const CLOUD_ENV_ID = 'your-cloud-env-id'
// 微信订阅消息模板 ID(在微信公众平台申请)
export const RESCUE_REVIEW_TEMPLATE_ID = 'your-template-id'
export const ADOPTION_STATUS_TEMPLATE_ID = 'your-template-id'cd rabbit_mp_api
source .venv/bin/activate
# 开发模式(热重载)
uvicorn app.main:app --reload --host 0.0.0.0 --port 8000
# 访问 API 文档
# http://localhost:8000/docscd Rabbit_mp
# 编译并监听变化(输出至 dist/dev/mp-weixin/)
npm run dev:mp-weixin然后打开微信开发者工具,导入 Rabbit_mp/dist/dev/mp-weixin/ 目录。
# 前端生产打包
cd Rabbit_mp
npm run build:mp-weixin
# 产物在 dist/build/mp-weixin/,上传至微信公众平台
# 后端生产部署(示例)
uvicorn app.main:app --host 0.0.0.0 --port 8000 --workers 4cd rabbit_mp_api
pytest tests/ -v| 功能 | 操作路径 | 说明 |
|---|---|---|
| 微信登录 | 首次进入 → 授权登录 | 使用微信账号一键登录,设置用户名 |
| 浏览救援贴 | 救援 Tab | 查看待救援兔子列表,支持城市/状态筛选 |
| 发布救援贴 | 救援 → 右上角「+」 | 填写地点、描述、上传照片,提交审核 |
| 参与救援 | 救援详情 → 我要参与救援 | 报名成为志愿者,管理员可查看名单 |
| 查看救援详情 | 点击任意救援卡片 | 查看详情、救援进度时间线 |
| 申请领养 | 领养 Tab → 点击兔兔 → 申请领养 | 填写申请信息,等待管理员审核 |
| 我的领养申请 | 我的 → 我的领养申请 | 查看申请状态与管理员回复 |
| 浏览捐换 | 爱兔公益 Tab → 捐换 | 查看物资捐赠/置换信息 |
| 发布捐换 | 爱兔公益 → 捐换 → 「+」 | 填写物资描述、联系方式 |
| 义卖商店 | 爱兔公益 Tab → 义卖 | 浏览义卖商品,联系管理员购买 |
| 申请寄养 | 爱兔公益 Tab → 寄养 | 了解寄养须知并提交申请 |
| 我的寄养申请 | 我的 → 我的寄养申请 | 查看申请状态与审核备注 |
| 参与活动 | 活动 Tab | 报名线下活动 / 云养兔捐积分 |
| 聊天 | 消息中心 → 聊天 / 各详情页入口 | 与管理员一对一私信 |
| 我的帖子 | 我的 → 我的帖子 | 查看自己发布的救援/捐换帖状态 |
| 消息中心 | 我的 → 消息 | 查看审核结果、状态变更通知 |
| 云币余额 | 我的 → 个人资料 | 查看云币数量(参与活动获得/消费) |
救援贴状态流转(用户视角):
草稿/被拒 → 提交审核(待审核)→ 审核通过 → 救援中 → 寄养中 → 已领养
↘ 直接领养(线下)↗ ↓
↘ 已去世 ←──────────────┘
管理员账号由系统数据库直接设置
role = 'admin'
| 功能 | 操作路径 | 说明 |
|---|---|---|
| 进入管理后台 | 我的 → 管理员入口(仅管理员可见) | 显示各类待处理数量徽章 |
| 审核救援贴 | 管理后台 → 待审核救援贴 | 通过/拒绝(需填写拒绝原因) |
| 查看审核历史 | 管理后台 → 审核历史 | 已审核救援贴归档,含审核人与时间 |
| 强制变更状态 | 救援贴详情 → 管理操作 | 可强制推进救援状态,附带备注 |
| 处理领养申请 | 管理后台 → 领养申请 | 审核领养意向,添加备注,更新申请状态 |
| 处理寄养申请 | 管理后台 → 寄养申请 | 审核寄养申请,添加审核备注 |
| 管理兔子档案 | 管理后台 → 兔子列表 | 查看/编辑在册兔子信息 |
| 管理义卖商品 | 管理后台 → 义卖管理 | 新增/编辑/下架义卖商品 |
| 用户管理 | 管理后台 → 用户管理(超管专属) | 提升/撤销管理员、封禁/解封账号 |
| 创建活动 | 活动 Tab → 右上角「+」(管理员可见) | 创建线下活动或云养兔活动 |
| 删除内容 | 各帖子详情 → 删除(软删除,可恢复) | 违规内容下架处理 |
审核工作流:
用户提交 → [待审核] → 管理员审核
├── 通过 → [已发布] → 对外展示 → 状态机流转
└── 拒绝(含原因) → 通知用户 → 用户修改后可重新提交(最多 3 次)
graph TB
subgraph Client["微信客户端"]
MP["兔兔救助小程序\nUniApp / Vue3"]
end
subgraph CDN["微信平台"]
WX["微信服务器\n登录 / 订阅消息"]
end
subgraph Server["服务端"]
direction TB
API["FastAPI\n:8000"]
AUTH["JWT 认证\n中间件"]
SM["救援状态机\nrescue state machine"]
API --> AUTH
API --> SM
end
subgraph Storage["存储层"]
DB["SQLite / PostgreSQL\nSQLAlchemy ORM"]
FS["静态文件\n/static/images/"]
end
MP -->|"HTTPS REST API\nBearer Token"| API
MP -->|"wx.login code"| WX
WX -->|"openid + session_key"| API
API -->|"订阅消息推送"| WX
WX -->|"通知"| MP
API --> DB
API --> FS
MP -->|"图片直接访问"| FS
数据流说明:
- 用户调用
wx.login()获取临时 code - 小程序将 code 发送至后端
/v1/auth/mp-login - 后端向微信服务器换取
openid,生成 JWT 返回 - 后续所有请求携带
Authorization: Bearer <token> - 审核结果通过微信订阅消息异步推送给用户
graph LR
subgraph Pages["主页面 pages/"]
P1["rescue/list\n救援列表"]
P2["adoption/index\n领养"]
P3["donation/index\n爱兔公益"]
P4["activity/index\n活动"]
P5["profile/index\n我的"]
P6["auth/login\n登录"]
end
subgraph Sub["分包 subpackages/"]
S1["rescue/\n详情/创建/编辑/筛选"]
S2["adoption/\n领养申请/兔子详情"]
S3["donation/\n捐换列表/详情/创建"]
S4["charity/\n义卖列表/详情/寄养招募/申请"]
S5["activity/\n云养兔/线下活动详情/创建/编辑"]
S6["profile/\n消息/帖子/我的领养/我的寄养/设置"]
S7["admin/\n待审核/历史/领养/寄养/兔子/义卖/用户管理"]
S8["chat/\n聊天"]
S9["about/\n关于"]
end
subgraph Shared["共享层"]
C["components/\n公共组件"]
SVC["services/\nAPI 封装"]
ST["stores/ Pinia\n状态管理"]
CFG["config/env.ts\n环境配置"]
end
Pages --> Shared
Sub --> Shared
SVC -->|"HTTP"| API["后端 API"]
ST --> SVC
graph TB
subgraph Routers["路由层 routers/"]
R1["auth\n登录/注册"]
R2["rescue\n救援贴/志愿者"]
R3["adoption\n领养"]
R4["donation\n捐换"]
R5["activity\n活动"]
R6["community\n社区"]
R7["messages\n消息"]
R8["profile\n用户资料"]
R9["admin\n管理员"]
R10["upload\n图片上传"]
R11["charity sale\n义卖商品"]
R12["foster\n寄养申请"]
R13["rabbits\n兔子档案"]
R14["chat\n聊天"]
end
subgraph Core["核心层"]
AUTH["auth.py\nJWT 生成/验证"]
WX["wechat.py\n微信 API 集成"]
SM["rescue state machine\n状态转换校验"]
DEP["deps.py\n依赖注入"]
end
subgraph Data["数据层"]
MOD["models.py\nSQLAlchemy 模型"]
SCH["schemas.py\nPydantic 校验"]
DB["database.py\n数据库会话"]
MIG["alembic/\n迁移脚本"]
end
Routers --> Core
Routers --> Data
Core --> Data
R2 --> SM
R1 --> WX
erDiagram
User {
int id
string openid
string username
string role
int cloud_coins
bool is_banned
}
RescuePost {
int id
string review_status
string rescue_status
int publisher_id
datetime deleted_at
}
RescueStatusHistory {
int id
int rescue_post_id
string old_status
string new_status
int changed_by_id
}
RescueApplication {
int id
int rescue_post_id
int applicant_id
}
AdoptionIntent {
int id
int rescue_post_id
int applicant_id
string status
}
DonationPost {
int id
string donation_type
int publisher_id
bool is_completed
}
CharitySaleItem {
int id
string title
string status
int created_by_id
}
Activity {
int id
string activity_type
int organizer_id
}
Message {
int id
int recipient_id
string content
bool is_read
}
Rabbit {
int id
string name
string status
string gender
int rescue_post_id
}
FosterApplication {
int id
int applicant_id
string status
string review_note
}
Conversation {
int id
int user_id
int admin_id
}
ChatMessage {
int id
int conversation_id
int sender_id
string content
}
User ||--o{ RescuePost : "发布"
User ||--o{ RescueApplication : "申请参与"
User ||--o{ AdoptionIntent : "申请领养"
User ||--o{ FosterApplication : "申请寄养"
User ||--o{ DonationPost : "发布"
User ||--o{ Message : "接收"
User ||--o{ Conversation : "参与聊天"
RescuePost ||--o{ RescueStatusHistory : "状态历史"
RescuePost ||--o{ RescueApplication : "救援申请"
RescuePost ||--o{ AdoptionIntent : "领养申请"
RescuePost ||--o| Rabbit : "兔子档案"
Conversation ||--o{ ChatMessage : "包含消息"
本节面向负责审核与推进救援进度的管理员志愿者,介绍从收到一条救援帖到兔兔找到家的完整操作流程,无需技术背景即可阅读。
flowchart TD
A([发布救援帖]) --> B[待审核]
B --> C{管理员审核}
C -->|通过| D[公开展示]
C -->|驳回 需填原因| E[已驳回]
E -->|修改重提<br/>最多3次| B
D --> P1[待救援]
P1 --> P2[救援中]
P2 -->|救出 送寄养家庭| P3[寄养中]
P2 -->|线下直接领养<br/>需填领养人备注| P4[已领养]
P2 -->|不治离世| P5[已去世]
P3 -->|找到领养家庭| P4
P3 -->|不治离世| P5
P3 -.->|系统自动建档| R[(兔兔档案)]
R -.->|出现在领养页面| Adopt[用户提交领养申请]
Adopt -.->|管理员审核通过| P4
P4 -.->|状态同步| R
P5 -.->|状态同步| R
style P1 fill:#dc2626,color:#fff
style P2 fill:#ea580c,color:#fff
style P3 fill:#7c3aed,color:#fff
style P4 fill:#15803d,color:#fff
style P5 fill:#6b7280,color:#fff
style R fill:#fffbeb,stroke:#d97706,stroke-width:2px
图例:实线箭头为管理员主动操作;虚线箭头(
-..->)为系统自动触发。
每条救援帖有两条并行的状态轨道,互不干扰:
| 轨道 | 含义 | 由谁改变 |
|---|---|---|
| 内容审核状态 | 这条帖子是否对公众展示 | 管理员审核 |
| 救援进度状态 | 兔兔目前在哪个阶段 | 管理员推进 |
内容审核通过后,救援进度才有意义;但两者是分开操作的。
待救援 → 救援中 → 寄养中 → 已领养
↘ 直接领养(线下,跳过寄养)↗
任意阶段 → 已去世
| 状态 | 含义 | 兔兔档案 |
|---|---|---|
| 待救援 | 帖子刚审核通过,还没有志愿者出动 | 尚未创建 |
| 救援中 | 有人正在出发/处理中 | 尚未创建 |
| 寄养中 | 兔兔已被救出,在寄养家庭安置 | 自动创建 |
| 已领养 | 兔兔已找到永久家庭 | 自动同步 |
| 已去世 | 兔兔未能救活或病重离世 | 自动同步(用于存档) |
关键:当进度推进到「寄养中」时,系统会自动根据救援帖的信息创建一份兔兔档案,这只兔兔就会出现在「领养」页面供用户申请领养。
- 打开小程序 → 我的 → 管理员入口
- 点击「待审核救援贴」,数字角标显示待处理数量
- 进入每条帖子查看详情
- 选择操作:
- 通过:帖子对外展示,救援进度变为「待救援」
- 驳回:必须填写驳回原因(用户会收到通知,可修改后重新提交,最多 3 次)
审核通过后,在救援帖详情页的管理操作区(页面底部,仅管理员可见),点击「推进救援状态」:
待救援 →(志愿者出动)→ 救援中
救援中 →(兔兔接出来了)→ 寄养中 ← 此时自动创建兔兔档案
寄养中 →(找到领养家庭)→ 已领养
每次推进都会向发帖人发送微信消息通知。
「寄养中」后,档案自动创建,但信息是从救援帖标题/描述里复制的,通常需要补全:
- 管理后台 → 兔子列表,找到对应兔兔
- 点击进入兔兔档案,页面顶部会出现黄色提示:
⚠ 此档案为救援审核流程自动创建,请补全名字、性别、出生年月
- 将兔兔名字改为正式名称、补充性别和出生年月
- 可以更换为寄养家庭拍摄的照片
- 点击「保存」
完成后,领养页面就会以完整信息展示这只兔兔,用户可以提交领养申请。
有时志愿者自己留养,或线下直接有人领养,不经过平台寄养流程。
操作:在「救援中」状态,点击「推进状态」选择「已领养」时,系统会弹出确认框并要求填写领养人备注(姓名/联系方式等)。填写后确认。
这个备注存档在兔兔档案里,用于日后追溯,不会对用户展示。
当进度推进到「寄养中」时,系统只会自动创建第一只兔兔档案。如果这次救援同时带回了 2 只或更多:
- 打开救援帖详情页(寄养中状态)
- 管理操作区会出现「补建兔兔档案(多胎)」按钮
- 点击后进入预填好信息的新建档案页
- 修改名字、补全信息、保存
每只兔兔都建一份独立档案,方便分别管理领养进度。
任何阶段都可以将进度推进到「已去世」,系统会同步更新兔兔档案状态(如果已创建),并从公开领养列表中撤除。
救援帖(寄养中)→ 自动创建兔兔档案(寄养中)
↓
用户在「领养」Tab 看到这只兔兔
↓
用户提交领养申请(需填写资质信息)
↓
管理员在「管理后台 → 领养申请」审核
↓
通过 → 联系双方、完成领养交接
↓
管理员在救援帖将进度推进到「已领养」→ 档案同步更新
注意:领养申请批准后,兔兔档案不会自动变为「已领养」——管理员需要单独在救援帖推进状态。这是系统目前设计,避免自动化操作绕过线下交接确认。
每一次状态变更都留有完整记录。如果出现问题,可以在救援帖详情页点击「查看审计」,看到:
- 操作时间
- 操作人
- 变更前后状态
- 备注原因(如驳回理由、强制变更原因)
如果误操作(例如不小心推进了状态),可以用「强制变更」功能(需要填写原因)将状态改回,记录不会消失,完整保留变更轨迹。
系统有两级权限:普通管理员(admin) 和 超级管理员(super admin)。
| 角色 | 配置方式 | 能做什么 |
|---|---|---|
| 普通管理员 | 数据库 role = 'admin' |
审核救援贴、处理领养/寄养申请、管理兔子档案、创建活动 |
| 超级管理员 | 环境变量 ADMIN_OPENIDS |
以上全部 + 管理用户(提升/撤销管理员、封禁/解封账号) |
超级管理员身份由 env 配置决定,不存数据库,封禁对其无效,登录时自动提权为 admin。
cd rabbit_mp_api
# 开发环境(SQLite)
sqlite3 rabbit_mp_dev.db
# 生产环境(根据 DATABASE_URL 配置)
# sqlite3 /path/to/prod.db-- 查看所有用户
SELECT id, username, role, cloud_coins, created_at FROM users;
-- 按用户名搜索
SELECT id, username, role, openid, created_at
FROM users
WHERE username LIKE '%张三%';
-- 查看所有管理员
SELECT id, username, openid, created_at
FROM users
WHERE role = 'admin';
-- 查看最近注册的用户
SELECT id, username, role, created_at
FROM users
ORDER BY created_at DESC
LIMIT 20;
-- 按 ID 查询指定用户
SELECT * FROM users WHERE id = 1;说明:
openid是微信用户的唯一标识,用户登录小程序后自动创建记录。
用户需先登录小程序完成注册,再通过以下命令提升为管理员:
-- 通过用户名提升为管理员
UPDATE users SET role = 'admin' WHERE username = '张三';
-- 通过用户 ID 提升为管理员
UPDATE users SET role = 'admin' WHERE id = 5;
-- 确认修改生效
SELECT id, username, role FROM users WHERE role = 'admin';撤销管理员权限:
UPDATE users SET role = 'user' WHERE username = '张三';# 在 rabbit_mp_api 目录下执行
cd rabbit_mp_api
source .venv/bin/activate
python3 - <<'EOF'
from app.database import SessionLocal
from app.models import User, UserRole
db = SessionLocal()
# 查询所有管理员
admins = db.query(User).filter(User.role == UserRole.admin).all()
for u in admins:
print(f"ID={u.id} 用户名={u.username} 注册时间={u.created_at}")
db.close()
EOF# 提升指定用户为管理员(按用户名)
python3 - <<'EOF'
from app.database import SessionLocal
from app.models import User, UserRole
TARGET_USERNAME = "张三" # 修改为目标用户名
db = SessionLocal()
user = db.query(User).filter(User.username == TARGET_USERNAME).first()
if user:
user.role = UserRole.admin
db.commit()
print(f"✅ 已将 {user.username}(ID={user.id})设为管理员")
else:
print(f"❌ 未找到用户名为 {TARGET_USERNAME} 的用户")
db.close()
EOF超级管理员通过后端环境变量 ADMIN_OPENIDS 配置,不依赖数据库角色。
编辑 rabbit_mp_api/.env,填写超级管理员的微信 openid:
# 支持两种格式:
# JSON 数组
ADMIN_OPENIDS=["openidA","openidB"]
# 逗号分隔
ADMIN_OPENIDS=openidA,openidB重启后端服务后生效。
用户 openid 在数据库 users 表中,登录小程序后自动写入:
-- 按用户名查询 openid
SELECT id, username, openid FROM users WHERE username = '张三';- 首次登录自动提权:openid 在列表中的用户登录时,若数据库 role 不是 admin,系统自动升级(无需手动改库)
- 封禁豁免:即使数据库中被标记为
is_banned,超级管理员仍可正常登录 - 不可互相修改:超级管理员无法在小程序内修改其他超级管理员的角色或封禁状态
超级管理员登录后,管理后台会额外显示「用户管理」入口,可在小程序内:
- 查看所有注册用户列表
- 将普通用户提升为管理员 / 撤销管理员权限
- 封禁账号(被封禁用户无法登录)/ 解封账号
注意:超级管理员自身不能在小程序内被其他超级管理员修改,需通过修改
ADMIN_OPENIDS环境变量来增减超级管理员。
启动后端后访问自动生成的交互式文档:
- Swagger UI:
http://localhost:8000/docs - ReDoc:
http://localhost:8000/redoc
所有图片通过后端静态服务提供,不依赖小程序本地 static 路径:
rabbit_mp_api/static/images/ ← 图片存放位置
https://your-domain.com/static/images/xxx.jpg ← 访问 URL
原因:Vite 会将本地
/static/路径处理为模块导入并生成哈希路径,但实际文件不输出到 dist,导致 404。
本节记录当前小程序在主体资质、收款功能、微信认证、订阅消息方面的现状与待办事项,供后续跟进。
| 功能 | 是否需要认证 |
|---|---|
微信支付(wx.requestPayment) |
必须 |
订阅消息(wx.requestSubscribeMessage) |
必须 |
获取用户手机号(wx.getPhoneNumber 按钮) |
必须 |
| 纯内容展示、无敏感权限 | 不需要 |
当前策略:义卖、爱心橱窗、订阅消息三个功能均已隐藏,暂不触发认证要求,可先上线。
涉及认证的功能现状:
| 功能 | API | 当前状态 |
|---|---|---|
| 救援/领养订阅通知 | wx.requestSubscribeMessage |
已完全移除,代码中无任何调用 |
| 获取手机号 | wx.getPhoneNumber |
未使用,手机号为普通文本输入框 |
| 微信支付 | wx.requestPayment |
未使用,义卖为线下/联系购买模式 |
结论:当前代码中不触发任何需要认证的微信 API,可在未认证状态下上线全部功能。
企业/个体工商户:
- 营业执照(有效期内扫描件)
- 法人身份证正反面
- 法人手机号(接收验证码)
- 银行卡(微信打款验证 + 扣除 300 元年费)
民间团体(未注册): 微信不支持直接认证,可选路径:
- 个体工商户(最快):团队成员以个人名义注册,工商局免费,3-7 个工作日
- 民办非企业单位(最正规):向民政局申请,1-3 个月
- 挂靠已有公益组织:借用其主体申请,需签合作协议
| 路径 | 省掉什么 | 省不掉什么 |
|---|---|---|
| 微信小商店 | 商品管理页面、订单系统、支付对接代码 | 营业执照、微信支付审核、类目资质 |
| 自建商城 | 无 | 以上全部 + 后端开发 |
微信小商店不需要自己的小程序,可独立运营;如需在本小程序内嵌入,需小程序已认证。
- 申请微信支付商户号(
mch_id),提交营业执照、法人身份证、银行账户 - 在小程序后台绑定商户号
- 后端接入支付 API:
- 下单:
POST /v3/pay/transactions/jsapi - 支付回调:处理异步通知
- 退款:
POST /v3/refund/domestic/refunds
- 下单:
- 前端调用
wx.requestPayment(...)拉起支付
当前代码中无任何支付相关代码,义卖页面(
charity/sale-*.vue)仅展示「联系管理员购买」,不涉及wx.requestPayment。
订阅消息是微信在小程序外(服务通知)主动推送给用户的消息,需要:
- 小程序认证
- 在微信公众平台申请消息模板,获得模板 ID
- 用户在小程序内点击同意订阅
- 后端调用微信接口主动推送
与小程序内部消息中心(用户打开小程序才能看到)不同,后者无需认证。
wx.requestSubscribeMessage 调用已从所有页面和组件中移除(rescue/create.vue、rescue/detail.vue、adoption/apply.vue、SubscribeBanner.vue),订阅弹窗不会弹出,不报错,不影响其他功能,可带此状态上线。
站内消息中心(用户打开小程序后查看的通知)不受影响,仍正常工作。
- 完成小程序认证
- 微信公众平台 → 功能 → 订阅消息 → 申请模板(救援审核结果、领养状态变更)
- 将真实模板 ID 填入
Rabbit_mp/config/env.ts - 后端
rabbit_mp_api/app/wechat.py实现推送调用(接口已有路由占位)
| 方案 | 工作量 | 是否需要认证 |
|---|---|---|
| 保留订阅消息,认证后走通 | 中 | 需要 |
| 改为小程序内部消息中心(铃铛通知) | 中 | 不需要 |
| 直接删除消息通知功能 | 小 | 不需要 |
- 确定主体资质(个体工商户 / 民办非企业 / 挂靠)
- 完成小程序认证(300 元/年)
- 在微信公众平台申请订阅消息模板,填写真实模板 ID
- 后端实现订阅消息推送逻辑(
wechat.py) - 决策:卖货走微信小商店还是自建商城
当前图片上传链路:小程序 → 海外 API 服务器 → 保存至 static/images/。由于 API 服务器在海外,图片上传和加载速度对国内用户较慢。
微信云存储(CloudBase)是国内 CDN,上传和加载均走国内节点,可显著提升速度,且后端不再承担文件带宽压力。
将图片直传微信云存储,后端只处理业务逻辑,不再接收文件流。DB 中存储 CDN 永久 HTTPS URL,与现有数据格式完全兼容,旧图片无需迁移。
当前:小程序 ──上传──→ 海外 API ──写文件──→ static/images/ ──返回 URL──→ DB
优化后:小程序 ──上传──→ 微信云存储 CDN(国内)──返回永久 URL──→ DB
海外 API 只做业务,不碰文件
- 在微信云开发控制台开启云存储,并启用 CDN 加速
- 在云存储安全规则中配置:已登录用户可上传,所有人可读
- 获取存储桶的 CDN 域名(格式:
https://xxxx.tcb.qcloud.la)
当前
App.vue已调用wx.cloud.init({ env: CLOUD_ENV_ID }),utils/cloud.ts已实现uploadToCloud和getTempFileURL,基础框架已就位。
1. Rabbit_mp/utils/cloud.ts — 增加 CDN URL 拼接函数
const CDN_BASE = 'https://你的存储桶CDN域名' // 从 CloudBase 控制台获取
export function getCloudCDNUrl(cloudPath: string): string {
return `${CDN_BASE}/${cloudPath}`
}
export async function uploadToCloudAndGetUrl(
filePath: string,
cloudPath: string
): Promise<string> {
await uploadToCloud(filePath, cloudPath)
return getCloudCDNUrl(cloudPath)
}2. Rabbit_mp/services/api-upload.ts — 切换上传目标
import { uploadToCloudAndGetUrl } from '@/utils/cloud'
export async function uploadImage(filePath: string): Promise<string> {
const ext = filePath.split('.').pop() || 'jpg'
// cloudPath 加时间戳+随机串,避免文件名冲突
const cloudPath = `images/${Date.now()}_${Math.random().toString(36).slice(2)}.${ext}`
return uploadToCloudAndGetUrl(filePath, cloudPath)
}所有调用
uploadImage()的.vue文件一行不用改,接口签名相同,返回值仍是 HTTPS URL。
3. 后端 upload.py — 可保留作为降级方案,无需立即删除
| 维度 | 评估 |
|---|---|
| 改动文件数 | 仅 2 个(cloud.ts、api-upload.ts) |
| 调用方(.vue 文件)改动 | 零 |
| 旧图片兼容性 | 完全兼容,旧 URL 继续有效 |
| 后端文件校验 | 绕过(原有 Pillow 格式/大小/内容验证失效,需接受此 tradeoff) |
| 费用 | 云存储按量计费,小程序体量极低,基本免费 |
| 维度 | 当前(海外服务器) | 迁移后(云存储 CDN) |
|---|---|---|
| 上传速度 | 慢(跨境) | 快(国内节点) |
| 图片加载速度 | 慢(海外出口) | 快(CDN 就近分发) |
| 后端带宽压力 | 高 | 零 |
| 文件安全校验 | 有(格式/大小/内容) | 无(需另行处理) |
MIT