Commit Graph
17 Commits
Author SHA1 Message Date
WorkBuddy 69ebf33e18 release: freeze demo v1.1.0 2026-10-19
CI/CD Pipeline / Backend Build & Test (push) Canceled after 0s
CI/CD Pipeline / Admin Web Build (push) Canceled after 0s
CI/CD Pipeline / Mini Program Compile Check (push) Canceled after 0s
CI/CD Pipeline / Migration Validation (push) Canceled after 0s
CI/CD Pipeline / Test Shell Guard (Phase 23 Gate) (push) Canceled after 0s
CI/CD Pipeline / UI Contracts (source-structure gates) (push) Canceled after 0s
CI/CD Pipeline / E2E (live stack) (push) Canceled after 0s
2026-10-19 20:36:22 +08:00
WorkBuddy 8b505d9133 feat: disable user messaging and retain公益 service guidance
CI/CD Pipeline / Test Shell Guard (Phase 23 Gate) (push) Canceled after 0s
CI/CD Pipeline / Backend Build & Test (push) Canceled after 0s
CI/CD Pipeline / Admin Web Build (push) Canceled after 0s
CI/CD Pipeline / Mini Program Compile Check (push) Canceled after 0s
CI/CD Pipeline / Migration Validation (push) Canceled after 0s
CI/CD Pipeline / UI Contracts (source-structure gates) (push) Canceled after 0s
CI/CD Pipeline / E2E (live stack) (push) Canceled after 0s
2026-10-15 13:38:17 +08:00
WorkBuddy 3c2b967274 release: freeze 2026-10-15 validated release baseline
CI/CD Pipeline / UI Contracts (source-structure gates) (push) Canceled after 0s
CI/CD Pipeline / E2E (live stack) (push) Canceled after 0s
CI/CD Pipeline / Backend Build & Test (push) Canceled after 0s
CI/CD Pipeline / Admin Web Build (push) Canceled after 0s
CI/CD Pipeline / Mini Program Compile Check (push) Canceled after 0s
CI/CD Pipeline / Migration Validation (push) Canceled after 0s
CI/CD Pipeline / Test Shell Guard (Phase 23 Gate) (push) Canceled after 0s
2026-10-15 09:47:31 +08:00
WorkBuddy 0a100548a9 docs(phase26): 上线前准备 — 13 项手动验证清单 + 冒烟脚本骨架 + STATUS 文档矛盾清理
- 新增 docs/PHASE26-GO-LIVE-CHECKLIST.md:上线前 13 项人工验证清单
  (部署栈存活 / 微信登录 / 运营登录越权隔离 / 资料择偶条件 / 照片 SeaweedFS
  直传 / 推荐匹配 / 聊天 / 活动 / 咨询 / 红娘转介 / 内容 / 管理端口径 / 合规备份),
  每项含验证方式、通过标准、负责方、关联决策与配置旋钮;自动化子集见脚本。
- 加固 infrastructure/scripts/smoke-test.sh:健康检查 + 匿名鉴权守卫(401) +
  安全接口;带 OPERATOR_TOKEN/USER_TOKEN 时展开需登录链路,未提供明确打印
  「需人工验证」而非假绿(沿用项目「断言≠证据/不假绿」纪律)。修掉 curl -f
  在 4xx 退出码会把 000 拼到状态码后的 bug。
- STATUS.md:修正「V1.1 待实现模块」表把本地资格声明与审核误标 ⏳ 待开发
  的矛盾(实际 Phase 04/05 已于 2026-09-12 复验关闭,LocalEligibilityService
  已实现),与全文一致。
2026-09-23 11:57:07 +08:00
WorkBuddy dc0a62d4c5 fix(storage): presigned URL 改用对外可达 host,修复客户端直传断链
SeaweedFS 换存储后 MINIO_ENDPOINT 变为 seaweedfs:8333(容器名),
PhotoService 生成的 presigned URL 把该 host 直接下发给客户端,
客户端不在 docker 网络内无法解析 → 直传/直链全断。

