Files
patbond-api/Readme.md
T
lixi 0eae1c9bec feat: 新建 patbond-pet 模块骨架(ADR-009,T2-02 前置)
- 新 Maven 模块挂入父 pom,依赖/插件管理对齐既有 user/auth 模式
  (common 依赖、starter-web/validation/jdbc、exec classifier repackage)
- 骨架内容:PetApplication、/health 探活端点(含 DB 连通检查)、
  GlobalExceptionHandler(同一 {code,message,data} 信封契约)
- 与 user 共库只读写 pet_health schema;不携带 Flyway——单一迁移链
  (V1..V4)仍由 patbond-user 启动时统一执行,flyway_schema_history 不拆
- 配置走 .sample 模式(默认端口 8083,敏感信息经环境变量注入不入库)
- compose 编排纳入 pet 服务(依赖 postgres 健康 + user 先起保证迁移就绪);
  Dockerfile 与既有服务同模式;Readme 模块清单同步
- 测试:Testcontainers postgres:18 上下文启动冒烟 + /health 探活断言

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-07 14:34:52 +08:00

167 lines
6.8 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Patbond API
Patbond API is a Spring Boot multi-module backend.
## Modules
- `patbond-common`: stable cross-module contracts (response wrapper, shared DTOs). No web/messaging dependencies.
- `patbond-user`: user service for user creation, profile lookup, and password verification.
- `patbond-auth`: authentication service for registration and login. Calls `patbond-user` over HTTP via OpenFeign with a configured static URL (no service discovery in the MVP, see ADR-002).
- `patbond-pet`: pet profile and health record service (M2, ADR-009). Shares the database with `patbond-user` and only reads/writes the `pet_health` schema; the Flyway migration chain stays owned by `patbond-user`. First-wave skeleton: liveness endpoint only.
## Technology Stack
- Java 17 (build baseline; use JDK 17 for release builds)
- Spring Boot 3.5.16
- Spring Cloud 2025.0.3 (OpenFeign only)
- PostgreSQL 18 + Flyway (patbond-user owns the migration chain: `identity`/`media`/`platform`/`pet_health` schemas)
- Maven (use the committed Maven Wrapper `./mvnw`)
## Build and Test
```bash
./mvnw clean test
```
Integration tests start a disposable `postgres:18` via Testcontainers, so a
running Docker daemon is required (no local PostgreSQL installation or
credentials are needed).
If your default JDK is not 17, point `JAVA_HOME` at a JDK 17 installation first, e.g.:
```bash
JAVA_HOME=/usr/lib/jvm/java-17-openjdk ./mvnw clean test
```
## Run
Running `patbond-user` requires a reachable PostgreSQL 18 database; Flyway
applies the versioned migrations in
`patbond-user/src/main/resources/db/migration` automatically on startup.
Development seed data (`db/dev`, regions reference rows) is opt-in via a dev
profile — see `application.yml.sample`.
Each service ships a committed `application.yml.sample`; the real `application.yml`
is git-ignored. First copy the samples (defaults work locally, overrides via
environment variables):
```bash
cp patbond-user/src/main/resources/application.yml.sample \
patbond-user/src/main/resources/application.yml
cp patbond-auth/src/main/resources/application.yml.sample \
patbond-auth/src/main/resources/application.yml
cp patbond-pet/src/main/resources/application.yml.sample \
patbond-pet/src/main/resources/application.yml
```
Install the shared module once, then run each service in its own terminal:
```bash
./mvnw -pl patbond-common install
./mvnw -pl patbond-user spring-boot:run
./mvnw -pl patbond-auth spring-boot:run
```
Running the services also needs an RS256 key pair for access tokens (the
private key signs in `patbond-auth`, the public key verifies in
`patbond-user`; neither is committed):
```bash
openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -out jwt-private.pem
openssl pkey -in jwt-private.pem -pubout -out jwt-public.pem
export PATBOND_JWT_PRIVATE_KEY=$PWD/jwt-private.pem # patbond-auth
export PATBOND_JWT_PUBLIC_KEY=$PWD/jwt-public.pem # patbond-user
```
Tests do not require this copy step — `patbond-auth/src/test/resources/application.yml`
keeps `./mvnw clean test` self-contained on a clean checkout.
Smoke check:
```bash
curl -X POST http://127.0.0.1:8081/api/v1/auth/register \
-H 'Content-Type: application/json' \
-d '{"username":"demo_user","password":"secret123"}'
```
### Configuration
| Environment variable | Default | Used by |
| --- | --- | --- |
| `PATBOND_USER_PORT` | `8082` | patbond-user |
| `PATBOND_AUTH_PORT` | `8081` | patbond-auth |
| `PATBOND_USER_SERVICE_URL` | `http://127.0.0.1:8082` | patbond-auth (Feign target for patbond-user) |
| `PATBOND_DB_URL` | `jdbc:postgresql://127.0.0.1:5432/patbond` | patbond-user |
| `PATBOND_DB_USER` | `patbond` | patbond-user |
| `PATBOND_DB_PASSWORD` | `patbond` | patbond-user |
| `PATBOND_INTERNAL_TOKEN` | `dev-only-internal-token` | both (shared secret for `/internal/**`; inject a strong random value outside local dev) |
| `PATBOND_JWT_PRIVATE_KEY` | _(none, required)_ | patbond-auth (RS256 private key: PEM path or inline PEM) |
| `PATBOND_JWT_PUBLIC_KEY` | _(none)_ | patbond-user (RS256 public key: PEM path or inline PEM) |
| `PATBOND_ACCESS_TTL` | `15m` | patbond-auth (access token lifetime, ADR-003) |
| `PATBOND_REFRESH_TTL` | `30d` | patbond-user (refresh token lifetime, ADR-003) |
| `PATBOND_LOGIN_LOCK_MAX_FAILURES` | `5` | patbond-user (login failures before lockout) |
| `PATBOND_LOGIN_LOCK_WINDOW` | `15m` | patbond-user (failure counting window) |
| `PATBOND_LOGIN_LOCK_DURATION` | `15m` | patbond-user (lock duration) |
Machine-specific values live in the git-ignored `application.yml` (copied from the
committed `.sample`); never commit secrets to the samples.
## Docker Compose
MVP 编排(ADR-007):`postgres:18`(数据落 volume)+ 两个无状态应用容器。
配置与本机运行同一套约定 —— 容器内挂载 `application.yml.sample` 作为配置,
`PATBOND_*` 环境变量注入实际值。
```bash
# 1. 生成 RS256 密钥对与 .env(DB 口令、内部令牌;产物被 .gitignore 忽略)
./deploy/init-secrets.sh
# 2. 构建可执行 jar
JAVA_HOME=/usr/lib/jvm/java-17-openjdk ./mvnw -DskipTests package
# 3. 启动(首次会构建镜像)
docker compose up -d --build
# 冒烟
curl -s -X POST http://127.0.0.1:8081/api/v1/auth/register \
-H 'Content-Type: application/json' \
-d '{"username":"demo_user","password":"secret123"}'
# 停止(-v 同时删除数据库数据)
docker compose down
```
注意:数据库端口不对宿主机发布;`8082`(user)在 MVP 阶段直连暴露以提供
`/api/v1/me``/internal/**` 由服务间令牌保护,规模化阶段应改由网关统一入口。
## Services
### Auth Service
- Application name: `patbond-auth`
- Default port: `8081`
- Register: `POST /api/v1/auth/register`
- Login: `POST /api/v1/auth/login`
- Refresh (rotates the refresh token): `POST /api/v1/auth/refresh`
- Logout (revokes the current session): `POST /api/v1/auth/logout`
### User Service
- Application name: `patbond-user`
- Default port: `8082`
- Current user profile: `GET /api/v1/me` (Bearer access token, verified locally with the RS256 public key)
- Internal (require `X-Internal-Token`):
- Create user: `POST /internal/users`
- Verify password: `POST /internal/users/verify-password`
- Get user by id: `GET /internal/users/{id}`
- Get user by username: `GET /internal/users/by-username/{username}`
- Sessions: `POST /internal/sessions`, `POST /internal/sessions/refresh`, `POST /internal/sessions/revoke`
> Note: user data is persisted in PostgreSQL (`identity.users` /
> `identity.user_credentials`, bcrypt password hashes, UUIDv7 ids generated in
> the application). Refresh sessions live in `identity.auth_sessions` (SHA-256
> digests only, rotation on every refresh, token-family revocation on reuse,
> ADR-003). Errors follow the `{code, message, data}` envelope with stable
> business codes and matching HTTP statuses; the public contract is documented
> in `patbond-doc/docs/api/openapi.yaml`.