Skip to content

Commit 6f45938

Browse files
committed
feat: add new status payment_failure
1 parent e09eb5b commit 6f45938

File tree

3 files changed

+71
-142
lines changed

3 files changed

+71
-142
lines changed

README.md

Lines changed: 68 additions & 141 deletions
Original file line numberDiff line numberDiff line change
@@ -1,141 +1,68 @@
1-
# Ezclaim
2-
3-
A Spring Boot service for managing claims with MongoDB storage, S3-compatible object storage for photos, and a clean MVC/API structure. Includes Bruno API tests and dev tooling for quick startup.
4-
5-
## Tech Stack
6-
- Spring Boot 3.5.x (Java 21+/24 target)
7-
- MongoDB (Spring Data Mongo)
8-
- S3-compatible object storage via AWS SDK v2 (works with AWS S3, MinIO, etc.)
9-
- Messaging: Spring Cloud Stream + Kafka (Redpanda in dev)
10-
- Security: Spring Security OAuth2 Resource Server (JWT HS256)
11-
- Build: Maven Wrapper (`./mvnw`)
12-
- API tests: Bruno (`.bru` files under `bruno/`)
13-
14-
## Quick Start (Dev)
15-
1) Prereqs: Docker, Java 21+ (build is set to Java 24), optional direnv.
16-
2) Start dev services (MongoDB 8 + MinIO + Redpanda/Kafka):
17-
- `docker compose -f docker-compose.dev.yml up -d`
18-
3) Load project env (dev profile):
19-
- Install direnv, then in repo root: `direnv allow` (loads `.envrc` => `SPRING_PROFILES_ACTIVE=dev`).
20-
4) Run the app:
21-
- `./mvnw spring-boot:run`
22-
23-
Dev URLs
24-
- API base: `http://localhost:8080`
25-
- MinIO Console: `http://localhost:9001`
26-
- S3 endpoint: `http://localhost:9000`
27-
- Mongo: `mongodb://...` from `application-dev.yml`
28-
- Swagger UI: `http://localhost:8080/swagger-ui/index.html`
29-
- OpenAPI JSON: `http://localhost:8080/v3/api-docs`
30-
- OpenAPI YAML: `http://localhost:8080/v3/api-docs.yaml`
31-
32-
Generate static OpenAPI YAML
33-
- Start the app locally on port 8080, then run:
34-
- `./mvnw -Popenapi -DskipTests springdoc-openapi:generate`
35-
- Output: `target/openapi.yaml`
36-
37-
## Configuration
38-
Spring profiles are used to separate dev and prod configuration:
39-
- `src/main/resources/application-dev.yml` (local dev)
40-
- `src/main/resources/application-prod.yml` (deployment)
41-
42-
Mongo (dev)
43-
- `spring.data.mongodb.uri=mongodb://ezclaim:E2Claim@localhost:27017/ezclaim?authSource=admin`
44-
- If authentication fails, clear the docker volume or align the URI with the existing root user in your volume.
45-
46-
Object Store (generic S3)
47-
- Properties prefix: `app.objectstore.*`
48-
- `endpoint`: omit for AWS S3; set to `http://localhost:9000` for MinIO dev
49-
- `region`: e.g. `us-east-1`
50-
- `access-key`, `secret-key`: credentials
51-
- `bucket`: default bucket name
52-
- `path-style`: `true` for S3-compatible services like MinIO
53-
- `ensure-bucket`: set `true` in dev to auto-create; keep `false` in prod
54-
55-
Prod environment variables expected (see `application-prod.yml`):
56-
- `SPRING_DATA_MONGODB_URI`
57-
- `KAFKA_BOOTSTRAP_SERVERS`
58-
- `APP_OBJECTSTORE_ENDPOINT` (omit for AWS S3)
59-
- `APP_OBJECTSTORE_REGION`
60-
- `APP_OBJECTSTORE_ACCESS_KEY`
61-
- `APP_OBJECTSTORE_SECRET_KEY`
62-
- `APP_OBJECTSTORE_BUCKET`
63-
- `APP_OBJECTSTORE_PATH_STYLE`
64-
- `APP_OBJECTSTORE_ENSURE_BUCKET`
65-
- `APP_JWT_SECRET` (required for JWT HS256)
66-
- `APP_JWT_ALG` (optional, default `HS256`)
67-
- `APP_JWT_TTL` (optional, ISO-8601 duration, default `PT12H`)
68-
69-
## Domain & API
70-
Entities
71-
- Claim: `title`, `description`, `status`, `createdAt`, `updatedAt`, references to `photos[]`, `tags[]` (stored separately)
72-
- Photo: `bucket`, `key`, `uploadedAt` (S3 metadata)
73-
- Tag: `label`, `color`
74-
75-
Key Endpoints (REST)
76-
- Tags: `GET/POST/PUT/DELETE /api/tags[/id]`
77-
- Photos:
78-
- `POST /api/photos/presign-upload` → returns presigned PUT URL
79-
- `POST /api/photos` → create a Photo record (after you upload)
80-
- `GET /api/photos/{id}/download-url` → presigned GET URL
81-
- `GET/DELETE /api/photos[/id]`
82-
- Claims: `GET/POST/PUT/DELETE /api/claims[/id]` (accepts `photoIds[]` and `tagIds[]`)
83-
- Audit Events:
84-
- `GET /api/audit-events` (filters: `entityType`, `entityId`, `action`, `from`, `to`; paging `page`, `size`; sorting `sort=field,asc|desc`)
85-
- `GET /api/audit-events/{id}`
86-
87-
Auth (for Audit Events)
88-
- `POST /api/auth/login` with JSON `{ "username": "admin", "password": "ezclaim-password" }`
89-
- Returns `{ token, tokenType: "Bearer", expiresAt }`
90-
- Include header `Authorization: Bearer <token>` for all `/api/audit-events/**` requests.
91-
92-
Security notes
93-
- Uses standard JWT (HS256) via Spring Security OAuth2 Resource Server.
94-
- Dev secret and TTL are configured in `application-dev.yml` under `app.security.jwt.*`.
95-
- In prod, set `APP_JWT_SECRET` (and optionally `APP_JWT_TTL`). Plan for secret rotation and a shorter TTL.
96-
97-
Built-in Users (in-memory)
98-
- Anonymous (no token):
99-
- Can read claims: `GET /api/claims`, `GET /api/claims/{id}` (response includes related photos/tags).
100-
- `reader` / `reader-pass`:
101-
- Scopes: `AUDIT`, `TAG_READ`, `PHOTO_READ`, `CLAIM_READ`
102-
- Can: read audit events; read tags; read photos; read all claims.
103-
- `admin` / `ezclaim-password`:
104-
- Scopes: all of reader plus `CLAIM_WRITE`, `TAG_WRITE`, `PHOTO_DELETE`, `PHOTO_WRITE`
105-
- Can: update/create/delete claims; CRUD tags; delete photos; presign upload & create photo record.
106-
107-
Endpoint Authorization Summary
108-
- Audit: `/api/audit-events/**` → requires `SCOPE_AUDIT`
109-
- Claims:
110-
- `GET /api/claims``SCOPE_CLAIM_READ`
111-
- `GET /api/claims/{id}` → public
112-
- `POST/PUT/DELETE /api/claims[/id]``SCOPE_CLAIM_WRITE`
113-
- Tags:
114-
- `GET /api/tags[/id]``SCOPE_TAG_READ`
115-
- `POST/PUT/DELETE /api/tags[/id]``SCOPE_TAG_WRITE`
116-
- Photos:
117-
- `GET /api/photos[/id]`, `GET /api/photos/{id}/download-url``SCOPE_PHOTO_READ`
118-
- `DELETE /api/photos/{id}``SCOPE_PHOTO_DELETE`
119-
- `POST /api/photos/presign-upload`, `POST /api/photos``SCOPE_PHOTO_WRITE`
120-
121-
## Bruno API Tests
122-
- Collection root: `bruno/`
123-
- Environments: `bruno/environments/dev.bru`, `bruno/environments/prod.bru` (uses `baseUrl`)
124-
- Requests grouped in `bruno/Auth`, `bruno/Tags`, `bruno/Photos`, `bruno/Claims`, `bruno/Audit Events`
125-
- Open the `bruno/` folder in Bruno, choose an environment, then run tests in order (Auth → Tags → Photos → Claims → Audit Events). The Audit Events folder includes list, paginated list, filter example (with default variables), and get-by-id. The list/filter scripts capture the first event id to `{{auditEventId}}` for convenience.
126-
127-
Audit Events pipeline
128-
- Change events are published to Kafka topic `audit.events` (binding `auditEvents-out-0`) by `MongoChangePublisher`.
129-
- The Consumer function `auditEvents` persists them to Mongo (binding `auditEvents-in-0`).
130-
- Dev uses Redpanda on `localhost:9092`; adjust `KAFKA_BOOTSTRAP_SERVERS` as needed.
131-
132-
## Build & Test
133-
- Unit tests: `./mvnw test`
134-
- Package: `./mvnw -DskipTests package`
135-
136-
## Notes
137-
- If the app starts before MinIO, bucket provisioning waits up to ~60s with retries.
138-
- For Mongo auth errors in dev, the most common cause is a reused volume created with different credentials; reset the volume or align the URI.
139-
140-
## License
141-
This project is licensed under the WTFPL. See `LICENCE` for details.
1+
# EzClaim 管理后台
2+
3+
Next.js 14 构建的 EzClaim 管理端,提供管理员登录、标签管理、报销单审批以及审计事件查询等能力。后端接口基于项目根目录的 `api.json` (OpenAPI 3.1)。
4+
5+
## 功能亮点
6+
7+
- 🔐 管理员登录,使用 JWT 存储于 HttpOnly Cookie
8+
- 🏷️ 标签管理:创建、查看、删除标签
9+
- 💼 报销单管理:支持搜索、筛选、排序,状态流转遵守服务端规则
10+
- 📊 实时概览:按状态统计报销单数量
11+
- 🛠️ 审计事件查询:多条件过滤、分页展示、详细 JSON 查看
12+
13+
## 快速开始
14+
15+
```bash
16+
npm install
17+
npm run dev
18+
```
19+
20+
默认后端地址为 `http://localhost:8080`,如需修改可在启动前设置:
21+
22+
```bash
23+
export API_BASE_URL="http://your-backend:8080"
24+
```
25+
26+
或在 `.env.local` 中声明 `API_BASE_URL`/`NEXT_PUBLIC_API_BASE_URL`
27+
28+
## 运行前提
29+
30+
- Node.js 18+
31+
- 后端服务(默认端口 8080,可通过 `api.json` 查看接口定义)
32+
- Demo 账户:`admin / ezclaim-password``reader / reader-pass`
33+
34+
## 脚本
35+
36+
| 命令 | 说明 |
37+
| ------------ | -------------------- |
38+
| `npm run dev` | 开发模式 (`localhost:3000`) |
39+
| `npm run build` | 生产构建 |
40+
| `npm run start` | 启动生产环境服务器 |
41+
| `npm run lint` | 执行 ESLint 检查 |
42+
43+
## 目录结构
44+
45+
```
46+
app/ # App Router 路由
47+
(auth)/login # 登录页
48+
(dashboard)/ # 受保护的业务页面
49+
components/ # UI 组件与布局
50+
lib/ # API 客户端、配置、工具函数
51+
middleware.ts # 保护路由的中间件
52+
```
53+
54+
## 认证机制
55+
56+
- 登录后通过服务端调用 `/api/auth/login` 获取 JWT
57+
- Token 以 HttpOnly Cookie (`ezclaim_token`) 形式存储
58+
- `middleware.ts` 拦截未登录访问并重定向至 `/login`
59+
60+
## 注意事项
61+
62+
- 所有与后端交互的操作使用 Next.js Server Actions,并在成功后 `router.refresh()` + `revalidatePath`
63+
- 报销单状态流转遵循后端 `ClaimService` 的约束:
64+
- `SUBMITTED → APPROVED/REJECTED`
65+
- `APPROVED → PAID/REJECTED`
66+
- `Audit Events` 页面采用 GET 参数驱动,可直接分享链接复现查询条件
67+
68+
欢迎根据业务需要扩展更多管理能力,例如报销单详情编辑、批量操作等。