- S3Config:新增 amazonS3Signer Bean,指向 object-storage.public-endpoint
  (缺省回退内网 endpoint,向后兼容)。独立签名端是因为 SigV4 签名覆盖
  host 头,事后改 URL 字符串会让签名失效返 403;独立客户端让签名 host
  与客户端实际命中的 host 天然一致。
- PhotoService:去掉 @RequiredArgsConstructor,改显式 @Qualifier 构造器;
  getUploadToken / getSignedUrl / getSignedUrlForAuthorizedReview 三处
  presigned 生成改用 amazonS3Signer;putObject 服务端上传仍走内网 amazonS3。
- application-docker.yml / application-dev.yml:public-endpoint 读
  MINIO_PUBLIC_ENDPOINT(缺省回退 MINIO_ENDPOINT)。
- 三套 compose:注入 MINIO_PUBLIC_ENDPOINT
  (dev localhost:8333 / staging localhost:8334 / prod 留作单一旋钮)。
- 测试:PhotoServiceTest 新增嵌套 PresignedUrlPublicHostTest,用真实
  AmazonS3 客户端(仅本地签名不联网)断言下发 URL host 为对外地址、
  不含 seaweedfs 容器名;同步补齐两处 5→6 参构造调用。

验证:本机 65 E2E 全绿 + storage_link_probe path-style 全通 +
PhotoServiceTest/PhotoUploadValidationTest/PhotoLimitGuardTest 19 例单测全绿。

顺带:backend/maven-settings.xml 的 Central 兜底由投毒的 repo1.maven.org
改为可达的 repo.maven.apache.org,使本机镜像构建可走 aliyun 主源。
2026-09-22 17:10:40 +08:00
WorkBuddy 797b1c46cf refactor(storage): 对象存储从 MinIO 换成 SeaweedFS(钉 4.47)
动机:MinIO 的 GitHub 仓库已于 2026-02 归档,不再产出安全补丁,已累积多个
CVE 且不修;本项目存身份证 / 实名材料 / 用户照片,无人修 CVE 才是真正驱动因素
(Docker Hub 下架只是表象,quay.io 仍在提供)。

变更:
- 三套 compose(dev / staging / prod)的 minio 服务替换为 seaweedfs 4.47:
  覆盖 entrypoint 渲染 -s3.config 注入 identity(S3 网关默认不鉴权,必须显式开启),
  -volume.max=100 解除第二个 bucket 写入时的 InternalError。
- 新增一次性 storage-init 容器,经 filer 接口预建 public / private 桶
  (SeaweedFS PutObject 到不存在的 bucket 直接失败;全仓库原无任何 createBucket)。
- S3Config 显式开启 path-style:AWS SDK v1 默认 virtual-host 会把 bucket 拼进
  主机名,容器名在 DNS 中不存在必然失败(本机有 http_proxy 时易误判为 502 拒绝)。
- ci.yml:镜像校验与起栈步骤改为 seaweedfs + storage-init。
- 运维脚本 health-check.sh / run-uat.sh 的 MinIO 探针改为 SeaweedFS
  (S3 网关开启鉴权后匿名根请求返回 403,200/403 均算存活)。

注:path-style 与建桶属换存储的必要项;presigned URL 的 host 须对外可达这一
既有缺陷(原 minio:9000 直发客户端不可达)留待下一轮「改缺陷」处理。
2026-09-22 14:26:48 +08:00
WorkBuddy 7186a6b694 fix(infra): minio 镜像钉回确定版本(依据 CI 实测输出)
上一轮为让 CI 先跑通,minio 临时改用 quay.io 的 latest 滚动 tag,并在 e2e job 里
加了一步打印实际版本。该步骤的实测输出为:

  minio version RELEASE.2025-09-07T16-13-09Z (commit-id=07c3a429bfed…)

即 latest 当前正指向这个版本 —— 与本仓库原先在 Docker Hub 上钉的 tag 完全一致。
据此把三处 compose(本地 / staging / prod)统一钉死为
quay.io/minio/minio:RELEASE.2025-09-07T16-13-09Z,恢复「可复现」这一原则:
日后即使 quay 上的 latest 滚动到别的版本,本配置也不受影响。

