背景:`tests/e2e/activity_mutual_consent_test.py` 与 `service_appointment_test.py` 此前是 **诚实占位**(仅 docstring、0 用例,指针指向 run_v11_tests.py),计划 Task 2.1 要求把 文件头保留的场景清单逐条实现为真实 HTTP 用例。本提交完成该任务。 ═══ 新增真实用例 16 个(本机活栈 pytest tests/e2e 49 → 65 passed)═══ activity_mutual_consent_test.py(8,覆盖 INV-014/015/006 + AGENTS.md #19/#20) 1 建立互选记录不建 Match(INV-014) 2 单方确认不建 Match(INV-015) 3 双方确认建 Match Episode 且双方可见 4 已有 active Match 时再次双确认必须复用(#19) 5 曾 Unmatch 后重新双确认开启新 Episode:episode +1(#20) 6 一方拒绝不建 Match,对方随后确认也不建 7 超时:已过期记录不得被推动成双确认(含「未过期必须生效」对照,防假绿) 8 单向意向不泄露(INV-006,含「B 确认后必须看到 accepted」反证) service_appointment_test.py(8) 1 提交预约;2 幂等键重放不得产生第二条;3 运营确认 submitted→confirmed(用户侧同态可见) 4 并发「用户取消 × 运营确认」不得 5xx、最终状态必须收敛;5 我的列表只含本人 6 待确认可取消;7 归属隔离(他人不可读详情/不可取消) 8 运营侧状态闸门:已完成预约不得再被取消 ═══ 两个幂等 seed(E2E 前置数据,本机与 CI 通用)═══ - seed_e2e_activity.sql:organization + 两个 activity。 必须建的原因是硬约束而非选择:`activity_mutual_connection_consents.activity_id` 有 **FK 到 activities(id)**(实测约束名 …_activity_id_fkey),而 activities.organizer_org_id 又 FK 到 organizations(id) 且 NOT NULL —— 全新库里三张表全空,互选无活动可挂。 - seed_e2e_service_station.sql:service_location + **站点-运营关联**。 关联必需:运营确认受数据范围约束(`assertOperatorScope` → `canAccessByOperator`), super_admin **不豁免**,实测 e2e_admin 确认他站预约返回 403。 E2E 因此改用 seed 中该站点关联的 e2e_station。 两个 seed 由测试首用时幂等执行(docker exec psql),无需改 CI 步骤。 ═══ 负向注入(Task 2.1 Step 3)═══ ③ 撤销 service_station_operator 的 service_appointment.confirm → 恰 2 红(test_operator_can_confirm / test_operator_cannot_cancel_completed,403),其余 6 绿; ④ 触发器强制新建互选记录的对方侧 accepted → 3 红(both_confirm_creates_match_episode / timeout_handled / single_interest_not_leaked),其余 5 绿。 两处均已还原并复跑全绿(65 passed),确认无残留(无 e2e* 触发器、权限计数回到 1)。 **两次失败的注入(记录以免后人重复踩)**: - 删除站点→运营关联:**无效** —— 测试的 seed fixture 在用例前幂等重放 seed 会自愈该前置条件; - 改 user_b_status 列默认值:**无效** —— `createMutualConsent` 在 Java 侧显式写 pending, DB 默认值根本不参与(侧面证明写入路径的权威性)。 ═══ 计划文档校正(审阅产物)═══ 按实测推翻 4 处失实断言,登记 7 项新发现(详见计划文档「附 A / 附 B」): - 推翻:matchmaker_services「无表/需扩权」(实为 V009 已建表且 mapper 齐备); 门禁 14/14(实为 18/18);「tests/e2e 现为占位」(实为仅 2 个文件); E2E 基线 49(实为 65)。 - 新发现:① `POST /operator/organizations` 必然 500(createOrganization 不赋 id + IdType.INPUT + 无 DB 默认值;且未校验 chk_org_type)→ 连带「无法新建活动」; ② `createMutualConsent` 从不写 expires_at → 互选永不过期(即批 3 P16-T13,配置与 V041 部分索引空转);③ 用户侧取消预约不校验状态(已确认/已完成也能自取消), 与运营侧明确拒绝 completed 口径不一致 → 需产品裁定; ④ E2E 环境 organizations/activities/service_locations 均为空表(无迁移种子); ⑤ 运营数据范围 super_admin 不豁免(预期行为,勿误判); ⑥ 本机复跑会遇到 Redis 登录锁定键(auth:operator_login_lock:*); ⑦ e2e_* 账号因 ON CONFLICT DO NOTHING 重跑 seed 不覆盖旧口令。 ═══ 验证 ═══ - `pytest tests/e2e` → **65 passed**(含新增 16);`node scripts/run-ui-contracts.mjs` → **18/18 PASS**,退出 0; - 本机环境处置(不属代码改动):把 6 个 e2e_* 账号口令对齐到 CI 口令、清除 Redis 登录锁定键。 - **未验证**:微信开发者工具、CI 上的首次 E2E 实测绿(Task 2.3 Step 3,本机无 Docker Hub 出口)。
善缘丹阳公益婚恋服务平台 V1.1 增量恢复中
面向丹阳市成年单身人士,由公益组织参与运营的实名制公益婚恋服务平台。
真实 · 公益 · 本地 · 安全 · 高效
当前基线: V1.0.0-RC.2 + V1.1 客户需求增量 · 状态:INCREMENTAL RECOVERY / NO-GO
当前代码包含 V1.1 部分实现,但尚未通过 Admin、Miniapp、权限和 E2E 发布门禁。请勿依据历史 Completion Report 或 Jira 勾选项判断已经上线。唯一执行计划见
docs/善缘丹阳 V1.1 客户需求基线增量开发与修复计划.md。
项目简介
本平台帮助丹阳本地单身人士通过实名认证、人工审核、智能匹配、双向喜欢、在线沟通、公益红娘和线下相亲活动,安全、高效、真实地寻找合适伴侣。
用户永久免费。
技术栈
| 组件 | 技术 |
|---|---|
| 后端 | Java 21 + Spring Boot 3.x |
| 数据库 | PostgreSQL |
| 缓存/Session | Redis |
| 对象存储 | S3 兼容(MinIO / 国内云存储) |
| 实时通信 | WebSocket |
| 数据库迁移 | Flyway |
| 小程序 | 微信原生 + TypeScript |
| 管理后台 | Vue 3 + TypeScript + Vite + Pinia + Element Plus |
| 容器化 | Docker + Docker Compose + Nginx |
| CI/CD | GitHub Actions |
项目结构
├── backend/ # Spring Boot 后端服务
├── miniapp/ # 微信小程序用户端
├── admin-web/ # 运营管理后台(Web)
├── infrastructure/ # Docker Compose 编排
├── scripts/ # 运维脚本
├── tests/ # 测试代码
│ ├── unit/
│ ├── integration/
│ ├── api/
│ ├── permission/
│ ├── concurrency/
│ ├── e2e/
│ └── invariants/ # 核心不变量测试(每次CI必跑)
├── docs/ # 项目文档
│ ├── product/
│ ├── architecture/
│ ├── api/
│ ├── database/
│ ├── security/
│ ├── testing/
│ ├── ops/
│ └── decisions/
├── AGENTS.md # 项目最高执行规则摘要
├── CHANGELOG.md
└── README.md
快速开始(开发环境)
前置要求
- Docker & Docker Compose
- JDK 21
- Node.js 18+
- npm / pnpm
一键启动基础设施
# 进入基础设施目录
cd infrastructure
# 启动 PostgreSQL / Redis / MinIO
docker compose up -d
# 等待服务就绪(约30秒)
# 可运行:docker compose logs -f
启动后端
cd backend
# 复制环境变量
cp src/main/resources/application-dev.yml.example src/main/resources/application-dev.yml
# 编辑 application-dev.yml,设置数据库连接等
# 构建并启动
./mvnw spring-boot:run -Dspring-boot.run.profiles=dev
启动管理后台
cd admin-web
# 安装依赖
pnpm install
# 启动开发服务器
pnpm dev
启动小程序
使用微信开发者工具打开 miniapp/ 目录,配置 AppID 后即可预览。
环境变量
所有环境变量参见各模块的 .env.example 文件。
关键变量:
# 数据库
DB_HOST=localhost
DB_PORT=5432
DB_NAME=danyang_match
DB_USER=app_rw
DB_PASSWORD=<secret>
# Redis
REDIS_HOST=localhost
REDIS_PORT=6379
# 对象存储
MINIO_ENDPOINT=localhost:9000
MINIO_ACCESS_KEY=<secret>
MINIO_SECRET_KEY=<secret>
MINIO_BUCKET_PUBLIC=public
MINIO_BUCKET_PRIVATE=private
# 微信
WECHAT_APP_ID=<secret>
WECHAT_APP_SECRET=<secret>
Secret 不进 Git。
数据库迁移
cd backend
./mvnw flyway:migrate -Dflyway.url=jdbc:postgresql://localhost:5432/danyang_match
运行测试
Phase 01 一键基线检查
PowerShell:
.\scripts\verify-phase01.ps1
Bash:
./scripts/verify-phase01.sh
脚本依次检查 Git、Backend tests、Admin type-check/build、Miniapp TypeScript 和 V1.1 E2E。任一门禁失败时脚本返回非零退出码,并在结尾列出失败项。Windows 未安装 JDK/Maven 时,Backend 检查使用 Docker 中的 JDK 21 与仓库内 Maven 包;不会永久修改系统环境。
首次执行 Admin/Miniapp 检查前,在 admin-web 目录运行 npm ci。E2E 使用 python -m pytest,不要依赖全局 pytest 命令。
分项命令
# 后端单元测试
cd backend
./mvnw test
# 后端集成测试
./mvnw verify
# 管理后台构建检查
cd admin-web
pnpm build
# 小程序编译检查
cd miniapp
# 在微信开发者工具中查看编译结果
构建部署
# 构建后端镜像
docker build -t danyang-match-backend:latest ./backend
# 构建管理后台
cd admin-web && pnpm build
# 完整部署
cd infrastructure && docker compose up -d
核心业务闭环
微信登录 → 绑定手机 → 实名认证 → 填写资料 → 设置择偶 → 提交审核
↓
审核通过 → 系统推荐 → 用户Like → 双向Match → 安全聊天
↓
公益红娘辅助 / 参加线下活动 → 进一步了解 → 双方确认成功牵手
开发阶段
项目按 Phase 01~Phase 26 顺序推进。详见 docs/STATUS.md。
许可证
本项目为公益项目,服务于丹阳市单身人士婚恋匹配。