src/main/java/org/acssz/ezclaim/domain/ClaimStatus.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,6 @@ public enum ClaimStatus {
77
PAID, // admin: APPROVED -> PAID
88
FINISHED, // accessible user: PAID -> FINISHED
99
REJECTED, // admin: from any (except FINISHED, WITHDRAW) -> REJECTED
10+
PAYMENT_FAILED, // admin: from APPROVED -> PAYMENT_FAILED
1011
WITHDRAW // accessible user: SUBMITTED -> WITHDRAW
1112
}

src/main/java/org/acssz/ezclaim/service/ClaimService.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,8 @@ private boolean isAdminTransitionAllowed(ClaimStatus from, ClaimStatus to) {
164164
return from != ClaimStatus.FINISHED && from != ClaimStatus.WITHDRAW;
165165
}
166166
return (from == ClaimStatus.SUBMITTED && to == ClaimStatus.APPROVED)
167-
|| (from == ClaimStatus.APPROVED && to == ClaimStatus.PAID);
167+
|| (from == ClaimStatus.APPROVED
168+
&& (to == ClaimStatus.PAID || to == ClaimStatus.PAYMENT_FAILED));
168169
}
169170

170171
private boolean isUserTransitionAllowed(ClaimStatus from, ClaimStatus to) {

0 commit comments

Comments
 (0)