保留 e2e 中「校验钉版镜像可拉取并打印版本」这一步 —— 镜像拉取失败在 compose up
里会埋进几十行输出,独立一步能在起栈前秒级暴露「上游镜像下架」这类外部依赖失效。

上一轮结果:7 个 job 全部 success,GitHub Actions 首次跑绿。
2026-09-22 11:16:37 +08:00
WorkBuddy 4c98170107 fix(infra): minio 镜像源从 Docker Hub 迁到 quay.io(上游已下架,本地/CI/生产均受影响)
E2E job 日志:
  pull access denied for minio/minio, repository does not exist

问题不在 tag 本身,而在**整个仓库已从 Docker Hub 移除**:MinIO 自 2025-10 起停止
发布容器镜像(改为仅源码分发),其 GitHub 仓库于 2026-02 归档,Docker Hub 上
minio/minio 与 minio/mc 现均返回 404;quay.io/minio/minio 仍在提供镜像。

本机此前是用 quay.io 的镜像重打 tag 顶替才跑通的(见 known-risks KR-01),
但那只是**本地绕过**、没有同步回仓库,于是「本地能起栈、CI 拉不到」。
现在三处 compose(本地 / staging / prod)统一改为 quay.io,
让本地、CI 与生产拉取同一个镜像。

这不只是 CI 问题:prod 与 staging 同样引用了已下架的仓库,是真实的部署阻塞
—— 也就是说现有配置下,新环境的生产部署根本起不来。

已知偏离:使用 latest 而非钉版。本机无法访问 quay 的 tag API 去核实某个具体
RELEASE tag 是否仍存在(quay 上的镜像已冻结,最后发布为 2025-09)。
e2e job 新增一步打印实际拉取到的版本,日后即可据此钉版、恢复可复现性。
长期风险:quay 上的 MinIO 镜像同样可能下架,届时需自建镜像或迁移到其他
S3 兼容存储 —— 「依赖第三方镜像」本身就是一条外部依赖风险。

上一轮修复已见效:backend / admin-web / ui-contracts / miniapp /
test-shell-guard / migration 六个 job 均通过,本轮仅剩 E2E。
2026-09-22 11:04:01 +08:00
WorkBuddy b624c43432 fix(ci): 依据 Actions 真实日志修复三处失败(runner 无 pnpm / 门禁缺依赖 / 生产配置未入库)
上一轮已把 CI 跑起来并读到真实结果:7 个 job 中 Mini Program 与 Test Shell Guard
通过,Admin Web、Backend、UI Contracts 三个失败,migration 与 e2e 因依赖 backend
被跳过。本轮全部依据 Actions 原始日志定位,而不是本地推测 —— 三者都不是业务代码问题。

1. Admin Web Build:runner 已不再预装 pnpm
   日志:Unable to locate executable file: pnpm
   runner 镜像(ubuntu-24.04 / 2.337.0)的 PATH 里没有 pnpm,而 setup-node 的
   cache: 'pnpm' 在 restore 阶段就需要调用 pnpm 可执行文件,于是 job 在第一步失败、
   后续步骤全部 skipped。同批的 miniapp job 没配 cache 所以正常通过,正好反向印证。
   改为先用 corepack 启用并钉住 pnpm@9.15.9,store 缓存改由 actions/cache 手动配置
   (此时 pnpm 才存在于 PATH)。

2. UI Contracts:门禁依赖没装
   日志:Cannot find module '.../miniapp/node_modules/typescript/lib/typescript.js'
   phase04 守卫会用 TypeScript 编译器 API 解析小程序源码,但本 job 此前不装依赖。
   本地能跑通只因为工作区早已装过;CI 的每个 job 都在独立 runner 上,
   miniapp job 安装的依赖不会共享过来。补上 npm install,并修正 workflow 中
   「零 npm 依赖、故无需装依赖」的错误注释 —— 与实际不符的声明本身就是缺陷来源。

