Commit Graph

23 Commits

Author SHA1 Message Date
lixi a5634c5f85 feat: 用户资料读写——GET/PATCH /api/v1/me 补昵称与头像 URL(T3.5-04,ADR-022)
- GET /api/v1/me 补 nickname 与 avatarUrl:nickname 为 DB 原值不做 username
  回退(/me 是本人编辑态,回退会让用户误以为已设过昵称;他人视角的回退仍在
  /internal/users/profiles 的 SQL 层,M3 T3-05 已交付);avatarUrl 每次现签
  预签名 GET,非 ready 或存储未配置一律降级为 null 而非 500
- 新增 PATCH /api/v1/me:nickname 与 avatarAssetId 均为三态语义——键缺省即
  不改、显式 null 即清空、给值即设置;空 patch 答 400/40000 而非静默 200
- nickname 校验对齐 ck_users_nickname:btrim 后按码点计 1~32(PostgreSQL
  char_length 数码点,按 UTF-16 长度校验会误拒 32 个 emoji),纯空白答 40000
  而非隐式清空(清空只留显式 null 一种表达)
- avatarAssetId 沿用 T3-03 引用侧协议:不存在/非本人/已删/用途非
  user_avatar 均答 404/40405(防枚举合并),本人 user_avatar 未就绪答
  422/42203
- 写入走列级选择性 UPDATE 而非读-合并-写:并发的昵称与头像 PATCH 互不覆盖,
  故 /me 无需暴露版本号乐观锁
- MediaProperties.allowedPurposes 增 user_avatar/pet_avatar(ADR-022:purpose
  无 CHECK 约束,新用途只改配置,零 Flyway 迁移)