3. Backend:断言了一个永远不会入库的文件
   日志:DeploymentTests.testDockerComposeProdExists 期望 true 实际 false
   infrastructure/docker-compose.prod.yml 被 .gitignore 列在
   「Secrets and certificates」段而排除出版本控制,但这是**误分类**:
   该文件不含任何明文密钥,敏感值全部走 ${VAR} 注入、db 密码走 Docker secrets,
   文件头注释本身也写着「敏感信息通过 .env 文件注入,不进 Git」。
   后果是该测试「本地恒绿(文件在工作区)、CI 恒红(checkout 拿不到)」,
   且从本地永远发现不了。现修正 .gitignore 把它纳入版本控制;
   真正的密钥仍由 secrets/、certs/、backups/ 等规则挡住。

修复后,此前因 backend 失败而被跳过的 migration 与 e2e 两个 job 也会真正跑起来。
2026-09-22 10:54:24 +08:00
WorkBuddy 8bd1ab804a fix(organization): 修复创建机构审计缺操作人;补全机构修复的验证并部署 Docker 测试环境
一、机构审计归属缺陷(本轮活栈验收中新发现)
- 现象:audit_logs 中 CREATE_ORGANIZATION 的 operator_id 7/7 为 NULL,
  而 DISABLE/ENABLE_ORGANIZATION 均正确记录操作人。审计不可归属 = 无效审计。
- 根因:OrganizationController.create 持有 @AuthenticationPrincipal OperatorPrincipal,
  却只把 params 传给 Service,未透传 principal.id() → saveAuditLog(null, ...)。
- 修复:createOrganization(params, operatorId) 签名透传;新增测试断言
  createOrganizationRecordsOperatorInAuditLog 长期守卫。
- 复验:新写入的 7 条 CREATE 审计全部带 operator_id,旧 14 条仍为 NULL(前后对照清晰)。

二、补全 rc.22 的验证缺口(此前记为「已修复待验证」)
- 全量回归 757 / 0 / 0 / 3,BUILD SUCCESS,108 个测试类,6:44(基线 741 + 新增 16)。
- 负向注入:ORG_STATUS_ACTIVE 改回 "normal" → 16 例变 3 失败 + 7 错误
  (守卫三条断言各自命中 + 服务侧 7 例被 H2 CHK_ORG_STATUS 拦下)。
  注入采用「仓库只读挂载 + 容器内副本改写」,工作区哈希前后一致,源码零污染。
- 活栈实测(Docker 全栈):创建/停用/启用均 2xx、非法输入 400、不存在 404、全程零 5xx(15/15)。
- 对已部署栈 pytest tests/e2e → 65 passed。

三、Docker 测试环境部署与工具链固化
- 测试镜像 danyang-match-backend-test 与命名卷 danyang-m2-cache 因 Docker Desktop 重装丢失,
  构建定义此前是临时产物 → 落盘为仓库资产 infrastructure/test-toolchain/Dockerfile。
- 本地栈统一使用 docker compose -p danyang(原项目名 infra 与另一项目撞名,
  其 --remove-orphans 会误删本项目容器;已确认邻项目数据无丢失,见 known-risks KR-09)。

四、UI 门禁脆弱性修复
- ui-document-baseline-contracts.mjs 解析 docker-compose.staging.yml 时报假红
  (「守卫自身失效」);真因是该文件为 CRLF 而守卫正则要求 \n 紧随 container_name 之后
  (prod.yml 是 LF 所以此前能过)。解析改为行尾无关,真实断言未削弱。门禁 18/18。

五、文档
- STATUS.md 新增 rc.23 并将 rc.22 由「待验证」订正为「已验证关闭」。
- 实施计划附 B.1、PROGRESS-ASSESSMENT-2026-09-21.md 同步订正。
- known-risks.md:KR-07 解除(Docker 已恢复)、KR-01/KR-06 按复测订正,
  新增 KR-08(Docker Hub 域名 DNS 被投毒,改用国内镜像源绕行)与 KR-09(compose 项目名撞名)。