- 测试 +22(19 无存储降级路径 + 3 真实 MinIO 全链路签名/下载),user 模块
  100 → 131

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-11 10:02:26 +08:00
lixi 8089c06a73 feat: 事件字典 v3 白名单扩充——community 域 19 事件 + experiment_exposed 字典先行(T3-20 后端,ADR-020)
CI / backend-test (push) Successful in 5m26s
- EventDictionary 增补 20 个事件(iteration-3 报告 06 §1.5 白名单原样落地):
  post 域 8(发布漏斗 started/draft_saved/publish_succeeded/failed、post_deleted、
  媒体上传三段逐文件漏斗);feed 域 2(feed_viewed 浏览段聚合曝光 + feed_load_failed);
  互动 8(like/unlike、favorite/unfavorite、comment 成败对、follow/unfollow);
  platform 域 experiment_exposed(A/B 前置 #5,M4 启用字典先行)
- 字典 javadoc 同步 v3:社区隐私红线增量(§1.3 无正文/无内容 ID/无话题名/无媒体线索)
  与 pageName v3 页面族(§6.1 收编 4 + 新增 9,键级校验不变、后端零代码)
- EventDictionaryTest:每个新事件键集边界锁定 + §1.2/§1.4/§1.6 故意不设事件
  (post_impression/post_viewed/comment_create_started/单点互动 _failed/topic_follow)
- AnalyticsIntegrationTest:feed_viewed 聚合事件入库、post_liked 白名单外
  postId 剥离(红线 2)、post_impression 拒绝(§1.2 裁定 ingest 侧锁死)
- 测试 325 → 334(+9),openapi.yaml 无需变更(events 契约对事件名开放)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-09 11:51:16 +08:00
lixi 0569585434 test: 契约冻结 v1.3.0 api 侧收尾——四模块字节级快照同步 + community/media 契约矩阵入场
CI / backend-test (push) Successful in 5m4s
- 正典 v1.3.0(doc main@f848476)字节级复制为 pet/auth/community/user 四份
  openapi-v1.3.0.yaml 快照(md5 与正典一致),删除旧 v1.2.0(守卫只认一份,
  历史由 git 承载);pet/auth 守卫期望升版 1.3.0/31 路径/43 操作/72 schemas
- CommunityContractConformanceTest:community 域 17 操作 64 单元格全响应矩阵
  (Feed/帖子/评论/互动/关注,401/403/404/409/422 各格实证,零豁免)
- MediaContractConformanceTest(user 模块):media 两步上传 2 操作 8 单元格
  全矩阵(真实 MinIO 直传,零豁免)
- ContractValidator 四副本加单分支 allOf 展平合并,修复 v1.3.0
  nullable+allOf 模式(coverImage/replyToUser)被静默跳过的校验盲区,
  定向 mutation 自证生效
- 实现与冻结契约零漂移;310 → 325 测试全绿;check-secrets --all 通过

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-09 11:31:45 +08:00
lixi 40bac85543 feat: user 域 /internal 批量公开资料接口——昵称回退/头像指针/静默缺席(D3-9 方案 B,T3-05)
- GET /internal/users/profiles?ids=…:一次最多 50 个,超限/空/非法 UUID 均 400/40000
- 仅暴露 userId/nickname/avatarAssetId;nickname→username 回退在归属侧 SQL 完成
- 不存在与已注销用户静默缺席(墓碑形态 = 消费侧 authorId 保底,不泄露成因)
- InternalAuthFilter 既有 /internal/** 共享密钥保护直接覆盖,无新安全面
- 新增 InternalProfileEndpointTest 8 例(user 模块 88→96)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-09 10:26:01 +08:00
lixi 101ac0fbbc feat: 帖子生命周期——草稿/编辑/发布/软删/详情/我的列表(ADR-019,T3-04)
CI / backend-test (push) Successful in 6m33s
- patbond-community 帖子域五端点:POST/GET/PATCH/DELETE /api/v1/posts(/{postId}) + GET /api/v1/me/posts
- 创建型幂等按 ADR-019:Idempotency-Key 必带(1~128)落 uq_posts_author_idempotency,
  规范化 request_hash 比对——同键同 hash 返回首帖、异 hash 40905、键按作者隔离
- 权限/错误语义定型(T3-10 冻结输入):403/40301 仅发给可见者,一切不可见合并
  404/40403 防枚举(hidden/archived 对作者同样 404);version 乐观锁 40902
- 发布 = PATCH 状态迁移 draft→published(publishedAt 恰写一次,重复发布幂等 no-op)
- 软删 deleted_at 为唯一判定基准,published 行归档为 archived 满足 ck_posts_publish_state
- post_media:仅本人 ready asset(同库只读 media.assets,ADR-017 先例;40405/42203),
  position 全给或全不给、isCover 至多一、封面缺省落 position 0;PATCH media 整组替换
- 读取侧图片 URL 由 community 本地预签名 GET(与 user 共用 PATBOND_MINIO_* 配置,
  compose 已注入;未配置降级 url=null)
- ErrorCode 增 40301/40403/40905/42203;异常处理补 MissingRequestHeaderException→40000
- 集成测试 +25(六类路径/幂等专项/草稿可见性矩阵/并发 PATCH 真竞争),全套 251 全绿

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-09 09:59:46 +08:00
lixi 263cd88451 test: auth 域 6 操作契约一致性测试全响应矩阵(D3-8,T3-19)
CI / backend-test (push) Failing after 8s
- 复制 pet 模块契约框架(OpenApiContract/ContractValidator + v1.2.0
  字节级快照)入 patbond-auth,沿每模块复制纪律;快照守卫防漏同步
- AuthContractConformanceTest:沿 AuthE2eIntegrationTest 编排同 JVM 真实
  拉起 user 服务,register/login/refresh/logout 打 auth、me/trackEvents
  打 user;6 操作 19 个 (操作,状态码) 单元格全矩阵零豁免(含 409 双业务
  码、423 锁定、40102 重放、me 404 幽灵用户、events 匿名 202/无效 token 401)
- 修契约测试首轮抓到的真实漂移:EventResult.reason 在 accepted/duplicate
  条目序列化出 null,契约声明仅 rejected 时出现——reason 加
  @JsonInclude(NON_NULL),既有埋点测试零回归

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-08 17:11:58 +08:00
lixi 10a43f876c feat: media 域最小闭环——存储适配层 + MinIO 预签名两步上传(ADR-016/017,T3-03)
- ObjectStorage 适配层(供应商唯一缝)+ S3ObjectStorage(AWS SDK v2,
  path-style,内网/公网双端点分离签发);endpoint 未配置时服务可启动、
  media 端点 500(沿 JWT 公钥先例)
- POST /api/v1/media/uploads:白名单校验(purpose/mime/byteSize 全配置项)
  → uploading 行(objectKey 服务端生成不含用户输入)→ 预签名 PUT 凭据
  (requiredHeaders 定型仅 Content-Type,TTL 10m)
- complete:HEAD 校验存在性/大小/类型 → uploading→ready(guarded UPDATE
  并发幂等);对象缺失保持 uploading 可重试,内容不符置 failed 终态;
  防枚举 40405;新错误码 40405/42205
- 读取侧预签名 GET(TTL 1h),桶保持私有
- compose 纳入 minio 第六容器(镜像与 Testcontainer 同 tag,桶初始化走
  应用启动 ensureBucket,三环境零分叉);init-secrets.sh 幂等追加 MinIO
  根凭证到 .env(凭证零入库,ADR-021)
- MediaUploadIntegrationTest 12 例:MinIO Testcontainer 全链路直传取回 +
  六类失败路径 + 库层约束一致性;uploading 超时清理只记方案(报告 13 §6)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-08 17:11:44 +08:00
lixi a97814ac1c feat: Flyway V5 community schema 基线 + 迁移验证集成测试(T3-01)
从目标模型 718~875 行提取 community 全部 8 表(含 topics,ADR-018 表建功能剪、
无种子进生产链);启用 pg_trgm(02 号发现 V1 漏建,trgm 索引照建);剥离 2 条
跨 schema FK:posts.generation_job_id→creation(M4 补回)、posts.region_id→
platform.regions(M5 地区体系补回),裸可空 uuid 列与索引保留,照 V3 剪
marketplace FK 先例。CommunityMigrationIntegrationTest 8 例验证结构与裁剪。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-08 16:16:16 +08:00
lixi 64c9b72fd1 feat: 事件字典 v2 白名单扩充 pet/health_record 域 10 事件(T2-17 后端)
CI / backend-test (push) Successful in 5m18s
- EventDictionary 增补 pet 域 3 事件(pet_create_started/succeeded/failed)
  与 health_record 域 7 事件(create_started/succeeded/failed、viewed、
  edit_succeeded/failed、deleted),props 键集按 06 号报告 §1.5 直抄
- page_viewed 转正为 v2 正稿:props 键集 pageName/referrer 与正稿一致,
  pageName 枚举(含 pet_form)在客户端编译期约束 + §6.4 值级巡检兜底
- 字典边界测试 5 例:三域 props 键集全矩阵、page_viewed 正稿键集、
  刻意不设事件(pet_viewed/edit_started/delete_failed)保持 unknown
- 接收端集成测试 4 例:v2 事件端到端接受、v2 白名单外 props 剥离、
  枚举外 recordType 值过 ingest 交巡检、废弃 health_record_action 仍拒绝
- 测试 182 → 191 全绿

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-08 11:30:44 +08:00
lixi 58576f8ebc refactor: 移除 EventDictionary 的 health_record_action(ADR-013)
CI / backend-test (push) Successful in 7m19s
- health_record_action 从白名单直接移除(客户端零引用,废弃零成本)
- M2 事件按字典 v2(具体事件名,iteration-2/06)随第二波接口纵切落地;
  不复活通用 actionType 设计(多套指标共享分母污染)
- v1 auth 漏斗事件 + page_viewed(遗留 §2)为当前完整白名单
- 新增 EventDictionaryTest:锁定移除后白名单边界,回归防护

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-07 14:35:31 +08:00
lixi 49299fb4eb feat: Flyway V3 pet_health 结构基线 + V4 字典种子 + pet 域错误码(T2-01)
- V3:pet_health schema 8 张表(breeds/pets/pet_owners/pet_weight_records/
  vaccine_catalog/pet_vaccinations/health_events/care_reminders),列、CHECK、
  部分唯一索引、updated_at 触发器与目标模型一致
- 强制裁剪:剥离 bootstrap SQL 1156~1166 行 4 条指向 marketplace 的跨 schema
  外键(provider_id/booking_id 保留为裸可空 uuid,M5 迁移 marketplace 时补回)
- ADR-010:health_event_media 不建(media 剪出 M2,纯增量表后续补零成本)
- V4:breeds(28 条)与 vaccine_catalog(10 条)字典种子,生产参考数据走正式
  迁移链不放 db/dev;正典目录内容由产品侧供稿(D2-6)
- ErrorCode 预置 pets 域四码:40300 PET_ACCESS_DENIED、40401 PET_NOT_FOUND、
  40402 RECORD_NOT_FOUND、40902 VERSION_CONFLICT(第二波接口直接消费)
- 新增 PetHealthMigrationIntegrationTest:干净 postgres:18 上全量迁移验证
  表数、结构抽查、4 条 FK 确不存在、触发器与种子数据

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-07 14:34:19 +08:00
lixi 6d47c5a2b6 feat: 埋点接收端落地——V2 迁移 + POST /api/v1/events 批量上报(报告 13)
CI / backend-test (push) Failing after 1m31s
- Flyway V2:platform.product_events(客户端 UUIDv7 主键即幂等键,user_id 不设外键,client_ts 合理性约束,三索引),DDL 照报告 13 §2.2
- POST /api/v1/events(报告 13 §1 OpenAPI):批量 1-50 条、202 逐条结果(accepted/duplicate/rejected)、eventId ON CONFLICT 去重、字典 v1 白名单(11 个 auth_* + 工单增补 page_viewed/health_record_action)、字典外 props 剥离计数、隐私红线字段整条拒绝(forbidden_field)、认证请求 userId 与 token subject 不一致拒绝(identity_mismatch)
- BearerAuthFilter 对 /api/v1/events 改为可选鉴权(规范:唯一允许匿名的写端点;带 token 仍严格验签 401/40101)
- 日志红线:props 内容不落日志(仅计数与字段名告警)
- 门禁:./mvnw clean test → BUILD SUCCESS,82 测试 0 失败(新增 AnalyticsIntegrationTest 7 例:V2 生效/匿名落库/未知事件拒绝/去重/剥离/红线拒绝/参数 40000)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-04 17:09:00 +08:00
lixi 6528a06c28 feat: auth_sessions 死亡行定时清理任务(报告 16 遗留 §9.3)
CI / backend-test (push) Failing after 8m10s
- SessionCleanupJob(@Scheduled,间隔/初始延迟/保留期均配置项,默认 6h/10m/30d)删除撤销或过期超过保留期的会话行;保留期即重用检测窗口(已撤销行是重用比对对象),注释与 sample 已说明
- 多实例并发安全(幂等 DELETE);轮换链 FK 由 ON DELETE SET NULL 释放
- 门禁:./mvnw clean test → BUILD SUCCESS,75 测试 0 失败(新增 SessionCleanupIntegrationTest:仅删超期死亡行,存活与近期撤销行保留)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-04 15:45:38 +08:00
lixi ab0265c17b feat: Docker Compose 最小编排——postgres:18 + 双无状态服务容器(ADR-007)
- 新增 docker-compose.yml、两服务 Dockerfile(eclipse-temurin:17-jre、非 root、仅装 exec jar)与 deploy/init-secrets.sh(幂等生成 RS256 密钥对与 .env 随机机密,产物入 .gitignore)
- 容器配置复用 application.yml.sample(SPRING_CONFIG_LOCATION 挂载)+ PATBOND_* 环境变量,与本机运行同一套约定;DB 端口不对宿主机发布
- spring-boot-maven-plugin 显式绑定 repackage(本工程无 starter-parent,此前 package 产物不可执行),exec classifier 保留普通 jar 供 auth 模块 E2E 依赖
- 验收:docker compose up -d --build 后完整冒烟通过(register→me→refresh→旧 token 重用 40102→/internal 无凭证 401→logout),down -v 无残留;./mvnw clean test 全绿 74 测试
- README 新增 Docker Compose 一节

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-04 14:51:34 +08:00
lixi 8bdaf53222 feat: 会话记录接入客户端 X-Device-Id(auth_sessions.device_id)
- Flutter 端每次请求已携带 X-Device-Id;auth 读取该头(截断 128)经 CreateSessionRequest 透传,user 落 auth_sessions.device_id,为多设备会话列表备数据
- 门禁:./mvnw clean test → BUILD SUCCESS,74 测试 0 失败(新增 registerForwardsDeviceIdHeaderToTheSessionRecord + 会话落库断言)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-04 14:43:44 +08:00
lixi 4dc3dcdfa3 feat: JWT RS256 + refresh 会话轮换与 /api/v1 契约落地(ADR-003)
- 会话逻辑下沉 patbond-user(新 /internal/sessions;auth_sessions 只存 SHA-256 摘要,刷新即轮换并链 token_family,重用撤销整个 family,退出仅撤当前会话,多设备并行)
- patbond-auth 作薄入口签发 RS256 JWT(access 15m / refresh 30d 均为配置项;密钥经环境变量注入,仓库零密钥材料,测试密钥运行时生成);公开端点迁至 /api/v1,冻结契约字段零偏差,expiresAt 无时区遗留修复
- /internal/** 加 X-Internal-Token 服务间鉴权(无凭证 401);/api/v1/me 由 user 以公钥本地验签(40101/40102 新错误码)
- 登录失败限制:按用户名 15 分钟窗口 5 次锁 15 分钟(423/42300,DB 原子计数,可配置)
- 修复两处存量缺陷:ErrorDecoder 未注册进 Feign 子上下文、JDK HttpURLConnection 对流式 POST 的 401 读不到错误体(引入 feign-hc5)——真实调用中下游错误码此前一律折叠为 503
- 门禁:JAVA_HOME=/usr/lib/jvm/java-17-openjdk ./mvnw clean test → BUILD SUCCESS,73 测试 0 失败(37→73),含同 JVM 双服务真实 HTTP E2E:注册→me→刷新→旧 refresh 重用被拒且 family 撤销→退出后 refresh 失效;Testcontainers postgres:18,无遗留容器

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-04 12:15:40 +08:00
lixi 43ab6c5827 test: Testcontainers 数据库镜像升级为 postgres:18(ADR-008)
- 验收:JAVA_HOME=jdk17 ./mvnw clean test 全量 37 测试 0 失败,Flyway V1 于 PostgreSQL 18.6 执行无兼容问题
2026-09-04 11:34:08 +08:00
lixi bd20adc700 feat: 用户 UUID 持久化与统一异常契约(Flyway baseline / UUIDv7 / 错误码透传)
- Flyway V1 baseline:identity 全部 5 表 + media.assets + platform 最小硬依赖(set_updated_at/regions),无 fixture 凭据;dev 种子独立为 afterMigrate 回调且默认不执行
- patbond-user 迁移到 PostgreSQL:UUIDv7 主键、JdbcClient 仓储、bcrypt 密码、唯一性依赖 DB 约束翻译为 409、E.164 手机号校验对齐 ck_users_phone
- 统一异常契约:common 新增 ErrorCode/BusinessException,两服务 GlobalExceptionHandler;auth 经 ApiErrorDecoder 原码透传下游错误,修复状态码折叠(审计 M1)
- 门禁:JAVA_HOME=jdk17 ./mvnw clean test,37 个测试 0 失败(含 Testcontainers postgres:16 集成测试),BUILD SUCCESS

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-04 10:34:48 +08:00
lixi c7ddaecb76 重构底层框架 2026-09-03 17:59:56 +08:00
nmbh1122@126.com 5a52b44d46 chore: move local configs to sample files 2026-07-20 16:22:54 +08:00
nmbh1122@126.com 8cfc574247 feat(update):更新nacos配置文件 2026-07-20 14:56:05 +08:00
nmbh1122@126.com c43f30175d Ignore build outputs 2026-07-13 09:41:33 +08:00
nmbh1122@126.com b1252b991d Initialize patbond microservice modules 2026-07-13 09:41:15 +08:00