验证状态:全量回归 757/0/0/3 全绿;UI 契约门禁 18/18;对已部署栈 E2E 65/65;
负向注入可变红;工作区哈希未被注入过程污染。
未验证:GitHub Actions 首次真绿(仍未推送,见 KR-06)。
2026-09-22 00:46:10 +08:00
WorkBuddy 5715987049 fix(infra): 修复健康探针的 IPv4/IPv6 解析歧义(nginx 假 unhealthy)
问题:本地完整栈起齐后 nginx 容器报 unhealthy,但宿主 curl
http://localhost:6688/nginx-health 返回 200 —— 探针与实际服务不一致。

根因(实证,非推断):
- nginx 的 `listen 80` 只绑 IPv4:容器内 netstat 显示 0.0.0.0:80,无 [::]:80;
- /etc/hosts 中存在 `::1 localhost`,busybox wget 解析 localhost 时优先取 ::1;
- 容器内实测:wget http://127.0.0.1/nginx-health → ok
              wget http://localhost/nginx-health  → Connection refused。

即探针地址与服务的实际绑定族不一致,属「探针与被探对象脱钩」的假告警,
并会连带拖垮 depends_on: service_healthy 的下游等待。
(backend 之所以此前一直 healthy,是因为 Java 恰好双栈监听 :::8080 —— 属偶然,不应被依赖。)

修复:
- infrastructure/docker-compose.yml:nginx 与 backend 探针改用 127.0.0.1,并各加注释写明理由;
- infrastructure/docker-compose.staging.yml:backend 探针的同类写法一并改(staging 栈未起,
  未经运行验证,仅保持写法同构)。

验证:
- docker compose config 通过;nginx 重建后 State.Health = healthy(FailingStreak=0,探针 ExitCode=0);
- 链路回归:/admin/ 200(877B)、/admin/assets/index-CDxMNwaf.js 200(1,267,697B)、
  /nginx-health 200、/api/v1/health/live 200;/admin 与根路径均 301 → /admin/;
- 活栈 E2E 49 passed;UI 契约门禁 14/14 PASS。
2026-09-18 22:27:26 +08:00
WorkBuddy f63e579a8d ci(e2e): 落地「活栈供给方式」决策 — Compose in CI,并修掉 6 处真实缺陷
决策:E2E 活栈在 runner 内用 docker compose 起全栈(postgres + redis + minio + backend),
与本地验证路径完全一致;未采纳「service containers + runner 上 mvn package 后 java -jar」——
那需要为后端另写一套 host 网络配置,等于制造第二条与本地不一致的路径。

本机实测(与 CI 同一序列):中间件三项 healthcheck 全 Healthy(pg_isready / redis-cli ping /
mc ready local)→ backend /api/v1/health/live 返回 200 → seed 幂等可执行(INSERT 0 0)
→ pytest tests/e2e → 49 passed。
(本机限制:Docker Hub 不可达且无 temurin 基础镜像,未复现 --build;改用本地既有镜像为底座、
挂载当日 16:24 构建的 jar,使被验证的代码版本与 CI 一致 —— 否则是拿 09-09 的旧代码验 09-18 的用例。)

修掉的 6 处缺陷:
1. minio/minio:latest 是滚动 tag → pin RELEASE.2025-09-07T16-13-09Z(否则 CI 拉到的镜像
   与本地验证时不同,表现为不可复现的偶发失败)
2. up 未显式列出 minio(靠 depends_on 隐式拉起),而 minio 的 healthcheck 正是 backend
   能否启动的前置条件
3. up --build 把构建与起栈耦合,失败日志混杂 → 拆为 build / up 两步
4. 就绪窗口 5min 偏紧(首次构建后仍需 Spring 启动 + Flyway 46 个迁移)→ 7.5min
5. 缺 down -v 前置清卷:seed 用 ON CONFLICT (username) DO NOTHING,残留卷会让账号口令
   停留在上一轮 → 登录 401,并伪装成业务失败
6. job 无 timeout(GH Actions 默认 360 分钟)→ timeout-minutes: 30

另:pytest -v → pytest tests/e2e -v。原以为从仓库根收集会误收 tests/*_test.py
(其在 import 期依赖 psycopg2 与 E2E_DB_PASSWORD),实测否定:pytest.ini 的
testpaths = tests/e2e 已限定范围,从根收集正是 49 个用例且无 error。显式传参作为兜底保留。

计划 Task 2.3 由「依赖基础设施决策」改为「已决策并落地」,仅余 Step 3(CI 上实测一次绿)。
2026-09-18 21:58:56 +08:00
WorkBuddy f96dcb8aa6 fix(admin): 管理端入口收敛到 /admin/ 子路径,staging 端口 13521,修复生产 /admin/ 假成功
承接 rc.15 的管理端端口收敛(3000 → 13520),本轮把「入口」也收敛到一处:

- staging admin 发布端口 3001 → 13521(保持「生产 + 1」错开),同步
  tests/uat/run-uat.sh 的默认 UAT_ADMIN_URL。
- 管理端产物改为 /admin/ 子路径部署:vite 构建 base=/admin/(dev 仍为 /,开发与
  排查习惯不变)、router history base 跟随 import.meta.env.BASE_URL、三处容器化
  入口(生产 nginx / 本地整套栈 nginx / admin-web 容器内 nginx)统一为「不剥离
  前缀」转发,形态一致。
- 修复生产 nginx /admin/ 的假成功:原为 alias /usr/share/nginx/html/,而那正是
  nginx:alpine 纯净镜像自带的默认页目录(管理端产物不在该容器内),访问返回
  200 + nginx 欢迎页 —— 浏览器不报错、状态码正常,只是打开的不是管理端,比 404
  更难发现。改为 upstream admin_web + proxy_pass,并以真实两跳容器实测:
  /admin/ → 200 且为管理端标题与 /admin/assets/*、/admin/dashboard → 200(SPA
  兜底)、/admin/brand/logo-mark.png → 200、根路径 → 显式 404(用 location = /
  钉住,不依赖「本配置恰好没有 server 级 root」这一偶然事实)。
- 品牌资源引用统一走新增的 src/brand.ts(public/ 资源不受 vite base 影响,原 5 处
  写死根路径,子路径部署下会裂图);favicon 由仓库内并不存在的 /favicon.svg 改为
  存在的品牌标。
- 补全 tests/uat/run-uat.sh 被截断的尾部:原文件停在 `if [ -`,bash -n 不通过、
  脚本根本无法运行;顺带把此前「定义了却无人消费」的 ADMIN_URL 接到管理端可达性
  实测上,并补结果汇总与非零退出码。
- ui-tests:ui-document-baseline-contracts 新增两类一致性断言(staging 发布端口 ⇄
  run-uat.sh 默认入口、子路径部署的五处约束);admin-reference-contracts 的资产
  断言适配 brand 模块(断言「组件经 brand 模块引用」+「brand 模块指向正式资产」)。
  6 条新增断言全部经负向注入验证(注入对应缺陷 → 守卫变红且信息指向正确对象)。

验证:UI 门控 14/14 绿;vue-tsc EXIT=0;vite build EXIT=0 且产物资源均为
/admin/ 前缀;三份 nginx 配置 nginx -t 通过。

注:docs/STATUS.md 含此前的在途文档改动(非本次工作,且其中引用了已被删除的
plans 文件),故未纳入本提交;本轮的口径更新已写入该文件待一并处理。
2026-09-18 20:51:12 +08:00
Codex ea2bb2439d chore: complete infrastructure config, contracts, reference images and ui redesign artifacts 2026-09-14 10:02:34 +08:00
admin ac2327b499 chore: checkpoint before switching to main 2026-09-11 09:07:41 +08:00
admin 5b51d5b3f1 feat: complete phase 02 RBAC and data scope recovery 2026-09-10 10:58:43 +08:00
admin b46b48eb33 chore: establish V1.1 recovery baseline 2026-09-09 22:05:00 +08:00