移除多餘文件
This commit is contained in:
@@ -1,23 +0,0 @@
|
||||
---
|
||||
name: OpenSpec: Apply
|
||||
description: Implement an approved OpenSpec change and keep tasks in sync.
|
||||
category: OpenSpec
|
||||
tags: [openspec, apply]
|
||||
---
|
||||
<!-- OPENSPEC:START -->
|
||||
**Guardrails**
|
||||
- Favor straightforward, minimal implementations first and add complexity only when it is requested or clearly required.
|
||||
- Keep changes tightly scoped to the requested outcome.
|
||||
- Refer to `openspec/AGENTS.md` (located inside the `openspec/` directory—run `ls openspec` or `openspec update` if you don't see it) if you need additional OpenSpec conventions or clarifications.
|
||||
|
||||
**Steps**
|
||||
Track these steps as TODOs and complete them one by one.
|
||||
1. Read `changes/<id>/proposal.md`, `design.md` (if present), and `tasks.md` to confirm scope and acceptance criteria.
|
||||
2. Work through tasks sequentially, keeping edits minimal and focused on the requested change.
|
||||
3. Confirm completion before updating statuses—make sure every item in `tasks.md` is finished.
|
||||
4. Update the checklist after all work is done so each task is marked `- [x]` and reflects reality.
|
||||
5. Reference `openspec list` or `openspec show <item>` when additional context is required.
|
||||
|
||||
**Reference**
|
||||
- Use `openspec show <id> --json --deltas-only` if you need additional context from the proposal while implementing.
|
||||
<!-- OPENSPEC:END -->
|
||||
@@ -1,27 +0,0 @@
|
||||
---
|
||||
name: OpenSpec: Archive
|
||||
description: Archive a deployed OpenSpec change and update specs.
|
||||
category: OpenSpec
|
||||
tags: [openspec, archive]
|
||||
---
|
||||
<!-- OPENSPEC:START -->
|
||||
**Guardrails**
|
||||
- Favor straightforward, minimal implementations first and add complexity only when it is requested or clearly required.
|
||||
- Keep changes tightly scoped to the requested outcome.
|
||||
- Refer to `openspec/AGENTS.md` (located inside the `openspec/` directory—run `ls openspec` or `openspec update` if you don't see it) if you need additional OpenSpec conventions or clarifications.
|
||||
|
||||
**Steps**
|
||||
1. Determine the change ID to archive:
|
||||
- If this prompt already includes a specific change ID (for example inside a `<ChangeId>` block populated by slash-command arguments), use that value after trimming whitespace.
|
||||
- If the conversation references a change loosely (for example by title or summary), run `openspec list` to surface likely IDs, share the relevant candidates, and confirm which one the user intends.
|
||||
- Otherwise, review the conversation, run `openspec list`, and ask the user which change to archive; wait for a confirmed change ID before proceeding.
|
||||
- If you still cannot identify a single change ID, stop and tell the user you cannot archive anything yet.
|
||||
2. Validate the change ID by running `openspec list` (or `openspec show <id>`) and stop if the change is missing, already archived, or otherwise not ready to archive.
|
||||
3. Run `openspec archive <id> --yes` so the CLI moves the change and applies spec updates without prompts (use `--skip-specs` only for tooling-only work).
|
||||
4. Review the command output to confirm the target specs were updated and the change landed in `changes/archive/`.
|
||||
5. Validate with `openspec validate --strict` and inspect with `openspec show <id>` if anything looks off.
|
||||
|
||||
**Reference**
|
||||
- Use `openspec list` to confirm change IDs before archiving.
|
||||
- Inspect refreshed specs with `openspec list --specs` and address any validation issues before handing off.
|
||||
<!-- OPENSPEC:END -->
|
||||
@@ -1,28 +0,0 @@
|
||||
---
|
||||
name: OpenSpec: Proposal
|
||||
description: Scaffold a new OpenSpec change and validate strictly.
|
||||
category: OpenSpec
|
||||
tags: [openspec, change]
|
||||
---
|
||||
<!-- OPENSPEC:START -->
|
||||
**Guardrails**
|
||||
- Favor straightforward, minimal implementations first and add complexity only when it is requested or clearly required.
|
||||
- Keep changes tightly scoped to the requested outcome.
|
||||
- Refer to `openspec/AGENTS.md` (located inside the `openspec/` directory—run `ls openspec` or `openspec update` if you don't see it) if you need additional OpenSpec conventions or clarifications.
|
||||
- Identify any vague or ambiguous details and ask the necessary follow-up questions before editing files.
|
||||
- Do not write any code during the proposal stage. Only create design documents (proposal.md, tasks.md, design.md, and spec deltas). Implementation happens in the apply stage after approval.
|
||||
|
||||
**Steps**
|
||||
1. Review `openspec/project.md`, run `openspec list` and `openspec list --specs`, and inspect related code or docs (e.g., via `rg`/`ls`) to ground the proposal in current behaviour; note any gaps that require clarification.
|
||||
2. Choose a unique verb-led `change-id` and scaffold `proposal.md`, `tasks.md`, and `design.md` (when needed) under `openspec/changes/<id>/`.
|
||||
3. Map the change into concrete capabilities or requirements, breaking multi-scope efforts into distinct spec deltas with clear relationships and sequencing.
|
||||
4. Capture architectural reasoning in `design.md` when the solution spans multiple systems, introduces new patterns, or demands trade-off discussion before committing to specs.
|
||||
5. Draft spec deltas in `changes/<id>/specs/<capability>/spec.md` (one folder per capability) using `## ADDED|MODIFIED|REMOVED Requirements` with at least one `#### Scenario:` per requirement and cross-reference related capabilities when relevant.
|
||||
6. Draft `tasks.md` as an ordered list of small, verifiable work items that deliver user-visible progress, include validation (tests, tooling), and highlight dependencies or parallelizable work.
|
||||
7. Validate with `openspec validate <id> --strict` and resolve every issue before sharing the proposal.
|
||||
|
||||
**Reference**
|
||||
- Use `openspec show <id> --json --deltas-only` or `openspec show <spec> --type spec` to inspect details when validation fails.
|
||||
- Search existing requirements with `rg -n "Requirement:|Scenario:" openspec/specs` before writing new ones.
|
||||
- Explore the codebase with `rg <keyword>`, `ls`, or direct file reads so proposals align with current implementation realities.
|
||||
<!-- OPENSPEC:END -->
|
||||
16
.mcp.json
16
.mcp.json
@@ -1,16 +0,0 @@
|
||||
{
|
||||
"mcpServers": {
|
||||
"playwright": {
|
||||
"type": "stdio",
|
||||
"command": "npx",
|
||||
"args": [
|
||||
"@playwright/mcp@latest"
|
||||
],
|
||||
"env": {}
|
||||
},
|
||||
"vscode-lsp": {
|
||||
"type": "stdio",
|
||||
"command": "./mcp-lsp-proxy.sh"
|
||||
}
|
||||
}
|
||||
}
|
||||
18
AGENTS.md
18
AGENTS.md
@@ -1,18 +0,0 @@
|
||||
<!-- OPENSPEC:START -->
|
||||
# OpenSpec Instructions
|
||||
|
||||
These instructions are for AI assistants working in this project.
|
||||
|
||||
Always open `@/openspec/AGENTS.md` when the request:
|
||||
- Mentions planning or proposals (words like proposal, spec, change, plan)
|
||||
- Introduces new capabilities, breaking changes, architecture shifts, or big performance/security work
|
||||
- Sounds ambiguous and you need the authoritative spec before coding
|
||||
|
||||
Use `@/openspec/AGENTS.md` to learn:
|
||||
- How to create and apply change proposals
|
||||
- Spec format and conventions
|
||||
- Project structure and guidelines
|
||||
|
||||
Keep this managed block so 'openspec update' can refresh the instructions.
|
||||
|
||||
<!-- OPENSPEC:END -->
|
||||
18
CLAUDE.md
18
CLAUDE.md
@@ -1,18 +0,0 @@
|
||||
<!-- OPENSPEC:START -->
|
||||
# OpenSpec Instructions
|
||||
|
||||
These instructions are for AI assistants working in this project.
|
||||
|
||||
Always open `@/openspec/AGENTS.md` when the request:
|
||||
- Mentions planning or proposals (words like proposal, spec, change, plan)
|
||||
- Introduces new capabilities, breaking changes, architecture shifts, or big performance/security work
|
||||
- Sounds ambiguous and you need the authoritative spec before coding
|
||||
|
||||
Use `@/openspec/AGENTS.md` to learn:
|
||||
- How to create and apply change proposals
|
||||
- Spec format and conventions
|
||||
- Project structure and guidelines
|
||||
|
||||
Keep this managed block so 'openspec update' can refresh the instructions.
|
||||
|
||||
<!-- OPENSPEC:END -->
|
||||
@@ -1,29 +0,0 @@
|
||||
1. 系統架構 (System Architecture)
|
||||
採用三層式架構 (3-Tier Architecture) 以確保未來擴展性(如未來可對接 MES/ERP 數據)。
|
||||
|
||||
前端 (Presentation Layer): React.js 或 Vue.js (推薦 React,其生態系在處理複雜管理後台較成熟)。
|
||||
|
||||
後端 (Application Layer): Python (FastAPI) ;Python 方便未來導入 AI 風險預測與數據分析。
|
||||
|
||||
數據層 (Data Layer): * 主資料庫: mysql (關聯式結構最適合處理專案間的複雜層級與相依性)。
|
||||
|
||||
快取與即時通訊: Redis (用於即時通知推播與任務狀態鎖定)。
|
||||
|
||||
2. 核心數據模型設計 (ERD Concept)
|
||||
User: ID, Name, Department, Role, Skills, Capacity.
|
||||
|
||||
Project: ID, Title, Owner, Budget, Timeline, Security_Level.
|
||||
|
||||
Task: ID, Project_ID, Assignee, Priority, Status, Original_Estimate, Time_Spent, Blocker_Flag.
|
||||
|
||||
Attachment: ID, Task_ID, Version, File_Path (支援地端 NAS 存儲)。
|
||||
|
||||
3. 關鍵技術功能設計
|
||||
即時同步 (Real-time Sync): 使用 WebSocket,當多人同時編輯同一個專案看板時,狀態能即時更新而不需刷新頁面。
|
||||
|
||||
文件加密: 針對半導體敏感圖檔,存儲層進行 AES-256 加密,且下載時自動加上使用者浮水印。
|
||||
|
||||
整合介面 (Integrations):
|
||||
|
||||
SSO: 整合企業內部 Windows AD。(使用https://pj-auth-api.vercel.app)
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Add local bin to PATH
|
||||
export PATH="$HOME/.local/bin:$PATH"
|
||||
|
||||
# Check if mcp-proxy is installed, if not install it
|
||||
if ! command -v mcp-proxy &> /dev/null; then
|
||||
echo "mcp-proxy not found, installing..." >&2
|
||||
"$HOME/.local/bin/uv" tool install mcp-proxy || uv tool install mcp-proxy
|
||||
fi
|
||||
|
||||
# Get the port from environment variable, default to 37140 if not set
|
||||
if [ ! -z "${LSP_MCP_PORT}" ]; then
|
||||
PORT="${LSP_MCP_PORT}"
|
||||
else
|
||||
# Look for a file in the current dir called .lsp_mcp.port and get the port from there
|
||||
if [ -f .lsp_mcp.port ]; then
|
||||
PORT=$(cat .lsp_mcp.port)
|
||||
else
|
||||
PORT=37140
|
||||
fi
|
||||
fi
|
||||
|
||||
# Call mcp-proxy to connect to the LSP server
|
||||
exec mcp-proxy --transport streamablehttp "http://localhost:${PORT}"
|
||||
@@ -1,456 +0,0 @@
|
||||
# OpenSpec Instructions
|
||||
|
||||
Instructions for AI coding assistants using OpenSpec for spec-driven development.
|
||||
|
||||
## TL;DR Quick Checklist
|
||||
|
||||
- Search existing work: `openspec spec list --long`, `openspec list` (use `rg` only for full-text search)
|
||||
- Decide scope: new capability vs modify existing capability
|
||||
- Pick a unique `change-id`: kebab-case, verb-led (`add-`, `update-`, `remove-`, `refactor-`)
|
||||
- Scaffold: `proposal.md`, `tasks.md`, `design.md` (only if needed), and delta specs per affected capability
|
||||
- Write deltas: use `## ADDED|MODIFIED|REMOVED|RENAMED Requirements`; include at least one `#### Scenario:` per requirement
|
||||
- Validate: `openspec validate [change-id] --strict` and fix issues
|
||||
- Request approval: Do not start implementation until proposal is approved
|
||||
|
||||
## Three-Stage Workflow
|
||||
|
||||
### Stage 1: Creating Changes
|
||||
Create proposal when you need to:
|
||||
- Add features or functionality
|
||||
- Make breaking changes (API, schema)
|
||||
- Change architecture or patterns
|
||||
- Optimize performance (changes behavior)
|
||||
- Update security patterns
|
||||
|
||||
Triggers (examples):
|
||||
- "Help me create a change proposal"
|
||||
- "Help me plan a change"
|
||||
- "Help me create a proposal"
|
||||
- "I want to create a spec proposal"
|
||||
- "I want to create a spec"
|
||||
|
||||
Loose matching guidance:
|
||||
- Contains one of: `proposal`, `change`, `spec`
|
||||
- With one of: `create`, `plan`, `make`, `start`, `help`
|
||||
|
||||
Skip proposal for:
|
||||
- Bug fixes (restore intended behavior)
|
||||
- Typos, formatting, comments
|
||||
- Dependency updates (non-breaking)
|
||||
- Configuration changes
|
||||
- Tests for existing behavior
|
||||
|
||||
**Workflow**
|
||||
1. Review `openspec/project.md`, `openspec list`, and `openspec list --specs` to understand current context.
|
||||
2. Choose a unique verb-led `change-id` and scaffold `proposal.md`, `tasks.md`, optional `design.md`, and spec deltas under `openspec/changes/<id>/`.
|
||||
3. Draft spec deltas using `## ADDED|MODIFIED|REMOVED Requirements` with at least one `#### Scenario:` per requirement.
|
||||
4. Run `openspec validate <id> --strict` and resolve any issues before sharing the proposal.
|
||||
|
||||
### Stage 2: Implementing Changes
|
||||
Track these steps as TODOs and complete them one by one.
|
||||
1. **Read proposal.md** - Understand what's being built
|
||||
2. **Read design.md** (if exists) - Review technical decisions
|
||||
3. **Read tasks.md** - Get implementation checklist
|
||||
4. **Implement tasks sequentially** - Complete in order
|
||||
5. **Confirm completion** - Ensure every item in `tasks.md` is finished before updating statuses
|
||||
6. **Update checklist** - After all work is done, set every task to `- [x]` so the list reflects reality
|
||||
7. **Approval gate** - Do not start implementation until the proposal is reviewed and approved
|
||||
|
||||
### Stage 3: Archiving Changes
|
||||
After deployment, create separate PR to:
|
||||
- Move `changes/[name]/` → `changes/archive/YYYY-MM-DD-[name]/`
|
||||
- Update `specs/` if capabilities changed
|
||||
- Use `openspec archive <change-id> --skip-specs --yes` for tooling-only changes (always pass the change ID explicitly)
|
||||
- Run `openspec validate --strict` to confirm the archived change passes checks
|
||||
|
||||
## Before Any Task
|
||||
|
||||
**Context Checklist:**
|
||||
- [ ] Read relevant specs in `specs/[capability]/spec.md`
|
||||
- [ ] Check pending changes in `changes/` for conflicts
|
||||
- [ ] Read `openspec/project.md` for conventions
|
||||
- [ ] Run `openspec list` to see active changes
|
||||
- [ ] Run `openspec list --specs` to see existing capabilities
|
||||
|
||||
**Before Creating Specs:**
|
||||
- Always check if capability already exists
|
||||
- Prefer modifying existing specs over creating duplicates
|
||||
- Use `openspec show [spec]` to review current state
|
||||
- If request is ambiguous, ask 1–2 clarifying questions before scaffolding
|
||||
|
||||
### Search Guidance
|
||||
- Enumerate specs: `openspec spec list --long` (or `--json` for scripts)
|
||||
- Enumerate changes: `openspec list` (or `openspec change list --json` - deprecated but available)
|
||||
- Show details:
|
||||
- Spec: `openspec show <spec-id> --type spec` (use `--json` for filters)
|
||||
- Change: `openspec show <change-id> --json --deltas-only`
|
||||
- Full-text search (use ripgrep): `rg -n "Requirement:|Scenario:" openspec/specs`
|
||||
|
||||
## Quick Start
|
||||
|
||||
### CLI Commands
|
||||
|
||||
```bash
|
||||
# Essential commands
|
||||
openspec list # List active changes
|
||||
openspec list --specs # List specifications
|
||||
openspec show [item] # Display change or spec
|
||||
openspec validate [item] # Validate changes or specs
|
||||
openspec archive <change-id> [--yes|-y] # Archive after deployment (add --yes for non-interactive runs)
|
||||
|
||||
# Project management
|
||||
openspec init [path] # Initialize OpenSpec
|
||||
openspec update [path] # Update instruction files
|
||||
|
||||
# Interactive mode
|
||||
openspec show # Prompts for selection
|
||||
openspec validate # Bulk validation mode
|
||||
|
||||
# Debugging
|
||||
openspec show [change] --json --deltas-only
|
||||
openspec validate [change] --strict
|
||||
```
|
||||
|
||||
### Command Flags
|
||||
|
||||
- `--json` - Machine-readable output
|
||||
- `--type change|spec` - Disambiguate items
|
||||
- `--strict` - Comprehensive validation
|
||||
- `--no-interactive` - Disable prompts
|
||||
- `--skip-specs` - Archive without spec updates
|
||||
- `--yes`/`-y` - Skip confirmation prompts (non-interactive archive)
|
||||
|
||||
## Directory Structure
|
||||
|
||||
```
|
||||
openspec/
|
||||
├── project.md # Project conventions
|
||||
├── specs/ # Current truth - what IS built
|
||||
│ └── [capability]/ # Single focused capability
|
||||
│ ├── spec.md # Requirements and scenarios
|
||||
│ └── design.md # Technical patterns
|
||||
├── changes/ # Proposals - what SHOULD change
|
||||
│ ├── [change-name]/
|
||||
│ │ ├── proposal.md # Why, what, impact
|
||||
│ │ ├── tasks.md # Implementation checklist
|
||||
│ │ ├── design.md # Technical decisions (optional; see criteria)
|
||||
│ │ └── specs/ # Delta changes
|
||||
│ │ └── [capability]/
|
||||
│ │ └── spec.md # ADDED/MODIFIED/REMOVED
|
||||
│ └── archive/ # Completed changes
|
||||
```
|
||||
|
||||
## Creating Change Proposals
|
||||
|
||||
### Decision Tree
|
||||
|
||||
```
|
||||
New request?
|
||||
├─ Bug fix restoring spec behavior? → Fix directly
|
||||
├─ Typo/format/comment? → Fix directly
|
||||
├─ New feature/capability? → Create proposal
|
||||
├─ Breaking change? → Create proposal
|
||||
├─ Architecture change? → Create proposal
|
||||
└─ Unclear? → Create proposal (safer)
|
||||
```
|
||||
|
||||
### Proposal Structure
|
||||
|
||||
1. **Create directory:** `changes/[change-id]/` (kebab-case, verb-led, unique)
|
||||
|
||||
2. **Write proposal.md:**
|
||||
```markdown
|
||||
# Change: [Brief description of change]
|
||||
|
||||
## Why
|
||||
[1-2 sentences on problem/opportunity]
|
||||
|
||||
## What Changes
|
||||
- [Bullet list of changes]
|
||||
- [Mark breaking changes with **BREAKING**]
|
||||
|
||||
## Impact
|
||||
- Affected specs: [list capabilities]
|
||||
- Affected code: [key files/systems]
|
||||
```
|
||||
|
||||
3. **Create spec deltas:** `specs/[capability]/spec.md`
|
||||
```markdown
|
||||
## ADDED Requirements
|
||||
### Requirement: New Feature
|
||||
The system SHALL provide...
|
||||
|
||||
#### Scenario: Success case
|
||||
- **WHEN** user performs action
|
||||
- **THEN** expected result
|
||||
|
||||
## MODIFIED Requirements
|
||||
### Requirement: Existing Feature
|
||||
[Complete modified requirement]
|
||||
|
||||
## REMOVED Requirements
|
||||
### Requirement: Old Feature
|
||||
**Reason**: [Why removing]
|
||||
**Migration**: [How to handle]
|
||||
```
|
||||
If multiple capabilities are affected, create multiple delta files under `changes/[change-id]/specs/<capability>/spec.md`—one per capability.
|
||||
|
||||
4. **Create tasks.md:**
|
||||
```markdown
|
||||
## 1. Implementation
|
||||
- [ ] 1.1 Create database schema
|
||||
- [ ] 1.2 Implement API endpoint
|
||||
- [ ] 1.3 Add frontend component
|
||||
- [ ] 1.4 Write tests
|
||||
```
|
||||
|
||||
5. **Create design.md when needed:**
|
||||
Create `design.md` if any of the following apply; otherwise omit it:
|
||||
- Cross-cutting change (multiple services/modules) or a new architectural pattern
|
||||
- New external dependency or significant data model changes
|
||||
- Security, performance, or migration complexity
|
||||
- Ambiguity that benefits from technical decisions before coding
|
||||
|
||||
Minimal `design.md` skeleton:
|
||||
```markdown
|
||||
## Context
|
||||
[Background, constraints, stakeholders]
|
||||
|
||||
## Goals / Non-Goals
|
||||
- Goals: [...]
|
||||
- Non-Goals: [...]
|
||||
|
||||
## Decisions
|
||||
- Decision: [What and why]
|
||||
- Alternatives considered: [Options + rationale]
|
||||
|
||||
## Risks / Trade-offs
|
||||
- [Risk] → Mitigation
|
||||
|
||||
## Migration Plan
|
||||
[Steps, rollback]
|
||||
|
||||
## Open Questions
|
||||
- [...]
|
||||
```
|
||||
|
||||
## Spec File Format
|
||||
|
||||
### Critical: Scenario Formatting
|
||||
|
||||
**CORRECT** (use #### headers):
|
||||
```markdown
|
||||
#### Scenario: User login success
|
||||
- **WHEN** valid credentials provided
|
||||
- **THEN** return JWT token
|
||||
```
|
||||
|
||||
**WRONG** (don't use bullets or bold):
|
||||
```markdown
|
||||
- **Scenario: User login** ❌
|
||||
**Scenario**: User login ❌
|
||||
### Scenario: User login ❌
|
||||
```
|
||||
|
||||
Every requirement MUST have at least one scenario.
|
||||
|
||||
### Requirement Wording
|
||||
- Use SHALL/MUST for normative requirements (avoid should/may unless intentionally non-normative)
|
||||
|
||||
### Delta Operations
|
||||
|
||||
- `## ADDED Requirements` - New capabilities
|
||||
- `## MODIFIED Requirements` - Changed behavior
|
||||
- `## REMOVED Requirements` - Deprecated features
|
||||
- `## RENAMED Requirements` - Name changes
|
||||
|
||||
Headers matched with `trim(header)` - whitespace ignored.
|
||||
|
||||
#### When to use ADDED vs MODIFIED
|
||||
- ADDED: Introduces a new capability or sub-capability that can stand alone as a requirement. Prefer ADDED when the change is orthogonal (e.g., adding "Slash Command Configuration") rather than altering the semantics of an existing requirement.
|
||||
- MODIFIED: Changes the behavior, scope, or acceptance criteria of an existing requirement. Always paste the full, updated requirement content (header + all scenarios). The archiver will replace the entire requirement with what you provide here; partial deltas will drop previous details.
|
||||
- RENAMED: Use when only the name changes. If you also change behavior, use RENAMED (name) plus MODIFIED (content) referencing the new name.
|
||||
|
||||
Common pitfall: Using MODIFIED to add a new concern without including the previous text. This causes loss of detail at archive time. If you aren’t explicitly changing the existing requirement, add a new requirement under ADDED instead.
|
||||
|
||||
Authoring a MODIFIED requirement correctly:
|
||||
1) Locate the existing requirement in `openspec/specs/<capability>/spec.md`.
|
||||
2) Copy the entire requirement block (from `### Requirement: ...` through its scenarios).
|
||||
3) Paste it under `## MODIFIED Requirements` and edit to reflect the new behavior.
|
||||
4) Ensure the header text matches exactly (whitespace-insensitive) and keep at least one `#### Scenario:`.
|
||||
|
||||
Example for RENAMED:
|
||||
```markdown
|
||||
## RENAMED Requirements
|
||||
- FROM: `### Requirement: Login`
|
||||
- TO: `### Requirement: User Authentication`
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Common Errors
|
||||
|
||||
**"Change must have at least one delta"**
|
||||
- Check `changes/[name]/specs/` exists with .md files
|
||||
- Verify files have operation prefixes (## ADDED Requirements)
|
||||
|
||||
**"Requirement must have at least one scenario"**
|
||||
- Check scenarios use `#### Scenario:` format (4 hashtags)
|
||||
- Don't use bullet points or bold for scenario headers
|
||||
|
||||
**Silent scenario parsing failures**
|
||||
- Exact format required: `#### Scenario: Name`
|
||||
- Debug with: `openspec show [change] --json --deltas-only`
|
||||
|
||||
### Validation Tips
|
||||
|
||||
```bash
|
||||
# Always use strict mode for comprehensive checks
|
||||
openspec validate [change] --strict
|
||||
|
||||
# Debug delta parsing
|
||||
openspec show [change] --json | jq '.deltas'
|
||||
|
||||
# Check specific requirement
|
||||
openspec show [spec] --json -r 1
|
||||
```
|
||||
|
||||
## Happy Path Script
|
||||
|
||||
```bash
|
||||
# 1) Explore current state
|
||||
openspec spec list --long
|
||||
openspec list
|
||||
# Optional full-text search:
|
||||
# rg -n "Requirement:|Scenario:" openspec/specs
|
||||
# rg -n "^#|Requirement:" openspec/changes
|
||||
|
||||
# 2) Choose change id and scaffold
|
||||
CHANGE=add-two-factor-auth
|
||||
mkdir -p openspec/changes/$CHANGE/{specs/auth}
|
||||
printf "## Why\n...\n\n## What Changes\n- ...\n\n## Impact\n- ...\n" > openspec/changes/$CHANGE/proposal.md
|
||||
printf "## 1. Implementation\n- [ ] 1.1 ...\n" > openspec/changes/$CHANGE/tasks.md
|
||||
|
||||
# 3) Add deltas (example)
|
||||
cat > openspec/changes/$CHANGE/specs/auth/spec.md << 'EOF'
|
||||
## ADDED Requirements
|
||||
### Requirement: Two-Factor Authentication
|
||||
Users MUST provide a second factor during login.
|
||||
|
||||
#### Scenario: OTP required
|
||||
- **WHEN** valid credentials are provided
|
||||
- **THEN** an OTP challenge is required
|
||||
EOF
|
||||
|
||||
# 4) Validate
|
||||
openspec validate $CHANGE --strict
|
||||
```
|
||||
|
||||
## Multi-Capability Example
|
||||
|
||||
```
|
||||
openspec/changes/add-2fa-notify/
|
||||
├── proposal.md
|
||||
├── tasks.md
|
||||
└── specs/
|
||||
├── auth/
|
||||
│ └── spec.md # ADDED: Two-Factor Authentication
|
||||
└── notifications/
|
||||
└── spec.md # ADDED: OTP email notification
|
||||
```
|
||||
|
||||
auth/spec.md
|
||||
```markdown
|
||||
## ADDED Requirements
|
||||
### Requirement: Two-Factor Authentication
|
||||
...
|
||||
```
|
||||
|
||||
notifications/spec.md
|
||||
```markdown
|
||||
## ADDED Requirements
|
||||
### Requirement: OTP Email Notification
|
||||
...
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
### Simplicity First
|
||||
- Default to <100 lines of new code
|
||||
- Single-file implementations until proven insufficient
|
||||
- Avoid frameworks without clear justification
|
||||
- Choose boring, proven patterns
|
||||
|
||||
### Complexity Triggers
|
||||
Only add complexity with:
|
||||
- Performance data showing current solution too slow
|
||||
- Concrete scale requirements (>1000 users, >100MB data)
|
||||
- Multiple proven use cases requiring abstraction
|
||||
|
||||
### Clear References
|
||||
- Use `file.ts:42` format for code locations
|
||||
- Reference specs as `specs/auth/spec.md`
|
||||
- Link related changes and PRs
|
||||
|
||||
### Capability Naming
|
||||
- Use verb-noun: `user-auth`, `payment-capture`
|
||||
- Single purpose per capability
|
||||
- 10-minute understandability rule
|
||||
- Split if description needs "AND"
|
||||
|
||||
### Change ID Naming
|
||||
- Use kebab-case, short and descriptive: `add-two-factor-auth`
|
||||
- Prefer verb-led prefixes: `add-`, `update-`, `remove-`, `refactor-`
|
||||
- Ensure uniqueness; if taken, append `-2`, `-3`, etc.
|
||||
|
||||
## Tool Selection Guide
|
||||
|
||||
| Task | Tool | Why |
|
||||
|------|------|-----|
|
||||
| Find files by pattern | Glob | Fast pattern matching |
|
||||
| Search code content | Grep | Optimized regex search |
|
||||
| Read specific files | Read | Direct file access |
|
||||
| Explore unknown scope | Task | Multi-step investigation |
|
||||
|
||||
## Error Recovery
|
||||
|
||||
### Change Conflicts
|
||||
1. Run `openspec list` to see active changes
|
||||
2. Check for overlapping specs
|
||||
3. Coordinate with change owners
|
||||
4. Consider combining proposals
|
||||
|
||||
### Validation Failures
|
||||
1. Run with `--strict` flag
|
||||
2. Check JSON output for details
|
||||
3. Verify spec file format
|
||||
4. Ensure scenarios properly formatted
|
||||
|
||||
### Missing Context
|
||||
1. Read project.md first
|
||||
2. Check related specs
|
||||
3. Review recent archives
|
||||
4. Ask for clarification
|
||||
|
||||
## Quick Reference
|
||||
|
||||
### Stage Indicators
|
||||
- `changes/` - Proposed, not yet built
|
||||
- `specs/` - Built and deployed
|
||||
- `archive/` - Completed changes
|
||||
|
||||
### File Purposes
|
||||
- `proposal.md` - Why and what
|
||||
- `tasks.md` - Implementation steps
|
||||
- `design.md` - Technical decisions
|
||||
- `spec.md` - Requirements and behavior
|
||||
|
||||
### CLI Essentials
|
||||
```bash
|
||||
openspec list # What's in progress?
|
||||
openspec show [item] # View details
|
||||
openspec validate --strict # Is it correct?
|
||||
openspec archive <change-id> [--yes|-y] # Mark complete (add --yes for automation)
|
||||
```
|
||||
|
||||
Remember: Specs are truth. Changes are proposals. Keep them in sync.
|
||||
@@ -1,241 +0,0 @@
|
||||
# Design: add-resource-workload
|
||||
|
||||
## Architecture Overview
|
||||
|
||||
```
|
||||
┌─────────────┐ ┌─────────────────────┐ ┌───────────────┐
|
||||
│ Frontend │────▶│ Workload API │────▶│ MySQL │
|
||||
│ (Heatmap) │ │ /api/v1/workload │ │ (Snapshots) │
|
||||
└─────────────┘ └─────────────────────┘ └───────────────┘
|
||||
│
|
||||
▼
|
||||
┌───────────────┐
|
||||
│ Redis │
|
||||
│ (Cache) │
|
||||
└───────────────┘
|
||||
```
|
||||
|
||||
## Key Design Decisions
|
||||
|
||||
### 1. 負載計算策略:即時計算 vs 快照
|
||||
|
||||
**決定**:採用**混合策略**
|
||||
- **即時計算**:API 請求時計算,結果快取 1 小時
|
||||
- **快照儲存**:每日凌晨儲存歷史快照供趨勢分析
|
||||
|
||||
**理由**:
|
||||
- 即時計算確保數據新鮮度
|
||||
- 快照提供歷史趨勢分析能力
|
||||
- Redis 快取減少計算負擔
|
||||
|
||||
### 2. 週邊界定義
|
||||
|
||||
**決定**:採用 **ISO 8601 週**(週一至週日)
|
||||
|
||||
**理由**:
|
||||
- 國際標準,避免歧義
|
||||
- Python/MySQL 原生支援
|
||||
- 便於未來國際化
|
||||
|
||||
### 3. 負載計算公式
|
||||
|
||||
```
|
||||
週負載 = Σ(該週到期任務的 original_estimate) / 週容量 × 100%
|
||||
```
|
||||
|
||||
**任務計入規則**:
|
||||
- `due_date` 在該週範圍內
|
||||
- `assignee_id` 為目標使用者
|
||||
- `status` 非已完成狀態
|
||||
|
||||
**邊界情況處理**:
|
||||
- `original_estimate` 為空:計為 0(不計入負載)
|
||||
- `capacity` 為 0:顯示為 N/A(避免除以零)
|
||||
|
||||
### 4. 負載等級閾值
|
||||
|
||||
| 等級 | 範圍 | 顏色 | 描述 |
|
||||
|------|------|------|------|
|
||||
| normal | 0-79% | green | 正常 |
|
||||
| warning | 80-99% | yellow | 警告 |
|
||||
| overloaded | ≥100% | red | 超載 |
|
||||
|
||||
### 5. 快取策略
|
||||
|
||||
```
|
||||
快取鍵格式:workload:{user_id}:{week_start}
|
||||
TTL:3600 秒(1 小時)
|
||||
```
|
||||
|
||||
**失效時機**:
|
||||
- 任務建立/更新/刪除
|
||||
- 使用者容量變更
|
||||
|
||||
**實作**:暫不實作主動失效,依賴 TTL 自然過期(Phase 1 簡化)
|
||||
|
||||
### 6. 權限控制
|
||||
|
||||
| 角色 | 可查看範圍 |
|
||||
|------|-----------|
|
||||
| super_admin | 所有使用者 |
|
||||
| manager | 同部門使用者 |
|
||||
| engineer | 僅自己 |
|
||||
|
||||
## API Design
|
||||
|
||||
### GET /api/v1/workload/heatmap
|
||||
|
||||
查詢團隊負載熱圖
|
||||
|
||||
**Query Parameters**:
|
||||
- `week_start`: ISO 日期(預設當週一)
|
||||
- `department_id`: 部門篩選(可選)
|
||||
- `user_ids`: 使用者 ID 陣列(可選)
|
||||
|
||||
**Response**:
|
||||
```json
|
||||
{
|
||||
"week_start": "2024-01-01",
|
||||
"week_end": "2024-01-07",
|
||||
"users": [
|
||||
{
|
||||
"user_id": "uuid",
|
||||
"user_name": "John Doe",
|
||||
"department_id": "uuid",
|
||||
"department_name": "R&D",
|
||||
"capacity_hours": 40.0,
|
||||
"allocated_hours": 32.5,
|
||||
"load_percentage": 81.25,
|
||||
"load_level": "warning",
|
||||
"task_count": 5
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### GET /api/v1/workload/user/{user_id}
|
||||
|
||||
查詢特定使用者的負載詳情
|
||||
|
||||
**Query Parameters**:
|
||||
- `week_start`: ISO 日期
|
||||
|
||||
**Response**:
|
||||
```json
|
||||
{
|
||||
"user_id": "uuid",
|
||||
"week_start": "2024-01-01",
|
||||
"capacity_hours": 40.0,
|
||||
"allocated_hours": 32.5,
|
||||
"load_percentage": 81.25,
|
||||
"load_level": "warning",
|
||||
"tasks": [
|
||||
{
|
||||
"task_id": "uuid",
|
||||
"title": "Task Name",
|
||||
"project_name": "Project A",
|
||||
"due_date": "2024-01-05",
|
||||
"original_estimate": 8.0,
|
||||
"status": "in_progress"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### PUT /api/v1/users/{user_id}/capacity
|
||||
|
||||
更新使用者容量
|
||||
|
||||
**Request Body**:
|
||||
```json
|
||||
{
|
||||
"capacity": 32.0,
|
||||
"effective_from": "2024-01-08",
|
||||
"effective_until": "2024-01-14",
|
||||
"reason": "年假"
|
||||
}
|
||||
```
|
||||
|
||||
## Data Model
|
||||
|
||||
### pjctrl_workload_snapshots
|
||||
|
||||
儲存歷史負載快照
|
||||
|
||||
```sql
|
||||
CREATE TABLE pjctrl_workload_snapshots (
|
||||
id VARCHAR(36) PRIMARY KEY,
|
||||
user_id VARCHAR(36) NOT NULL,
|
||||
week_start DATE NOT NULL,
|
||||
allocated_hours DECIMAL(8,2) NOT NULL DEFAULT 0,
|
||||
capacity_hours DECIMAL(8,2) NOT NULL DEFAULT 40,
|
||||
load_percentage DECIMAL(5,2) NOT NULL DEFAULT 0,
|
||||
task_count INT NOT NULL DEFAULT 0,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
|
||||
FOREIGN KEY (user_id) REFERENCES pjctrl_users(id) ON DELETE CASCADE,
|
||||
UNIQUE KEY uk_user_week (user_id, week_start),
|
||||
INDEX idx_week_start (week_start)
|
||||
);
|
||||
```
|
||||
|
||||
### pjctrl_capacity_adjustments(可選,Phase 1 暫不實作)
|
||||
|
||||
儲存臨時容量調整(如請假)
|
||||
|
||||
```sql
|
||||
CREATE TABLE pjctrl_capacity_adjustments (
|
||||
id VARCHAR(36) PRIMARY KEY,
|
||||
user_id VARCHAR(36) NOT NULL,
|
||||
week_start DATE NOT NULL,
|
||||
adjusted_capacity DECIMAL(5,2) NOT NULL,
|
||||
reason VARCHAR(200),
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
FOREIGN KEY (user_id) REFERENCES pjctrl_users(id) ON DELETE CASCADE,
|
||||
UNIQUE KEY uk_user_week (user_id, week_start)
|
||||
);
|
||||
```
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
### 後端結構
|
||||
|
||||
```
|
||||
backend/app/
|
||||
├── api/
|
||||
│ └── workload/
|
||||
│ ├── __init__.py
|
||||
│ └── router.py
|
||||
├── services/
|
||||
│ └── workload_service.py
|
||||
├── models/
|
||||
│ └── workload_snapshot.py
|
||||
└── schemas/
|
||||
└── workload.py
|
||||
```
|
||||
|
||||
### 週起始日計算
|
||||
|
||||
```python
|
||||
from datetime import date, timedelta
|
||||
|
||||
def get_week_start(d: date) -> date:
|
||||
"""取得 ISO 週的週一"""
|
||||
return d - timedelta(days=d.weekday())
|
||||
```
|
||||
|
||||
### Redis 快取範例
|
||||
|
||||
```python
|
||||
async def get_user_workload(user_id: str, week_start: date) -> dict:
|
||||
cache_key = f"workload:{user_id}:{week_start}"
|
||||
cached = redis_client.get(cache_key)
|
||||
if cached:
|
||||
return json.loads(cached)
|
||||
|
||||
result = calculate_workload(user_id, week_start)
|
||||
redis_client.setex(cache_key, 3600, json.dumps(result))
|
||||
return result
|
||||
```
|
||||
@@ -1,74 +0,0 @@
|
||||
# Proposal: add-resource-workload
|
||||
|
||||
## Summary
|
||||
|
||||
實作資源管理系統的核心功能:負載熱圖與容量追蹤。讓主管能夠視覺化了解團隊成員的工作負載狀況,並進行資源分配決策。
|
||||
|
||||
## Motivation
|
||||
|
||||
根據 `project.md` 的核心目標:
|
||||
- 提供**即時資源負載與專案進度分析**
|
||||
- 讓 Unit Managers 獲得**團隊工作負載可見度、資源分配熱圖**
|
||||
- 減輕工程師的**時間回報負擔**
|
||||
|
||||
目前系統已完成 `user-auth` 和 `task-management`,具備:
|
||||
- 使用者資料與容量(capacity)欄位
|
||||
- 任務的時間估算(original_estimate)與實際耗時(time_spent)
|
||||
- 任務指派(assignee_id)與截止日期(due_date)
|
||||
|
||||
基於現有基礎,可以開始計算並展示資源負載資訊。
|
||||
|
||||
## Scope
|
||||
|
||||
### In Scope (Phase 1 - 核心負載功能)
|
||||
|
||||
1. **Workload Heatmap API**
|
||||
- 計算每位成員的週負載百分比
|
||||
- 依負載狀態分類(綠色/黃色/紅色)
|
||||
- 支援時間範圍查詢
|
||||
|
||||
2. **User Capacity Management**
|
||||
- 更新使用者容量設定 API
|
||||
- 容量歷史記錄(未來週數的容量調整,如請假)
|
||||
|
||||
3. **Workload Snapshot Storage**
|
||||
- 建立 `pjctrl_workload_snapshots` 表
|
||||
- 定期快照或即時計算策略
|
||||
|
||||
4. **Team Workload Overview API**
|
||||
- 部門級別的負載總覽
|
||||
- 支援按專案或部門篩選
|
||||
|
||||
### Out of Scope (Future Phases)
|
||||
|
||||
- Multi-Project Health Dashboard(需要更多專案進度數據)
|
||||
- 資源分配不均的自動建議
|
||||
- 資源分配 AI 預測
|
||||
- WebSocket 即時更新
|
||||
|
||||
## Affected Specs
|
||||
|
||||
- `resource-management` - 實作以下需求:
|
||||
- Workload Heatmap(部分)
|
||||
- Capacity Planning(部分)
|
||||
- Team Workload Distribution(部分)
|
||||
|
||||
## Dependencies
|
||||
|
||||
- `user-auth` - 使用者認證與權限
|
||||
- `task-management` - 任務與時間估算數據來源
|
||||
|
||||
## Risks
|
||||
|
||||
| Risk | Impact | Mitigation |
|
||||
|------|--------|------------|
|
||||
| 負載計算效能 | 大量任務時計算緩慢 | 使用 Redis 快取計算結果 |
|
||||
| 任務時間數據不完整 | 無 original_estimate 的任務無法計算 | 提供預設值或排除計算 |
|
||||
| 週邊界定義不一致 | 不同使用者對「週」的理解不同 | 統一使用 ISO 週(週一至週日)|
|
||||
|
||||
## Success Criteria
|
||||
|
||||
- [ ] 主管可查看團隊成員的週負載熱圖
|
||||
- [ ] 負載百分比正確反映任務分配時數 vs 容量
|
||||
- [ ] 支援按部門篩選負載視圖
|
||||
- [ ] API 回應時間 < 500ms(快取命中時 < 100ms)
|
||||
@@ -1,87 +0,0 @@
|
||||
# resource-management spec delta
|
||||
|
||||
此變更實作 `resource-management` spec 的核心需求,補充 API 規格與實作細節。
|
||||
|
||||
## MODIFIED Requirements
|
||||
|
||||
### Requirement: Workload Heatmap
|
||||
|
||||
系統 SHALL 提供負載熱圖 API,自動統計每人每週分配的任務總時數,並以顏色等級表示負載狀態。
|
||||
|
||||
#### Scenario: 負載正常顯示
|
||||
- **GIVEN** 某人員本週被指派的任務總時數低於其容量的 80%
|
||||
- **WHEN** 主管查詢負載熱圖 API
|
||||
- **THEN** 該人員的 `load_level` 為 `normal`
|
||||
- **AND** 回傳包含 `load_percentage`、`allocated_hours`、`capacity_hours`
|
||||
|
||||
#### Scenario: 負載警告顯示
|
||||
- **GIVEN** 某人員本週被指派的任務總時數達到其容量的 80%-99%
|
||||
- **WHEN** 主管查詢負載熱圖 API
|
||||
- **THEN** 該人員的 `load_level` 為 `warning`
|
||||
|
||||
#### Scenario: 負載超載顯示
|
||||
- **GIVEN** 某人員本週被指派的任務總時數達到或超過其容量的 100%
|
||||
- **WHEN** 主管查詢負載熱圖 API
|
||||
- **THEN** 該人員的 `load_level` 為 `overloaded`
|
||||
|
||||
#### Scenario: 查詢特定週的負載
|
||||
- **GIVEN** 主管需要查看非當週的負載
|
||||
- **WHEN** 主管以 `week_start` 參數查詢負載熱圖 API
|
||||
- **THEN** 系統回傳該週的負載資料
|
||||
|
||||
#### Scenario: 快取機制
|
||||
- **GIVEN** 負載資料已被計算並快取
|
||||
- **WHEN** 相同查詢在 1 小時內再次發生
|
||||
- **THEN** 系統從 Redis 快取回傳結果
|
||||
|
||||
### Requirement: Capacity Planning
|
||||
|
||||
系統 SHALL 支援人員容量規劃,包含預設容量與臨時調整。
|
||||
|
||||
#### Scenario: 設定人員預設容量
|
||||
- **GIVEN** 管理者需要設定人員的週工時上限
|
||||
- **WHEN** 管理者更新使用者的 `capacity` 值
|
||||
- **THEN** 系統儲存新的容量設定
|
||||
- **AND** 後續負載計算使用新容量值
|
||||
|
||||
#### Scenario: 容量為零處理
|
||||
- **GIVEN** 使用者的容量設為 0
|
||||
- **WHEN** 系統計算該使用者的負載
|
||||
- **THEN** `load_percentage` 顯示為 `null`
|
||||
- **AND** `load_level` 顯示為 `unavailable`
|
||||
|
||||
### Requirement: Team Workload Distribution
|
||||
|
||||
系統 SHALL 提供團隊工作分配查詢功能。
|
||||
|
||||
#### Scenario: 部門負載總覽
|
||||
- **GIVEN** 主管需要了解部門整體負載
|
||||
- **WHEN** 主管以 `department_id` 參數查詢負載熱圖 API
|
||||
- **THEN** 僅顯示該部門成員的負載狀況
|
||||
|
||||
#### Scenario: 使用者負載詳情
|
||||
- **GIVEN** 主管需要了解某人的詳細任務分配
|
||||
- **WHEN** 主管查詢使用者負載詳情 API
|
||||
- **THEN** 回傳該週指派給該使用者的所有任務
|
||||
- **AND** 包含每個任務的 `original_estimate` 與 `due_date`
|
||||
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Workload Data Access Control
|
||||
|
||||
系統 SHALL 限制負載資料的存取權限。
|
||||
|
||||
#### Scenario: 系統管理員查看所有人
|
||||
- **GIVEN** 登入者為 `super_admin`
|
||||
- **WHEN** 查詢負載熱圖 API
|
||||
- **THEN** 可查看所有使用者的負載資料
|
||||
|
||||
#### Scenario: 一般使用者查看自己
|
||||
- **GIVEN** 登入者為一般使用者
|
||||
- **WHEN** 查詢負載熱圖 API 未指定 `user_ids`
|
||||
- **THEN** 僅回傳自己的負載資料
|
||||
|
||||
#### Scenario: 跨部門存取拒絕
|
||||
- **GIVEN** 登入者非系統管理員
|
||||
- **WHEN** 查詢其他部門使用者的負載
|
||||
- **THEN** 系統拒絕存取並回傳 403 Forbidden
|
||||
@@ -1,75 +0,0 @@
|
||||
# Tasks: add-resource-workload
|
||||
|
||||
## Phase 1: 資料模型與基礎設施
|
||||
|
||||
- [x] **1.1** 建立 WorkloadSnapshot model (`backend/app/models/workload_snapshot.py`)
|
||||
- [x] **1.2** 建立 Alembic migration 建立 `pjctrl_workload_snapshots` 表
|
||||
- [x] **1.3** 建立 Workload schemas (`backend/app/schemas/workload.py`)
|
||||
|
||||
## Phase 2: 核心服務層
|
||||
|
||||
- [x] **2.1** 實作 `workload_service.py` 核心邏輯
|
||||
- ISO 週計算函式
|
||||
- 使用者週負載計算
|
||||
- 負載等級判定(normal/warning/overloaded)
|
||||
- [x] **2.2** 實作 Redis 快取整合
|
||||
- 快取讀取/寫入
|
||||
- TTL 設定
|
||||
|
||||
## Phase 3: API 端點
|
||||
|
||||
- [x] **3.1** 建立 workload router (`backend/app/api/workload/router.py`)
|
||||
- [x] **3.2** 實作 `GET /api/workload/heatmap` - 團隊負載熱圖
|
||||
- [x] **3.3** 實作 `GET /api/workload/user/{user_id}` - 使用者負載詳情
|
||||
- [x] **3.4** 實作 `GET /api/workload/me` - 當前使用者負載(替代 3.4 的容量更新)
|
||||
- [x] **3.5** 註冊 workload router 至 main.py
|
||||
|
||||
## Phase 4: 權限控制
|
||||
|
||||
- [x] **4.1** 實作部門隔離邏輯
|
||||
- super_admin 可查看所有
|
||||
- manager 可查看同部門
|
||||
- engineer 僅可查看自己
|
||||
|
||||
## Phase 5: 測試
|
||||
|
||||
- [x] **5.1** 單元測試:負載計算邏輯
|
||||
- [x] **5.2** 單元測試:週邊界計算
|
||||
- [x] **5.3** API 測試:heatmap 端點
|
||||
- [x] **5.4** API 測試:user workload 端點
|
||||
- [x] **5.5** API 測試:權限控制
|
||||
|
||||
## Phase 6: E2E 測試與驗證
|
||||
|
||||
- [x] **6.1** 自動化 E2E 測試:負載熱圖完整流程
|
||||
- 建立測試使用者與任務資料
|
||||
- 驗證負載計算正確性
|
||||
- 驗證負載等級判定
|
||||
- [x] **6.2** 自動化 E2E 測試:權限控制流程
|
||||
- super_admin 可查看所有人
|
||||
- 一般使用者僅能查看自己
|
||||
- 跨部門存取拒絕
|
||||
- [x] **6.3** 自動化 E2E 測試:Redis 快取驗證
|
||||
- 首次請求計算並快取
|
||||
- 二次請求命中快取
|
||||
- [x] **6.4** 更新 API 文件(OpenAPI 自動生成)
|
||||
|
||||
## Dependencies
|
||||
|
||||
```
|
||||
1.1, 1.2 → 可平行執行
|
||||
1.3 → 依賴 1.1
|
||||
2.1 → 依賴 1.1, 1.3
|
||||
2.2 → 依賴 2.1
|
||||
3.1-3.5 → 依賴 2.1, 2.2
|
||||
4.1 → 依賴 3.1
|
||||
5.1-5.5 → 可平行執行,依賴 Phase 3, 4
|
||||
6.1-6.4 → 依賴 Phase 3, 4, 5
|
||||
```
|
||||
|
||||
## Validation Criteria
|
||||
|
||||
每個任務完成後需確認:
|
||||
- 程式碼無語法錯誤
|
||||
- 相關測試通過
|
||||
- 符合現有程式碼風格
|
||||
@@ -1,231 +0,0 @@
|
||||
# Design: add-task-management
|
||||
|
||||
## Architecture Overview
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ Frontend │
|
||||
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────────┐ │
|
||||
│ │ Spaces │ │ Projects │ │ Tasks │ │ Task Detail │ │
|
||||
│ │ List │ │ List │ │ ListView │ │ Form │ │
|
||||
│ └────┬─────┘ └────┬─────┘ └────┬─────┘ └──────┬───────┘ │
|
||||
└───────┼─────────────┼─────────────┼────────────────┼────────┘
|
||||
│ │ │ │
|
||||
▼ ▼ ▼ ▼
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ API Layer │
|
||||
│ ┌──────────────┐ ┌────────────────┐ ┌─────────────────┐ │
|
||||
│ │ /api/spaces │ │ /api/projects │ │ /api/tasks │ │
|
||||
│ │ router │ │ router │ │ router │ │
|
||||
│ └──────┬───────┘ └───────┬────────┘ └────────┬────────┘ │
|
||||
└─────────┼──────────────────┼────────────────────┼───────────┘
|
||||
│ │ │
|
||||
▼ ▼ ▼
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ Middleware Layer │
|
||||
│ ┌─────────────────┐ ┌─────────────────────────────────┐ │
|
||||
│ │ Auth Middleware│ │ Permission Middleware │ │
|
||||
│ │ (JWT驗證) │ │ (RBAC + Security Level) │ │
|
||||
│ └────────┬────────┘ └────────────────┬────────────────┘ │
|
||||
└───────────┼────────────────────────────┼────────────────────┘
|
||||
│ │
|
||||
▼ ▼
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ Data Layer │
|
||||
│ ┌────────────┐ ┌────────────┐ ┌────────────┐ │
|
||||
│ │ pjctrl_ │ │ pjctrl_ │ │ pjctrl_ │ │
|
||||
│ │ spaces │ │ projects │ │ tasks │ │
|
||||
│ └────────────┘ └────────────┘ └────────────┘ │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
## Data Model Details
|
||||
|
||||
### pjctrl_spaces
|
||||
|
||||
空間是最上層的組織單位,用於將相關專案分組。
|
||||
|
||||
```sql
|
||||
CREATE TABLE pjctrl_spaces (
|
||||
id CHAR(36) PRIMARY KEY,
|
||||
name VARCHAR(200) NOT NULL,
|
||||
description TEXT,
|
||||
owner_id CHAR(36) NOT NULL,
|
||||
is_active BOOLEAN DEFAULT true,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (owner_id) REFERENCES pjctrl_users(id)
|
||||
);
|
||||
|
||||
CREATE INDEX idx_spaces_owner ON pjctrl_spaces(owner_id);
|
||||
CREATE INDEX idx_spaces_active ON pjctrl_spaces(is_active);
|
||||
```
|
||||
|
||||
### pjctrl_projects
|
||||
|
||||
專案屬於某個空間,具有預算、時程和安全等級設定。
|
||||
|
||||
```sql
|
||||
CREATE TABLE pjctrl_projects (
|
||||
id CHAR(36) PRIMARY KEY,
|
||||
space_id CHAR(36) NOT NULL,
|
||||
title VARCHAR(200) NOT NULL,
|
||||
description TEXT,
|
||||
owner_id CHAR(36) NOT NULL,
|
||||
budget DECIMAL(15, 2),
|
||||
start_date DATE,
|
||||
end_date DATE,
|
||||
security_level ENUM('public', 'department', 'confidential') DEFAULT 'department',
|
||||
status VARCHAR(50) DEFAULT 'active',
|
||||
department_id CHAR(36),
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (space_id) REFERENCES pjctrl_spaces(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY (owner_id) REFERENCES pjctrl_users(id),
|
||||
FOREIGN KEY (department_id) REFERENCES pjctrl_departments(id)
|
||||
);
|
||||
|
||||
CREATE INDEX idx_projects_space ON pjctrl_projects(space_id);
|
||||
CREATE INDEX idx_projects_owner ON pjctrl_projects(owner_id);
|
||||
CREATE INDEX idx_projects_department ON pjctrl_projects(department_id);
|
||||
CREATE INDEX idx_projects_security ON pjctrl_projects(security_level);
|
||||
```
|
||||
|
||||
### pjctrl_task_statuses
|
||||
|
||||
定義專案內可用的任務狀態。
|
||||
|
||||
```sql
|
||||
CREATE TABLE pjctrl_task_statuses (
|
||||
id CHAR(36) PRIMARY KEY,
|
||||
project_id CHAR(36) NOT NULL,
|
||||
name VARCHAR(50) NOT NULL,
|
||||
color VARCHAR(7) DEFAULT '#808080',
|
||||
position INT DEFAULT 0,
|
||||
is_done BOOLEAN DEFAULT false,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (project_id) REFERENCES pjctrl_projects(id) ON DELETE CASCADE,
|
||||
UNIQUE KEY uk_status_name (project_id, name)
|
||||
);
|
||||
|
||||
CREATE INDEX idx_statuses_project ON pjctrl_task_statuses(project_id);
|
||||
```
|
||||
|
||||
### pjctrl_tasks
|
||||
|
||||
任務是核心實體,支援父子任務關係。
|
||||
|
||||
```sql
|
||||
CREATE TABLE pjctrl_tasks (
|
||||
id CHAR(36) PRIMARY KEY,
|
||||
project_id CHAR(36) NOT NULL,
|
||||
parent_task_id CHAR(36),
|
||||
title VARCHAR(500) NOT NULL,
|
||||
description TEXT,
|
||||
assignee_id CHAR(36),
|
||||
status_id CHAR(36),
|
||||
priority ENUM('low', 'medium', 'high', 'urgent') DEFAULT 'medium',
|
||||
original_estimate DECIMAL(8, 2),
|
||||
time_spent DECIMAL(8, 2) DEFAULT 0,
|
||||
blocker_flag BOOLEAN DEFAULT false,
|
||||
due_date DATETIME,
|
||||
position INT DEFAULT 0,
|
||||
created_by CHAR(36) NOT NULL,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (project_id) REFERENCES pjctrl_projects(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY (parent_task_id) REFERENCES pjctrl_tasks(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY (assignee_id) REFERENCES pjctrl_users(id),
|
||||
FOREIGN KEY (status_id) REFERENCES pjctrl_task_statuses(id),
|
||||
FOREIGN KEY (created_by) REFERENCES pjctrl_users(id)
|
||||
);
|
||||
|
||||
CREATE INDEX idx_tasks_project ON pjctrl_tasks(project_id);
|
||||
CREATE INDEX idx_tasks_parent ON pjctrl_tasks(parent_task_id);
|
||||
CREATE INDEX idx_tasks_assignee ON pjctrl_tasks(assignee_id);
|
||||
CREATE INDEX idx_tasks_status ON pjctrl_tasks(status_id);
|
||||
CREATE INDEX idx_tasks_due ON pjctrl_tasks(due_date);
|
||||
CREATE INDEX idx_tasks_blocker ON pjctrl_tasks(blocker_flag);
|
||||
```
|
||||
|
||||
## Permission Model
|
||||
|
||||
### Security Level 行為
|
||||
|
||||
| Security Level | 可見範圍 |
|
||||
|----------------|----------|
|
||||
| public | 所有登入使用者 |
|
||||
| department | 同部門使用者 + 專案成員 |
|
||||
| confidential | 僅專案成員 |
|
||||
|
||||
### 權限檢查流程
|
||||
|
||||
```python
|
||||
def check_project_access(user, project):
|
||||
# 系統管理員可存取所有
|
||||
if user.is_system_admin:
|
||||
return True
|
||||
|
||||
# 專案擁有者
|
||||
if project.owner_id == user.id:
|
||||
return True
|
||||
|
||||
# 依 security_level 判斷
|
||||
if project.security_level == 'public':
|
||||
return True
|
||||
elif project.security_level == 'department':
|
||||
return user.department_id == project.department_id
|
||||
else: # confidential
|
||||
return is_project_member(user, project)
|
||||
```
|
||||
|
||||
## API Design
|
||||
|
||||
### Spaces API
|
||||
|
||||
| Method | Endpoint | Description |
|
||||
|--------|----------|-------------|
|
||||
| GET | /api/spaces | 列出使用者可見的空間 |
|
||||
| POST | /api/spaces | 建立新空間 |
|
||||
| GET | /api/spaces/{id} | 取得空間詳情 |
|
||||
| PATCH | /api/spaces/{id} | 更新空間 |
|
||||
| DELETE | /api/spaces/{id} | 刪除空間 (軟刪除) |
|
||||
|
||||
### Projects API
|
||||
|
||||
| Method | Endpoint | Description |
|
||||
|--------|----------|-------------|
|
||||
| GET | /api/spaces/{space_id}/projects | 列出空間內的專案 |
|
||||
| POST | /api/spaces/{space_id}/projects | 建立新專案 |
|
||||
| GET | /api/projects/{id} | 取得專案詳情 |
|
||||
| PATCH | /api/projects/{id} | 更新專案 |
|
||||
| DELETE | /api/projects/{id} | 刪除專案 |
|
||||
|
||||
### Tasks API
|
||||
|
||||
| Method | Endpoint | Description |
|
||||
|--------|----------|-------------|
|
||||
| GET | /api/projects/{project_id}/tasks | 列出專案內的任務 |
|
||||
| POST | /api/projects/{project_id}/tasks | 建立新任務 |
|
||||
| GET | /api/tasks/{id} | 取得任務詳情 |
|
||||
| PATCH | /api/tasks/{id} | 更新任務 |
|
||||
| DELETE | /api/tasks/{id} | 刪除任務 |
|
||||
| PATCH | /api/tasks/{id}/status | 變更任務狀態 |
|
||||
| PATCH | /api/tasks/{id}/assign | 指派任務 |
|
||||
|
||||
## Default Task Statuses
|
||||
|
||||
每個新專案自動建立以下預設狀態:
|
||||
|
||||
| Name | Color | is_done |
|
||||
|------|-------|---------|
|
||||
| To Do | #808080 | false |
|
||||
| In Progress | #0066cc | false |
|
||||
| Blocked | #cc0000 | false |
|
||||
| Done | #00cc66 | true |
|
||||
|
||||
## Trade-offs & Decisions
|
||||
|
||||
1. **子任務深度限制**: 限制為 2 層 (Task → Sub-task),避免過度巢狀造成的複雜度
|
||||
2. **軟刪除 vs 硬刪除**: Space 使用軟刪除 (is_active),Tasks 使用級聯硬刪除
|
||||
3. **狀態定義層級**: 狀態定義在 Project 層級,而非全域,提供專案彈性
|
||||
@@ -1,60 +0,0 @@
|
||||
# Proposal: add-task-management
|
||||
|
||||
## Summary
|
||||
|
||||
實作任務管理核心系統,包含 Space > Project > Task > Sub-task 多層級架構、基本 CRUD 操作、狀態管理與任務指派功能。
|
||||
|
||||
## Motivation
|
||||
|
||||
任務管理是專案控制系統的核心功能,其他模組(資源管理、協作、自動化)都依賴於任務資料。需優先建立穩固的任務管理基礎。
|
||||
|
||||
## Scope
|
||||
|
||||
### 包含 (In Scope)
|
||||
|
||||
1. **資料模型**
|
||||
- pjctrl_spaces: 空間管理
|
||||
- pjctrl_projects: 專案管理
|
||||
- pjctrl_tasks: 任務與子任務管理
|
||||
- pjctrl_task_statuses: 任務狀態定義
|
||||
|
||||
2. **API 端點**
|
||||
- Spaces CRUD: GET/POST/PATCH/DELETE /api/spaces
|
||||
- Projects CRUD: GET/POST/PATCH/DELETE /api/projects
|
||||
- Tasks CRUD: GET/POST/PATCH/DELETE /api/tasks
|
||||
- Task 狀態管理與指派
|
||||
|
||||
3. **權限控制**
|
||||
- Space/Project 層級的存取權限
|
||||
- Security Level 隔離 (public, department, confidential)
|
||||
- 任務指派權限驗證
|
||||
|
||||
4. **前端基礎**
|
||||
- Space/Project 列表與詳情頁
|
||||
- Task 列表視角 (List View)
|
||||
- 任務建立/編輯表單
|
||||
|
||||
### 不包含 (Out of Scope)
|
||||
|
||||
- 自定義欄位 (Custom Fields) - 下一階段
|
||||
- 看板視角 (Kanban View) - 下一階段
|
||||
- 甘特圖視角 (Gantt View) - 下一階段
|
||||
- 行事曆視角 (Calendar View) - 下一階段
|
||||
- WebSocket 即時同步 - 下一階段
|
||||
- 公式欄位計算 - 下一階段
|
||||
|
||||
## Affected Specs
|
||||
|
||||
- `task-management`: 新增 Purpose 區塊,補充 ADDED Requirements
|
||||
|
||||
## Dependencies
|
||||
|
||||
- `user-auth`: 需使用認證中間件與使用者資料
|
||||
|
||||
## Risks
|
||||
|
||||
1. **資料模型複雜度**: 多層級結構可能導致查詢效能問題
|
||||
- 緩解: 適當建立索引,限制子任務深度
|
||||
|
||||
2. **權限檢查效能**: 每次 API 請求都需驗證權限
|
||||
- 緩解: Redis 快取使用者權限資料
|
||||
@@ -1,80 +0,0 @@
|
||||
# Task Management
|
||||
|
||||
## Purpose
|
||||
|
||||
任務管理核心系統,支援多層級架構 (Space > Project > Task > Sub-task)、狀態管理與任務指派功能。
|
||||
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Hierarchical Task Structure
|
||||
系統 SHALL 支援多層級任務架構:空間 (Space) > 專案 (Project) > 任務 (Task) > 子任務 (Sub-task)。
|
||||
|
||||
#### Scenario: 建立空間
|
||||
- **GIVEN** 使用者擁有建立空間的權限
|
||||
- **WHEN** 使用者建立新空間
|
||||
- **THEN** 系統建立空間並設定擁有者
|
||||
- **AND** 空間可包含多個專案
|
||||
|
||||
#### Scenario: 建立專案
|
||||
- **GIVEN** 使用者在某空間內擁有建立專案的權限
|
||||
- **WHEN** 使用者建立新專案
|
||||
- **THEN** 系統建立專案並關聯至該空間
|
||||
- **AND** 設定專案的 Owner、Budget、Timeline、Security_Level
|
||||
- **AND** 自動建立預設任務狀態 (To Do, In Progress, Blocked, Done)
|
||||
|
||||
#### Scenario: 建立任務與子任務
|
||||
- **GIVEN** 使用者在專案內擁有建立任務的權限
|
||||
- **WHEN** 使用者建立任務或子任務
|
||||
- **THEN** 系統建立任務並維護父子關係
|
||||
- **AND** 子任務深度限制為 2 層
|
||||
|
||||
### Requirement: Project Security Level
|
||||
系統 SHALL 根據專案的安全等級 (Security Level) 控制存取權限。
|
||||
|
||||
#### Scenario: Public 專案存取
|
||||
- **GIVEN** 專案設定為 security_level = 'public'
|
||||
- **WHEN** 任何已登入使用者嘗試存取
|
||||
- **THEN** 系統允許存取
|
||||
|
||||
#### Scenario: Department 專案存取
|
||||
- **GIVEN** 專案設定為 security_level = 'department'
|
||||
- **WHEN** 使用者嘗試存取
|
||||
- **THEN** 系統僅允許同部門使用者或專案成員存取
|
||||
|
||||
#### Scenario: Confidential 專案存取
|
||||
- **GIVEN** 專案設定為 security_level = 'confidential'
|
||||
- **WHEN** 使用者嘗試存取
|
||||
- **THEN** 系統僅允許專案成員存取
|
||||
- **AND** 系統管理員不受此限制
|
||||
|
||||
### Requirement: Task Status Management
|
||||
系統 SHALL 管理任務狀態,支援專案層級的狀態定義。
|
||||
|
||||
#### Scenario: 預設狀態建立
|
||||
- **GIVEN** 使用者建立新專案
|
||||
- **WHEN** 專案建立完成
|
||||
- **THEN** 系統自動建立預設狀態: To Do, In Progress, Blocked, Done
|
||||
|
||||
#### Scenario: 狀態變更
|
||||
- **GIVEN** 使用者擁有更新任務的權限
|
||||
- **WHEN** 使用者變更任務狀態
|
||||
- **THEN** 系統更新狀態並記錄變更時間
|
||||
|
||||
#### Scenario: 阻礙標記
|
||||
- **GIVEN** 任務遇到阻礙無法進行
|
||||
- **WHEN** 使用者將任務狀態變更為 "Blocked"
|
||||
- **THEN** 系統設定 blocker_flag = true
|
||||
|
||||
### Requirement: Task Assignment
|
||||
系統 SHALL 支援任務指派與時間估算。
|
||||
|
||||
#### Scenario: 指派任務
|
||||
- **GIVEN** 使用者擁有指派任務的權限
|
||||
- **WHEN** 使用者將任務指派給某人
|
||||
- **THEN** 系統更新 assignee_id
|
||||
- **AND** 任務計入被指派者的工作負載
|
||||
|
||||
#### Scenario: 時間估算與追蹤
|
||||
- **GIVEN** 任務已被指派
|
||||
- **WHEN** 使用者設定 original_estimate 與回報 time_spent
|
||||
- **THEN** 系統記錄並可計算剩餘時間
|
||||
@@ -1,163 +0,0 @@
|
||||
# Tasks: add-task-management
|
||||
|
||||
## 1. 資料庫模型
|
||||
|
||||
- [x] 1.1 建立 `pjctrl_spaces` migration
|
||||
- [x] 1.2 建立 `pjctrl_projects` migration
|
||||
- [x] 1.3 建立 `pjctrl_task_statuses` migration
|
||||
- [x] 1.4 建立 `pjctrl_tasks` migration
|
||||
- [x] 1.5 建立 SQLAlchemy models (Space, Project, TaskStatus, Task)
|
||||
- [x] 1.6 驗證 migration 可正確執行與回滾
|
||||
|
||||
## 2. Pydantic Schemas
|
||||
|
||||
- [x] 2.1 建立 Space schemas (Create, Update, Response)
|
||||
- [x] 2.2 建立 Project schemas (Create, Update, Response)
|
||||
- [x] 2.3 建立 TaskStatus schemas (Create, Update, Response)
|
||||
- [x] 2.4 建立 Task schemas (Create, Update, Response)
|
||||
|
||||
## 3. 權限中間件
|
||||
|
||||
- [x] 3.1 實作 Space 權限檢查 (`check_space_access`)
|
||||
- [x] 3.2 實作 Project 權限檢查 (`check_project_access`)
|
||||
- [x] 3.3 實作 Security Level 過濾邏輯
|
||||
- [x] 3.4 整合至現有 auth middleware
|
||||
|
||||
## 4. Spaces API
|
||||
|
||||
- [x] 4.1 實作 `GET /api/spaces` (列出可見空間)
|
||||
- [x] 4.2 實作 `POST /api/spaces` (建立空間)
|
||||
- [x] 4.3 實作 `GET /api/spaces/{id}` (取得詳情)
|
||||
- [x] 4.4 實作 `PATCH /api/spaces/{id}` (更新空間)
|
||||
- [x] 4.5 實作 `DELETE /api/spaces/{id}` (軟刪除)
|
||||
|
||||
## 5. Projects API
|
||||
|
||||
- [x] 5.1 實作 `GET /api/spaces/{space_id}/projects` (列出專案)
|
||||
- [x] 5.2 實作 `POST /api/spaces/{space_id}/projects` (建立專案)
|
||||
- [x] 5.3 實作 `GET /api/projects/{id}` (取得詳情)
|
||||
- [x] 5.4 實作 `PATCH /api/projects/{id}` (更新專案)
|
||||
- [x] 5.5 實作 `DELETE /api/projects/{id}` (刪除專案)
|
||||
- [x] 5.6 建立專案時自動建立預設狀態
|
||||
|
||||
## 6. Tasks API
|
||||
|
||||
- [x] 6.1 實作 `GET /api/projects/{project_id}/tasks` (列出任務)
|
||||
- [x] 6.2 實作 `POST /api/projects/{project_id}/tasks` (建立任務)
|
||||
- [x] 6.3 實作 `GET /api/tasks/{id}` (取得詳情)
|
||||
- [x] 6.4 實作 `PATCH /api/tasks/{id}` (更新任務)
|
||||
- [x] 6.5 實作 `DELETE /api/tasks/{id}` (刪除任務)
|
||||
- [x] 6.6 實作 `PATCH /api/tasks/{id}/status` (變更狀態)
|
||||
- [x] 6.7 實作 `PATCH /api/tasks/{id}/assign` (指派任務)
|
||||
- [x] 6.8 實作子任務建立 (parent_task_id)
|
||||
|
||||
## 7. 測試
|
||||
|
||||
- [x] 7.1 撰寫 Space API 單元測試
|
||||
- [x] 7.2 撰寫 Project API 單元測試
|
||||
- [x] 7.3 撰寫 Task API 單元測試
|
||||
- [x] 7.4 撰寫權限檢查測試 (Security Level)
|
||||
- [x] 7.5 撰寫子任務層級限制測試
|
||||
- [x] 7.6 整合測試
|
||||
|
||||
## 8. 前端 (基礎)
|
||||
|
||||
- [x] 8.1 建立 Spaces 列表頁面
|
||||
- [x] 8.2 建立 Projects 列表頁面
|
||||
- [x] 8.3 建立 Tasks 列表視角 (ListView)
|
||||
- [x] 8.4 建立任務建立/編輯表單
|
||||
- [x] 8.5 實作任務狀態變更
|
||||
- [x] 8.6 實作任務指派功能
|
||||
|
||||
## Dependencies
|
||||
|
||||
```
|
||||
1.x (資料庫) → 2.x (Schemas) → 3.x (權限)
|
||||
↓
|
||||
4.x (Spaces API)
|
||||
↓
|
||||
5.x (Projects API)
|
||||
↓
|
||||
6.x (Tasks API)
|
||||
↓
|
||||
7.x (測試)
|
||||
|
||||
8.x (前端) 可與 4.x-6.x 並行開發
|
||||
```
|
||||
|
||||
## Notes
|
||||
|
||||
- 所有資料表使用 `pjctrl_` 前綴
|
||||
- 子任務深度限制為 2 層
|
||||
- 每個新專案自動建立 4 個預設狀態
|
||||
- Security Level 為 'confidential' 時需檢查專案成員資格
|
||||
|
||||
## Implementation Summary
|
||||
|
||||
完成日期: 2024-12-28
|
||||
E2E 測試完成日期: 2024-12-29
|
||||
|
||||
### 已建立的檔案
|
||||
|
||||
**Backend:**
|
||||
- `migrations/versions/002_task_management_tables.py` - 資料庫 migration
|
||||
- `app/models/space.py` - Space model
|
||||
- `app/models/project.py` - Project model
|
||||
- `app/models/task_status.py` - TaskStatus model
|
||||
- `app/models/task.py` - Task model
|
||||
- `app/schemas/space.py` - Space schemas
|
||||
- `app/schemas/project.py` - Project schemas
|
||||
- `app/schemas/task_status.py` - TaskStatus schemas
|
||||
- `app/schemas/task.py` - Task schemas
|
||||
- `app/api/spaces/router.py` - Spaces API
|
||||
- `app/api/projects/router.py` - Projects API
|
||||
- `app/api/tasks/router.py` - Tasks API
|
||||
- `tests/test_spaces.py` - Space 測試
|
||||
- `tests/test_projects.py` - Project 測試
|
||||
- `tests/test_tasks.py` - Task 測試
|
||||
|
||||
**Frontend:**
|
||||
- `src/pages/Spaces.tsx` - Spaces 列表頁面
|
||||
- `src/pages/Projects.tsx` - Projects 列表頁面
|
||||
- `src/pages/Tasks.tsx` - Tasks 列表頁面
|
||||
- `src/components/Layout.tsx` - 共用 Layout 元件
|
||||
|
||||
### 測試結果
|
||||
|
||||
36 tests passed (包含 user-auth 的 13 個測試)
|
||||
|
||||
### E2E 測試結果 (2024-12-29)
|
||||
|
||||
使用真實帳號 (ymirliu@panjit.com.tw) 進行端對端測試:
|
||||
|
||||
**Spaces API:**
|
||||
- [x] POST /api/spaces - 建立空間
|
||||
- [x] GET /api/spaces - 列出空間
|
||||
- [x] GET /api/spaces/{id} - 取得空間詳情
|
||||
- [x] PATCH /api/spaces/{id} - 更新空間
|
||||
- [x] DELETE /api/spaces/{id} - 軟刪除空間
|
||||
|
||||
**Projects API:**
|
||||
- [x] POST /api/spaces/{space_id}/projects - 建立專案
|
||||
- [x] GET /api/spaces/{space_id}/projects - 列出專案
|
||||
- [x] GET /api/projects/{id} - 取得專案詳情
|
||||
- [x] PATCH /api/projects/{id} - 更新專案
|
||||
- [x] DELETE /api/projects/{id} - 刪除專案
|
||||
- [x] GET /api/projects/{id}/statuses - 驗證 4 個預設狀態自動建立
|
||||
|
||||
**Tasks API:**
|
||||
- [x] POST /api/projects/{project_id}/tasks - 建立任務
|
||||
- [x] GET /api/projects/{project_id}/tasks - 列出任務
|
||||
- [x] GET /api/tasks/{id} - 取得任務詳情
|
||||
- [x] PATCH /api/tasks/{id} - 更新任務
|
||||
- [x] PATCH /api/tasks/{id}/status - 變更狀態
|
||||
- [x] PATCH /api/tasks/{id}/assign - 指派任務
|
||||
- [x] DELETE /api/tasks/{id} - 刪除任務
|
||||
- [x] 子任務建立 (Level 1) - 成功
|
||||
- [x] 子任務深度限制 (Level 2) - 正確拒絕
|
||||
- [x] subtask_count 自動計算 - 正確
|
||||
|
||||
**權限控制:**
|
||||
- [x] Admin 可存取 department 級別專案
|
||||
- [x] Admin 可存取 confidential 級別專案
|
||||
- [x] Security Level 過濾邏輯運作正常
|
||||
@@ -1,199 +0,0 @@
|
||||
# Design: add-user-auth
|
||||
|
||||
## Context
|
||||
|
||||
這是專案的第一個模組,需要建立認證與授權的基礎架構。系統使用外部認證 API 進行身份驗證,本地不儲存密碼。認證成功後由本地系統核發 JWT Token 並管理 Session。
|
||||
|
||||
### Stakeholders
|
||||
- 所有系統使用者(工程師、主管、PMO)
|
||||
- 系統管理員 (ymirliu@panjit.com.tw)
|
||||
- 外部認證 API 維護者
|
||||
|
||||
### Constraints
|
||||
- 必須使用外部 API 認證,不可本地繞過
|
||||
- 資料表必須使用 `pjctrl_` 前綴
|
||||
- 需支援部門級資料隔離
|
||||
|
||||
## Goals / Non-Goals
|
||||
|
||||
### Goals
|
||||
- 實現安全的外部 API 認證整合
|
||||
- 建立 RBAC 權限控制框架
|
||||
- 支援部門級資料隔離
|
||||
- 預設系統管理員帳號
|
||||
|
||||
### Non-Goals
|
||||
- 本地密碼儲存與驗證
|
||||
- 多因素認證 (MFA) - 由外部 API 處理
|
||||
- OAuth2 第三方登入
|
||||
- 使用者自助註冊
|
||||
|
||||
## Decisions
|
||||
|
||||
### Decision 1: 認證流程架構
|
||||
|
||||
**選擇**: Frontend-to-External-API 模式
|
||||
|
||||
```
|
||||
┌─────────┐ ┌─────────────┐ ┌──────────────────────────┐
|
||||
│ User │────▶│ Frontend │────▶│ pj-auth-api.vercel.app │
|
||||
└─────────┘ └─────────────┘ └──────────────────────────┘
|
||||
│ │
|
||||
│◀────── Auth Token ──────│
|
||||
│
|
||||
▼
|
||||
┌─────────────┐
|
||||
│ Backend │
|
||||
│ - 驗證 Token│
|
||||
│ - 核發 JWT │
|
||||
│ - 建立 Session
|
||||
└─────────────┘
|
||||
```
|
||||
|
||||
**原因**:
|
||||
- 減少後端介入認證流程的攻擊面
|
||||
- 外部 API 已處理密碼安全性
|
||||
- 後端只負責驗證與授權
|
||||
|
||||
**替代方案考量**:
|
||||
- Backend Proxy 模式:增加延遲與複雜度,無明顯優勢
|
||||
|
||||
### Decision 2: JWT Token 策略
|
||||
|
||||
**選擇**: 短期 Access Token + Redis Session
|
||||
|
||||
```python
|
||||
JWT_PAYLOAD = {
|
||||
"sub": "user_id",
|
||||
"email": "user@example.com",
|
||||
"role": "engineer",
|
||||
"department_id": "uuid",
|
||||
"is_system_admin": false,
|
||||
"exp": "15 minutes from now",
|
||||
"iat": "now"
|
||||
}
|
||||
```
|
||||
|
||||
**原因**:
|
||||
- 短期 Token 減少被盜用風險
|
||||
- Redis Session 可即時撤銷
|
||||
- 權限資訊嵌入 Token 減少 DB 查詢
|
||||
|
||||
### Decision 3: 權限模型
|
||||
|
||||
**選擇**: RBAC + 部門隔離混合模式
|
||||
|
||||
```python
|
||||
# 權限檢查順序
|
||||
def check_permission(user, resource, action):
|
||||
# 1. 系統管理員 - 全權通過
|
||||
if user.is_system_admin:
|
||||
return True
|
||||
|
||||
# 2. 角色權限檢查
|
||||
if not has_role_permission(user.role, action):
|
||||
return False
|
||||
|
||||
# 3. 部門隔離檢查
|
||||
if not is_same_department_or_allowed(user, resource):
|
||||
return False
|
||||
|
||||
return True
|
||||
```
|
||||
|
||||
**原因**:
|
||||
- 系統管理員需無限制存取(用於問題排查)
|
||||
- RBAC 提供功能層面控制
|
||||
- 部門隔離提供資料層面控制
|
||||
|
||||
### Decision 4: 資料表設計
|
||||
|
||||
```sql
|
||||
-- 使用 UUID 而非 auto-increment
|
||||
-- 便於分散式環境與資料合併
|
||||
|
||||
CREATE TABLE pjctrl_roles (
|
||||
id CHAR(36) PRIMARY KEY,
|
||||
name VARCHAR(50) NOT NULL UNIQUE,
|
||||
permissions JSON NOT NULL,
|
||||
is_system_role BOOLEAN DEFAULT FALSE,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
CREATE TABLE pjctrl_departments (
|
||||
id CHAR(36) PRIMARY KEY,
|
||||
name VARCHAR(100) NOT NULL,
|
||||
parent_id CHAR(36) REFERENCES pjctrl_departments(id),
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
CREATE TABLE pjctrl_users (
|
||||
id CHAR(36) PRIMARY KEY,
|
||||
email VARCHAR(200) NOT NULL UNIQUE,
|
||||
name VARCHAR(200) NOT NULL,
|
||||
department_id CHAR(36) REFERENCES pjctrl_departments(id),
|
||||
role_id CHAR(36) REFERENCES pjctrl_roles(id),
|
||||
skills JSON,
|
||||
capacity DECIMAL(5,2) DEFAULT 40.00,
|
||||
is_active BOOLEAN DEFAULT TRUE,
|
||||
is_system_admin BOOLEAN DEFAULT FALSE,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
|
||||
);
|
||||
```
|
||||
|
||||
## Risks / Trade-offs
|
||||
|
||||
| 風險 | 影響 | 緩解措施 |
|
||||
|------|------|---------|
|
||||
| 外部 API 不可用 | 所有使用者無法登入 | 顯示友善錯誤訊息、記錄事件、監控告警 |
|
||||
| JWT Token 洩漏 | 短期內帳號被盜用 | 15 分鐘過期、Redis 可即時撤銷 |
|
||||
| 部門隔離邏輯錯誤 | 資料外洩 | 完整測試案例、程式碼審查 |
|
||||
|
||||
## Migration Plan
|
||||
|
||||
1. 執行資料庫 migration 建立資料表
|
||||
2. 執行 seed data 建立預設角色與管理員
|
||||
3. 部署後端認證模組
|
||||
4. 部署前端登入頁面
|
||||
5. 驗證管理員可登入
|
||||
|
||||
**Rollback**:
|
||||
- 資料庫:執行 down migration
|
||||
- 程式碼:回復至前一版本
|
||||
|
||||
## Environment Configuration
|
||||
|
||||
```bash
|
||||
# Database
|
||||
MYSQL_HOST=mysql.theaken.com
|
||||
MYSQL_PORT=33306
|
||||
MYSQL_USER=A060
|
||||
MYSQL_PASSWORD=<見密碼管理>
|
||||
MYSQL_DATABASE=db_A060
|
||||
|
||||
# Redis (待設定)
|
||||
REDIS_HOST=localhost
|
||||
REDIS_PORT=6379
|
||||
|
||||
# JWT
|
||||
JWT_SECRET_KEY=<生成隨機金鑰>
|
||||
JWT_ALGORITHM=HS256
|
||||
JWT_EXPIRE_MINUTES=15
|
||||
|
||||
# External Auth API
|
||||
AUTH_API_URL=https://pj-auth-api.vercel.app
|
||||
```
|
||||
|
||||
## Admin Credentials
|
||||
|
||||
- **Email**: `ymirliu@panjit.com.tw`
|
||||
- **Password**: 由外部認證 API 管理(已設定於外部系統)
|
||||
|
||||
## Open Questions
|
||||
|
||||
1. ~~外部認證 API 的回應格式?~~ (需確認)
|
||||
2. ~~Session 過期時間?~~ 建議 15 分鐘,可透過環境變數調整
|
||||
3. ~~是否需要 Refresh Token 機制?~~ 暫不需要,視實際使用情況再評估
|
||||
4. ~~MySQL 連線資訊?~~ 已提供
|
||||
5. ~~Admin 帳號密碼?~~ 已提供 (ymirliu@panjit.com.tw)
|
||||
@@ -1,38 +0,0 @@
|
||||
# Change: Add User Authentication & Authorization
|
||||
|
||||
## Why
|
||||
|
||||
系統需要使用者認證與授權機制作為所有功能的基礎。沒有認證系統,無法識別使用者身份、無法實施權限控制、無法追蹤操作記錄。這是整個專案管理系統的第一個必要模組。
|
||||
|
||||
## What Changes
|
||||
|
||||
- **新增** 外部 API 認證整合 (https://pj-auth-api.vercel.app)
|
||||
- **新增** JWT Token 驗證與 Session 管理
|
||||
- **新增** 使用者資料表 (`pjctrl_users`)
|
||||
- **新增** 部門資料表 (`pjctrl_departments`)
|
||||
- **新增** 角色資料表 (`pjctrl_roles`)
|
||||
- **新增** 預設系統管理員帳號 (`ymirliu@panjit.com.tw`)
|
||||
- **新增** RBAC 權限檢查中間件
|
||||
- **新增** 部門級資料隔離機制
|
||||
|
||||
## Impact
|
||||
|
||||
- **Affected specs**: `user-auth` (新增)
|
||||
- **Affected code**:
|
||||
- Backend: `app/api/auth/`, `app/models/user.py`, `app/core/security.py`
|
||||
- Frontend: `src/contexts/AuthContext.tsx`, `src/pages/Login.tsx`
|
||||
- Database: Migration for `pjctrl_users`, `pjctrl_departments`, `pjctrl_roles`
|
||||
|
||||
## Dependencies
|
||||
|
||||
- 外部認證 API: https://pj-auth-api.vercel.app (必須可用)
|
||||
- MySQL 資料庫連線
|
||||
- Redis (用於 Session 儲存)
|
||||
|
||||
## Success Criteria
|
||||
|
||||
1. 使用者可透過外部 API 完成登入
|
||||
2. JWT Token 正確核發與驗證
|
||||
3. 系統管理員帳號可登入並存取所有資源
|
||||
4. 非授權使用者無法存取受保護的 API
|
||||
5. 部門隔離正確運作
|
||||
@@ -1,86 +0,0 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: API-Based Authentication
|
||||
系統 SHALL 限定使用外部認證 API (https://pj-auth-api.vercel.app) 進行登入認證,不支援其他認證方式。
|
||||
|
||||
#### Scenario: API 登入成功
|
||||
- **GIVEN** 使用者擁有有效的企業帳號
|
||||
- **WHEN** 使用者透過前端提交憑證
|
||||
- **THEN** 系統呼叫 https://pj-auth-api.vercel.app 驗證憑證
|
||||
- **AND** 驗證成功後建立 session 並回傳 JWT token
|
||||
|
||||
#### Scenario: API 登入失敗
|
||||
- **GIVEN** 使用者提供無效的憑證
|
||||
- **WHEN** 使用者嘗試登入
|
||||
- **THEN** 認證 API 回傳錯誤
|
||||
- **AND** 系統拒絕登入並顯示錯誤訊息
|
||||
- **AND** 記錄失敗的登入嘗試
|
||||
|
||||
#### Scenario: 認證 API 無法連線
|
||||
- **GIVEN** 認證 API 服務無法連線
|
||||
- **WHEN** 使用者嘗試登入
|
||||
- **THEN** 系統顯示服務暫時無法使用的訊息
|
||||
- **AND** 記錄連線失敗事件
|
||||
|
||||
### Requirement: System Administrator
|
||||
系統 SHALL 預設一個系統管理員帳號,擁有所有權限。系統管理員帳號必須存在於外部認證系統,且登入流程仍需透過外部認證 API;不允許本地繞過認證。
|
||||
|
||||
#### Scenario: 預設管理員帳號
|
||||
- **GIVEN** 系統初始化完成
|
||||
- **WHEN** 系統啟動
|
||||
- **THEN** 存在預設管理員帳號 ymirliu@panjit.com.tw
|
||||
- **AND** 該帳號擁有 super_admin 角色
|
||||
- **AND** 該帳號不可被刪除或降級
|
||||
|
||||
#### Scenario: 管理員登入流程
|
||||
- **GIVEN** 管理員帳號 ymirliu@panjit.com.tw 需要登入
|
||||
- **WHEN** 管理員提交憑證
|
||||
- **THEN** 系統仍需呼叫 https://pj-auth-api.vercel.app 驗證
|
||||
- **AND** 不存在任何本地繞過認證的機制
|
||||
- **AND** 驗證成功後才授予 super_admin 權限
|
||||
|
||||
#### Scenario: 管理員全域權限
|
||||
- **GIVEN** 管理員帳號 ymirliu@panjit.com.tw 已通過 API 認證並登入
|
||||
- **WHEN** 管理員存取任何資源
|
||||
- **THEN** 系統允許存取,無視部門隔離限制
|
||||
|
||||
### Requirement: Role-Based Access Control
|
||||
系統 SHALL 支援基於角色的存取控制 (RBAC)。
|
||||
|
||||
#### Scenario: 角色權限檢查
|
||||
- **GIVEN** 使用者被指派特定角色
|
||||
- **WHEN** 使用者嘗試存取受保護的資源
|
||||
- **THEN** 系統根據角色權限決定是否允許存取
|
||||
|
||||
#### Scenario: 角色指派
|
||||
- **GIVEN** 管理員擁有使用者管理權限
|
||||
- **WHEN** 管理員為使用者指派角色
|
||||
- **THEN** 系統更新使用者的角色設定
|
||||
- **AND** 新權限立即生效
|
||||
|
||||
### Requirement: Department Isolation
|
||||
系統 SHALL 實施部門級別的資料隔離,確保跨部門資料安全。
|
||||
|
||||
#### Scenario: 部門資料隔離
|
||||
- **GIVEN** 使用者屬於研發部門
|
||||
- **WHEN** 使用者嘗試存取廠務部門的專案
|
||||
- **THEN** 系統拒絕存取並顯示無權限訊息
|
||||
|
||||
#### Scenario: 跨部門專案存取
|
||||
- **GIVEN** 專案被設定為跨部門可見
|
||||
- **WHEN** 不同部門的使用者嘗試存取該專案
|
||||
- **THEN** 系統根據專案的 Security_Level 設定決定是否允許存取
|
||||
|
||||
### Requirement: Session Management
|
||||
系統 SHALL 管理使用者 session,包含過期與登出機制。
|
||||
|
||||
#### Scenario: Session 過期
|
||||
- **GIVEN** 使用者已登入系統
|
||||
- **WHEN** Session 超過設定的有效期限
|
||||
- **THEN** 系統自動使 session 失效
|
||||
- **AND** 使用者需重新登入
|
||||
|
||||
#### Scenario: 主動登出
|
||||
- **GIVEN** 使用者已登入系統
|
||||
- **WHEN** 使用者執行登出操作
|
||||
- **THEN** 系統銷毀 session 並清除 token
|
||||
@@ -1,138 +0,0 @@
|
||||
# Tasks: add-user-auth
|
||||
|
||||
## 1. 專案初始化
|
||||
|
||||
- [x] 1.1 建立 Conda 環境與 requirements.txt
|
||||
- [x] 1.2 初始化 FastAPI 專案結構
|
||||
- [x] 1.3 設定 MySQL 連線與 SQLAlchemy
|
||||
- [x] 1.4 設定 Redis 連線
|
||||
- [x] 1.5 建立環境變數配置 (.env.example)
|
||||
|
||||
## 2. 資料庫模型
|
||||
|
||||
- [x] 2.1 建立 `pjctrl_roles` 資料表 migration
|
||||
- [x] 2.2 建立 `pjctrl_departments` 資料表 migration
|
||||
- [x] 2.3 建立 `pjctrl_users` 資料表 migration
|
||||
- [x] 2.4 建立 seed data (預設管理員與角色)
|
||||
- [x] 2.5 驗證 migration 可正確執行與回滾
|
||||
|
||||
## 3. 認證模組
|
||||
|
||||
- [x] 3.1 實作外部 API 認證 client (`app/services/auth_client.py`)
|
||||
- [x] 3.2 實作 JWT Token 驗證邏輯
|
||||
- [x] 3.3 實作登入 API endpoint (`POST /api/auth/login`)
|
||||
- [x] 3.4 實作登出 API endpoint (`POST /api/auth/logout`)
|
||||
- [x] 3.5 實作取得當前使用者 API (`GET /api/auth/me`)
|
||||
- [x] 3.6 處理認證 API 連線失敗情境
|
||||
|
||||
## 4. Session 管理
|
||||
|
||||
- [x] 4.1 實作 Redis Session 儲存
|
||||
- [x] 4.2 實作 Session 過期機制
|
||||
- [x] 4.3 實作 Token 刷新機制 (如需要) - 暫不需要
|
||||
|
||||
## 5. 權限控制
|
||||
|
||||
- [x] 5.1 實作認證中間件 (`app/middleware/auth.py`)
|
||||
- [x] 5.2 實作 RBAC 權限檢查裝飾器
|
||||
- [x] 5.3 實作部門隔離邏輯
|
||||
- [x] 5.4 實作系統管理員全域權限判斷
|
||||
|
||||
## 6. 使用者管理 API
|
||||
|
||||
- [x] 6.1 實作使用者列表 API (`GET /api/users`)
|
||||
- [x] 6.2 實作使用者詳情 API (`GET /api/users/{id}`)
|
||||
- [x] 6.3 實作角色指派 API (`PATCH /api/users/{id}/role`)
|
||||
- [x] 6.4 實作部門管理 API (CRUD)
|
||||
|
||||
## 7. 測試
|
||||
|
||||
- [x] 7.1 撰寫認證模組單元測試
|
||||
- [x] 7.2 撰寫權限檢查單元測試
|
||||
- [x] 7.3 撰寫 API 整合測試
|
||||
- [x] 7.4 測試系統管理員權限
|
||||
- [x] 7.5 測試部門隔離情境
|
||||
|
||||
## 8. 前端 (基礎)
|
||||
|
||||
- [x] 8.1 建立 React 專案結構
|
||||
- [x] 8.2 實作 AuthContext (認證狀態管理)
|
||||
- [x] 8.3 實作登入頁面
|
||||
- [x] 8.4 實作 Protected Route 元件
|
||||
- [x] 8.5 實作登出功能
|
||||
|
||||
## Dependencies
|
||||
|
||||
```
|
||||
1.x (專案初始化) → 2.x (資料庫) → 3.x (認證) → 4.x (Session)
|
||||
↓
|
||||
5.x (權限) → 6.x (使用者管理)
|
||||
↓
|
||||
7.x (測試)
|
||||
|
||||
8.x (前端) 可與 3.x-6.x 並行開發
|
||||
```
|
||||
|
||||
## Notes
|
||||
|
||||
- 所有資料表使用 `pjctrl_` 前綴
|
||||
- 認證必須透過外部 API,不可有本地繞過
|
||||
- 系統管理員帳號在 seed data 中建立
|
||||
|
||||
## Implementation Summary
|
||||
|
||||
完成日期: 2024-01-XX
|
||||
|
||||
### 已建立的檔案結構
|
||||
|
||||
```
|
||||
backend/
|
||||
├── app/
|
||||
│ ├── api/
|
||||
│ │ ├── auth/router.py # 認證 API
|
||||
│ │ ├── users/router.py # 使用者管理 API
|
||||
│ │ └── departments/router.py # 部門管理 API
|
||||
│ ├── core/
|
||||
│ │ ├── config.py # 環境配置
|
||||
│ │ ├── database.py # 資料庫連線
|
||||
│ │ ├── redis.py # Redis 連線
|
||||
│ │ └── security.py # JWT 處理
|
||||
│ ├── middleware/
|
||||
│ │ └── auth.py # 認證與權限中間件
|
||||
│ ├── models/
|
||||
│ │ ├── user.py # User model
|
||||
│ │ ├── role.py # Role model
|
||||
│ │ └── department.py # Department model
|
||||
│ ├── schemas/ # Pydantic schemas
|
||||
│ ├── services/
|
||||
│ │ └── auth_client.py # 外部認證 API client
|
||||
│ └── main.py # FastAPI 應用程式
|
||||
├── migrations/
|
||||
│ └── versions/
|
||||
│ └── 001_initial_auth_tables.py
|
||||
├── tests/
|
||||
│ ├── conftest.py
|
||||
│ ├── test_auth.py
|
||||
│ └── test_users.py
|
||||
├── .env
|
||||
├── .env.example
|
||||
├── requirements.txt
|
||||
└── environment.yml
|
||||
|
||||
frontend/
|
||||
├── src/
|
||||
│ ├── components/
|
||||
│ │ └── ProtectedRoute.tsx
|
||||
│ ├── contexts/
|
||||
│ │ └── AuthContext.tsx
|
||||
│ ├── pages/
|
||||
│ │ ├── Login.tsx
|
||||
│ │ └── Dashboard.tsx
|
||||
│ ├── services/
|
||||
│ │ └── api.ts
|
||||
│ ├── App.tsx
|
||||
│ └── main.tsx
|
||||
├── package.json
|
||||
├── tsconfig.json
|
||||
└── vite.config.ts
|
||||
```
|
||||
@@ -1,147 +0,0 @@
|
||||
# Design: add-audit-trail
|
||||
|
||||
## Architecture Decision
|
||||
|
||||
### Approach: Application-layer Middleware
|
||||
|
||||
選擇應用層中間件而非資料庫觸發器:
|
||||
|
||||
| 方案 | 優點 | 缺點 |
|
||||
|-----|------|-----|
|
||||
| **Middleware (選擇)** | 可取得完整 context (user, IP)、跨資料庫相容 | 需要在每個 API 加入 |
|
||||
| DB Trigger | 自動捕捉所有變更 | 無法取得 user context、MySQL 觸發器效能差 |
|
||||
|
||||
### Implementation Strategy
|
||||
|
||||
```
|
||||
Request → FastAPI Middleware → Extract metadata (user, IP, user_agent)
|
||||
↓
|
||||
API Handler → Execute operation
|
||||
↓
|
||||
AuditService.log() → Async write to pjctrl_audit_logs
|
||||
↓
|
||||
(if sensitive) → NotificationService → Alert admins
|
||||
```
|
||||
|
||||
## Data Model
|
||||
|
||||
### pjctrl_audit_logs
|
||||
|
||||
```sql
|
||||
CREATE TABLE pjctrl_audit_logs (
|
||||
id VARCHAR(36) PRIMARY KEY,
|
||||
event_type VARCHAR(50) NOT NULL,
|
||||
resource_type VARCHAR(50) NOT NULL,
|
||||
resource_id VARCHAR(36),
|
||||
user_id VARCHAR(36),
|
||||
action ENUM('create', 'update', 'delete', 'restore', 'login', 'logout') NOT NULL,
|
||||
changes JSON,
|
||||
metadata JSON,
|
||||
sensitivity_level ENUM('low', 'medium', 'high', 'critical') DEFAULT 'low',
|
||||
checksum VARCHAR(64) NOT NULL,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
INDEX idx_audit_user (user_id, created_at),
|
||||
INDEX idx_audit_resource (resource_type, resource_id, created_at),
|
||||
INDEX idx_audit_time (created_at),
|
||||
|
||||
FOREIGN KEY (user_id) REFERENCES pjctrl_users(id) ON DELETE SET NULL
|
||||
);
|
||||
```
|
||||
|
||||
### pjctrl_audit_alerts
|
||||
|
||||
```sql
|
||||
CREATE TABLE pjctrl_audit_alerts (
|
||||
id VARCHAR(36) PRIMARY KEY,
|
||||
audit_log_id VARCHAR(36) NOT NULL,
|
||||
alert_type VARCHAR(50) NOT NULL,
|
||||
recipients JSON NOT NULL,
|
||||
message TEXT,
|
||||
is_acknowledged BOOLEAN DEFAULT FALSE,
|
||||
acknowledged_by VARCHAR(36),
|
||||
acknowledged_at TIMESTAMP,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
FOREIGN KEY (audit_log_id) REFERENCES pjctrl_audit_logs(id),
|
||||
FOREIGN KEY (acknowledged_by) REFERENCES pjctrl_users(id)
|
||||
);
|
||||
```
|
||||
|
||||
## Checksum Calculation
|
||||
|
||||
確保日誌不可竄改:
|
||||
|
||||
```python
|
||||
import hashlib
|
||||
import json
|
||||
|
||||
def calculate_checksum(log: AuditLog) -> str:
|
||||
content = f"{log.event_type}|{log.resource_id}|{log.user_id}|{json.dumps(log.changes, sort_keys=True)}|{log.created_at.isoformat()}"
|
||||
return hashlib.sha256(content.encode()).hexdigest()
|
||||
```
|
||||
|
||||
## Sensitivity Levels & Event Types
|
||||
|
||||
| Event Type | Sensitivity | Alert |
|
||||
|-----------|-------------|-------|
|
||||
| task.create, task.update, task.assign | low | No |
|
||||
| task.delete, task.blocker | medium | No |
|
||||
| project.create, project.update | medium | No |
|
||||
| project.delete | high | Yes |
|
||||
| user.permission_change | critical | Yes |
|
||||
| user.login (異常) | high | Yes |
|
||||
|
||||
## API Design
|
||||
|
||||
### Query Audit Logs
|
||||
|
||||
```
|
||||
GET /api/audit-logs
|
||||
Query params:
|
||||
- start_date: datetime
|
||||
- end_date: datetime
|
||||
- user_id: UUID (optional)
|
||||
- resource_type: string (optional)
|
||||
- resource_id: UUID (optional)
|
||||
- sensitivity_level: string (optional)
|
||||
- limit: int (default 50, max 100)
|
||||
- offset: int
|
||||
```
|
||||
|
||||
### Resource History
|
||||
|
||||
```
|
||||
GET /api/audit-logs/resource/{resource_type}/{resource_id}
|
||||
Returns: Change history for specific resource
|
||||
```
|
||||
|
||||
### Export
|
||||
|
||||
```
|
||||
GET /api/audit-logs/export
|
||||
Query params: same as query + format=csv
|
||||
Returns: CSV file download
|
||||
```
|
||||
|
||||
### Integrity Check
|
||||
|
||||
```
|
||||
POST /api/audit-logs/verify-integrity
|
||||
Body: { start_date, end_date }
|
||||
Returns: { total_checked, valid_count, invalid_records: [] }
|
||||
```
|
||||
|
||||
## Integration Points
|
||||
|
||||
1. **Task API**: Log create/update/delete/assign
|
||||
2. **Project API**: Log create/update/delete
|
||||
3. **User API**: Log permission changes
|
||||
4. **Auth Middleware**: Log login/logout
|
||||
5. **Blocker API**: Log blocker events
|
||||
|
||||
## Alert Thresholds
|
||||
|
||||
- **Bulk delete**: > 5 deletes within 5 minutes
|
||||
- **Off-hours login**: Outside 06:00-22:00 local time
|
||||
- **Permission escalation**: Any admin role assignment
|
||||
@@ -1,40 +0,0 @@
|
||||
# Proposal: add-audit-trail
|
||||
|
||||
## Why
|
||||
|
||||
半導體產業對資料追溯有嚴格的合規需求。目前系統缺乏統一的稽核日誌機制,無法追蹤:
|
||||
- 誰在何時修改了什麼資料
|
||||
- 關鍵操作(如權限變更、資料刪除)的完整記錄
|
||||
- 異常行為的即時警示
|
||||
|
||||
此變更建立系統級稽核追蹤功能,為未來 document-management 模組提供基礎。
|
||||
|
||||
## What Changes
|
||||
|
||||
### Backend
|
||||
- 新增 AuditLog、AuditAlert models
|
||||
- 新增 AuditService (中間件自動記錄)
|
||||
- 新增 `/api/audit-logs` 查詢 API
|
||||
- 新增稽核報告匯出功能 (CSV)
|
||||
- 整合 NotificationService 發送敏感操作警示
|
||||
|
||||
### Frontend
|
||||
- 新增稽核日誌查詢頁面 (Admin only)
|
||||
- 新增資源變更歷史元件 (Task/Project 詳情頁)
|
||||
|
||||
### Database
|
||||
- 新增 `pjctrl_audit_logs` 表 (append-only)
|
||||
- 新增 `pjctrl_audit_alerts` 表
|
||||
|
||||
## Impact
|
||||
|
||||
- **依賴**: 使用現有 NotificationService 發送警示
|
||||
- **被依賴**: document-management 將使用此稽核功能
|
||||
- **權限**: 稽核查詢限 system_admin
|
||||
- **效能**: 使用非同步寫入避免影響主流程
|
||||
|
||||
## Out of Scope
|
||||
|
||||
- 時間序列資料庫(先用 MySQL,未來可擴展)
|
||||
- PDF 匯出(先實作 CSV)
|
||||
- 資料庫觸發器(使用應用層中間件)
|
||||
@@ -1,94 +0,0 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Change Logging
|
||||
系統 SHALL 記錄所有關鍵變更操作,包含誰在何時改了什麼。
|
||||
|
||||
#### Scenario: 任務欄位變更記錄
|
||||
- **GIVEN** 使用者修改任務的任何欄位(如截止日期、狀態、指派者)
|
||||
- **WHEN** 變更儲存成功
|
||||
- **THEN** 系統記錄變更前後的值
|
||||
- **AND** 記錄操作者、時間、IP 位址
|
||||
|
||||
#### Scenario: 專案設定變更記錄
|
||||
- **GIVEN** 管理者修改專案設定
|
||||
- **WHEN** 設定變更儲存
|
||||
- **THEN** 系統記錄所有變更的設定項目
|
||||
- **AND** 記錄操作者與時間
|
||||
|
||||
#### Scenario: 權限變更記錄
|
||||
- **GIVEN** 管理者修改使用者權限或角色
|
||||
- **WHEN** 權限變更生效
|
||||
- **THEN** 系統記錄權限變更詳情
|
||||
- **AND** 標記為高敏感度操作
|
||||
|
||||
### Requirement: Delete Operations Tracking
|
||||
系統 SHALL 追蹤所有刪除操作,支援軟刪除與追溯。
|
||||
|
||||
#### Scenario: 任務刪除記錄
|
||||
- **GIVEN** 使用者刪除任務
|
||||
- **WHEN** 刪除操作執行
|
||||
- **THEN** 系統執行軟刪除(標記 is_deleted = true)
|
||||
- **AND** 記錄刪除操作與原因
|
||||
|
||||
#### Scenario: 附件刪除記錄
|
||||
- **GIVEN** 使用者刪除附件
|
||||
- **WHEN** 刪除操作執行
|
||||
- **THEN** 系統保留檔案於存檔區
|
||||
- **AND** 記錄刪除操作詳情
|
||||
|
||||
### Requirement: Audit Log Immutability
|
||||
系統 SHALL 確保稽核日誌不可竄改。
|
||||
|
||||
#### Scenario: 日誌寫入
|
||||
- **GIVEN** 需要記錄稽核事件
|
||||
- **WHEN** 日誌寫入
|
||||
- **THEN** 日誌記錄不可被修改或刪除
|
||||
- **AND** 包含 SHA-256 校驗碼確保完整性
|
||||
|
||||
#### Scenario: 日誌完整性驗證
|
||||
- **GIVEN** 稽核人員需要驗證日誌完整性
|
||||
- **WHEN** 執行完整性檢查
|
||||
- **THEN** 系統驗證所有日誌記錄的校驗碼
|
||||
- **AND** 報告任何異常記錄
|
||||
|
||||
### Requirement: Audit Query Interface
|
||||
系統 SHALL 提供稽核查詢介面供授權人員使用。
|
||||
|
||||
#### Scenario: 依時間範圍查詢
|
||||
- **GIVEN** 稽核人員需要查詢特定時間範圍的操作
|
||||
- **WHEN** 設定時間範圍並執行查詢
|
||||
- **THEN** 顯示該時間範圍內的所有稽核記錄
|
||||
|
||||
#### Scenario: 依操作者查詢
|
||||
- **GIVEN** 稽核人員需要查詢特定使用者的操作歷史
|
||||
- **WHEN** 選擇使用者並執行查詢
|
||||
- **THEN** 顯示該使用者的所有操作記錄
|
||||
|
||||
#### Scenario: 依資源查詢
|
||||
- **GIVEN** 稽核人員需要查詢特定任務或專案的變更歷史
|
||||
- **WHEN** 選擇資源並執行查詢
|
||||
- **THEN** 顯示該資源的完整變更歷程
|
||||
|
||||
#### Scenario: 稽核報告匯出
|
||||
- **GIVEN** 稽核人員需要匯出稽核報告
|
||||
- **WHEN** 選擇匯出 CSV 格式
|
||||
- **THEN** 系統生成報告檔案供下載
|
||||
|
||||
### Requirement: Sensitive Operation Alerts
|
||||
系統 SHALL 對高敏感度操作發送即時警示。
|
||||
|
||||
#### Scenario: 權限提升警示
|
||||
- **GIVEN** 使用者被授予管理員權限
|
||||
- **WHEN** 權限變更生效
|
||||
- **THEN** 系統發送警示給安全管理員
|
||||
- **AND** 建立 AuditAlert 記錄
|
||||
|
||||
#### Scenario: 大量刪除警示
|
||||
- **GIVEN** 使用者在 5 分鐘內刪除超過 5 筆資料
|
||||
- **WHEN** 偵測到異常刪除模式
|
||||
- **THEN** 系統發送警示給安全管理員
|
||||
|
||||
#### Scenario: 警示確認
|
||||
- **GIVEN** 管理員收到敏感操作警示
|
||||
- **WHEN** 管理員確認警示
|
||||
- **THEN** 系統記錄確認者與確認時間
|
||||
@@ -1,84 +0,0 @@
|
||||
## 1. Database Schema
|
||||
|
||||
- [x] 1.1 建立 AuditLog model (`pjctrl_audit_logs`)
|
||||
- [x] 1.2 建立 AuditAlert model (`pjctrl_audit_alerts`)
|
||||
- [x] 1.3 建立 Alembic migration
|
||||
- [x] 1.4 建立 event types 和 sensitivity levels 常數
|
||||
|
||||
## 2. Core Audit Service
|
||||
|
||||
- [x] 2.1 建立 AuditService 核心類別
|
||||
- [x] 2.2 實作 checksum 計算邏輯
|
||||
- [x] 2.3 實作 log_event() 方法 (非同步)
|
||||
- [x] 2.4 實作 detect_changes() 方法 (比較 old/new values)
|
||||
- [x] 2.5 實作敏感度判定邏輯
|
||||
|
||||
## 3. Audit Middleware
|
||||
|
||||
- [x] 3.1 建立 AuditMiddleware 擷取 request metadata (IP, user_agent)
|
||||
- [x] 3.2 將 metadata 注入 request state
|
||||
|
||||
## 4. API Integration - Task
|
||||
|
||||
- [x] 4.1 整合 Task create 稽核
|
||||
- [x] 4.2 整合 Task update 稽核 (含 changes diff)
|
||||
- [x] 4.3 整合 Task delete 稽核
|
||||
- [x] 4.4 整合 Task assign 稽核
|
||||
|
||||
## 5. API Integration - Project
|
||||
|
||||
- [x] 5.1 整合 Project create 稽核
|
||||
- [x] 5.2 整合 Project update 稽核
|
||||
- [x] 5.3 整合 Project delete 稽核
|
||||
|
||||
## 6. API Integration - User & Auth
|
||||
|
||||
- [x] 6.1 整合 User permission change 稽核
|
||||
- [x] 6.2 整合 Login/Logout 稽核
|
||||
- [x] 6.3 整合 Blocker 事件稽核
|
||||
|
||||
## 7. Backend API - Query
|
||||
|
||||
- [x] 7.1 建立 AuditLog schemas (response)
|
||||
- [x] 7.2 實作 GET `/api/audit-logs` - 查詢稽核日誌
|
||||
- [x] 7.3 實作 GET `/api/audit-logs/resource/{type}/{id}` - 資源歷史
|
||||
- [x] 7.4 實作 query filters (時間、使用者、資源、敏感度)
|
||||
|
||||
## 8. Backend API - Export & Verify
|
||||
|
||||
- [x] 8.1 實作 GET `/api/audit-logs/export` - CSV 匯出
|
||||
- [x] 8.2 實作 POST `/api/audit-logs/verify-integrity` - 完整性驗證
|
||||
- [x] 8.3 實作分頁處理大量資料
|
||||
|
||||
## 9. Alert System
|
||||
|
||||
- [x] 9.1 建立 AuditAlert schemas
|
||||
- [x] 9.2 實作 create_alert() 方法
|
||||
- [x] 9.3 實作敏感操作警示觸發
|
||||
- [x] 9.4 實作大量刪除偵測
|
||||
- [x] 9.5 整合 NotificationService 發送警示
|
||||
- [x] 9.6 實作 PUT `/api/audit-alerts/{id}/acknowledge` - 確認警示
|
||||
|
||||
## 10. Frontend - Admin Audit Page
|
||||
|
||||
- [x] 10.1 建立 audit.ts service
|
||||
- [x] 10.2 建立 AuditLogList 元件
|
||||
- [x] 10.3 建立 AuditLogFilters 元件 (日期、使用者、資源)
|
||||
- [x] 10.4 建立 AuditLogDetail modal (顯示 changes diff)
|
||||
- [x] 10.5 建立 CSV 匯出按鈕
|
||||
- [x] 10.6 新增 Admin menu 連結
|
||||
|
||||
## 11. Frontend - Resource History
|
||||
|
||||
- [x] 11.1 建立 ResourceHistory 元件
|
||||
- [x] 11.2 整合至 Task 詳情頁
|
||||
- [x] 11.3 整合至 Project 詳情頁
|
||||
|
||||
## 12. Testing
|
||||
|
||||
- [x] 12.1 AuditService 單元測試
|
||||
- [x] 12.2 Checksum 計算測試
|
||||
- [x] 12.3 Audit API 端點測試
|
||||
- [x] 12.4 Alert 觸發測試
|
||||
- [x] 12.5 CSV 匯出測試
|
||||
- [x] 12.6 完整性驗證測試
|
||||
@@ -1,185 +0,0 @@
|
||||
## Context
|
||||
|
||||
自動化系統需要處理兩種類型的自動化:
|
||||
1. 事件驅動 - 任務欄位變更時觸發
|
||||
2. 時間驅動 - 排程執行(如週報)
|
||||
|
||||
## Goals / Non-Goals
|
||||
|
||||
**Goals:**
|
||||
- 提供欄位變更觸發器(status, assignee, priority)
|
||||
- 實作每週五 16:00 自動週報
|
||||
- 整合現有通知系統
|
||||
- 記錄所有觸發器執行日誌
|
||||
|
||||
**Non-Goals:**
|
||||
- 複雜的工作流引擎
|
||||
- 跨任務觸發器
|
||||
- Email 發送(僅系統內通知)
|
||||
|
||||
## Decisions
|
||||
|
||||
### 1. 排程方案
|
||||
|
||||
**Decision:** 使用 APScheduler 而非 Celery
|
||||
|
||||
**Rationale:**
|
||||
- 無需額外 worker 進程,減少部署複雜度
|
||||
- 嵌入 FastAPI 應用內運行
|
||||
- 足夠處理週報等簡單定時任務
|
||||
- 未來如需擴展可遷移至 Celery
|
||||
|
||||
**Configuration:**
|
||||
```python
|
||||
from apscheduler.schedulers.asyncio import AsyncIOScheduler
|
||||
from apscheduler.triggers.cron import CronTrigger
|
||||
|
||||
scheduler = AsyncIOScheduler()
|
||||
scheduler.add_job(
|
||||
generate_weekly_reports,
|
||||
CronTrigger(day_of_week='fri', hour=16, minute=0),
|
||||
id='weekly_report'
|
||||
)
|
||||
```
|
||||
|
||||
### 2. 觸發器評估策略
|
||||
|
||||
**Decision:** 同步評估,任務更新時直接執行
|
||||
|
||||
**Rationale:**
|
||||
- 簡化實作,無需消息隊列
|
||||
- 觸發器執行速度快(僅發送通知)
|
||||
- 失敗可即時回報
|
||||
|
||||
**Flow:**
|
||||
```
|
||||
Task Update → Detect Changes → Find Matching Triggers → Execute Actions → Log Results
|
||||
```
|
||||
|
||||
### 3. 條件定義格式
|
||||
|
||||
**Decision:** 使用 JSON 結構定義條件
|
||||
|
||||
```json
|
||||
{
|
||||
"field": "status_id",
|
||||
"operator": "equals",
|
||||
"value": "uuid-of-testing-status"
|
||||
}
|
||||
```
|
||||
|
||||
**Supported Operators:**
|
||||
- `equals` - 等於
|
||||
- `not_equals` - 不等於
|
||||
- `changed_to` - 變更為特定值
|
||||
- `changed_from` - 從特定值變更
|
||||
|
||||
### 4. 動作定義格式
|
||||
|
||||
**Decision:** 使用 JSON 陣列定義動作
|
||||
|
||||
```json
|
||||
[
|
||||
{
|
||||
"type": "notify",
|
||||
"target": "assignee",
|
||||
"template": "任務 {task.title} 狀態已變更為 {new_value}"
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
**Supported Actions (Phase 1):**
|
||||
- `notify` - 發送系統通知
|
||||
|
||||
**Target Types:**
|
||||
- `assignee` - 任務指派者
|
||||
- `creator` - 任務建立者
|
||||
- `project_owner` - 專案擁有者
|
||||
- `user:<user_id>` - 指定使用者
|
||||
|
||||
## Data Model
|
||||
|
||||
```sql
|
||||
-- 觸發器表
|
||||
pjctrl_triggers
|
||||
├── id: UUID (PK)
|
||||
├── project_id: UUID (FK -> projects)
|
||||
├── name: VARCHAR(200)
|
||||
├── description: TEXT
|
||||
├── trigger_type: ENUM('field_change', 'schedule')
|
||||
├── conditions: JSON
|
||||
├── actions: JSON
|
||||
├── is_active: BOOLEAN DEFAULT true
|
||||
├── created_by: UUID (FK -> users)
|
||||
├── created_at: TIMESTAMP
|
||||
└── updated_at: TIMESTAMP
|
||||
|
||||
-- 觸發器執行日誌
|
||||
pjctrl_trigger_logs
|
||||
├── id: UUID (PK)
|
||||
├── trigger_id: UUID (FK -> triggers)
|
||||
├── task_id: UUID (FK -> tasks, nullable)
|
||||
├── executed_at: TIMESTAMP
|
||||
├── status: ENUM('success', 'failed')
|
||||
├── details: JSON
|
||||
└── error_message: TEXT
|
||||
|
||||
-- 排程報告設定
|
||||
pjctrl_scheduled_reports
|
||||
├── id: UUID (PK)
|
||||
├── report_type: ENUM('weekly')
|
||||
├── recipient_id: UUID (FK -> users)
|
||||
├── is_active: BOOLEAN DEFAULT true
|
||||
├── last_sent_at: TIMESTAMP
|
||||
└── created_at: TIMESTAMP
|
||||
|
||||
-- 報告歷史
|
||||
pjctrl_report_history
|
||||
├── id: UUID (PK)
|
||||
├── report_id: UUID (FK -> scheduled_reports)
|
||||
├── generated_at: TIMESTAMP
|
||||
├── content: JSON
|
||||
├── status: ENUM('sent', 'failed')
|
||||
└── error_message: TEXT
|
||||
```
|
||||
|
||||
## API Design
|
||||
|
||||
```
|
||||
# Triggers
|
||||
POST /api/projects/{project_id}/triggers # 建立觸發器
|
||||
GET /api/projects/{project_id}/triggers # 列出專案觸發器
|
||||
GET /api/triggers/{id} # 觸發器詳情
|
||||
PUT /api/triggers/{id} # 更新觸發器
|
||||
DELETE /api/triggers/{id} # 刪除觸發器
|
||||
GET /api/triggers/{id}/logs # 觸發器執行日誌
|
||||
|
||||
# Reports
|
||||
GET /api/reports/weekly/preview # 預覽週報
|
||||
POST /api/reports/weekly/generate # 手動觸發週報
|
||||
GET /api/reports/history # 報告歷史
|
||||
```
|
||||
|
||||
## Risks / Trade-offs
|
||||
|
||||
| Risk | Mitigation |
|
||||
|------|------------|
|
||||
| 大量觸發器影響效能 | 限制每專案觸發器數量,建立索引 |
|
||||
| APScheduler 單點故障 | 記錄 last_sent_at 防止重複,考慮多實例鎖 |
|
||||
| 觸發器條件複雜度 | Phase 1 僅支援簡單條件,後續擴展 |
|
||||
|
||||
## Integration Points
|
||||
|
||||
1. **Task Update Hook**
|
||||
- 在 `tasks/router.py` 的 update_task 後調用 TriggerService.evaluate()
|
||||
|
||||
2. **Notification Integration**
|
||||
- 使用現有 NotificationService.create_notification()
|
||||
|
||||
3. **Audit Integration**
|
||||
- 觸發器執行記錄至 TriggerLog(非 AuditLog)
|
||||
|
||||
## Open Questions
|
||||
|
||||
- [ ] 是否需要支援 Email 發送?(目前僅系統內通知)
|
||||
- [ ] 週報收件者如何設定?(主管自動訂閱 or 手動設定)
|
||||
@@ -1,52 +0,0 @@
|
||||
# Change: Add Automation System
|
||||
|
||||
## Why
|
||||
專案管理需要自動化功能來減少重複性工作:
|
||||
- 當任務狀態變更時自動通知相關人員
|
||||
- 每週自動生成進度報告發送給主管
|
||||
- 減少人工追蹤與提醒的負擔
|
||||
|
||||
## What Changes
|
||||
- **新增 Trigger 模型** - 定義觸發條件與動作
|
||||
- **新增 TriggerService** - 觸發器評估與執行
|
||||
- **新增 ReportService** - 週報生成邏輯
|
||||
- **新增背景排程** - APScheduler 處理定時任務
|
||||
- **整合任務 API** - 任務變更時評估觸發器
|
||||
|
||||
## Impact
|
||||
- Affected specs: `automation`
|
||||
- Affected code:
|
||||
- `backend/app/models/` - 新增 trigger, scheduled_report 模型
|
||||
- `backend/app/api/` - 新增 triggers, reports router
|
||||
- `backend/app/services/` - 新增 trigger_service, report_service
|
||||
- `backend/app/api/tasks/router.py` - 整合觸發器評估
|
||||
- `frontend/src/` - 新增觸發器管理頁面
|
||||
|
||||
## Implementation Phases
|
||||
|
||||
### Phase 1: Event-Based Triggers
|
||||
- Trigger 模型與 CRUD API
|
||||
- 欄位變更觸發器(status, assignee, priority)
|
||||
- 通知動作執行
|
||||
- 整合任務更新流程
|
||||
|
||||
### Phase 2: Weekly Reports
|
||||
- ScheduledReport 模型
|
||||
- 週報生成邏輯(彙整任務統計)
|
||||
- APScheduler 背景排程
|
||||
- 系統內通知發送
|
||||
|
||||
### Phase 3: Advanced Features (Optional)
|
||||
- 時間條件觸發器
|
||||
- 複合條件支援
|
||||
- 更新欄位動作
|
||||
- 自動指派動作
|
||||
|
||||
## Dependencies
|
||||
- notification (已完成) - 用於發送觸發通知
|
||||
- audit-trail (已完成) - 記錄觸發器執行日誌
|
||||
|
||||
## Technical Decisions
|
||||
- **使用 APScheduler** 而非 Celery - 輕量級,無需額外 worker 進程
|
||||
- **同步觸發器評估** - 任務更新時同步執行,避免複雜的異步處理
|
||||
- **JSON 欄位儲存條件** - 靈活的條件定義格式
|
||||
@@ -1,105 +0,0 @@
|
||||
## MODIFIED Requirements
|
||||
|
||||
### Requirement: Trigger-Based Automation
|
||||
系統 SHALL 支援觸發器 (Triggers),當特定條件滿足時自動執行動作。
|
||||
|
||||
#### Scenario: 狀態變更觸發通知
|
||||
- **GIVEN** 專案設定了「當任務狀態變更為待測試時,通知指派者」的觸發器
|
||||
- **WHEN** 任務狀態變更為「待測試」
|
||||
- **THEN** 系統自動發送通知給任務指派者
|
||||
- **AND** 觸發器執行記錄至 TriggerLog
|
||||
|
||||
#### Scenario: 建立觸發器
|
||||
- **GIVEN** 專案管理者需要建立自動化規則
|
||||
- **WHEN** 管理者透過 API 設定觸發條件與動作
|
||||
- **THEN** 系統儲存觸發器規則
|
||||
- **AND** 規則立即生效
|
||||
|
||||
### Requirement: Trigger Conditions
|
||||
系統 SHALL 支援欄位變更觸發條件。
|
||||
|
||||
#### Scenario: 欄位變更條件
|
||||
- **GIVEN** 觸發器設定為「當 status_id 欄位變更為特定值」
|
||||
- **WHEN** 任務的 status_id 欄位變更為該值
|
||||
- **THEN** 觸發器被觸發
|
||||
- **AND** 支援運算子: equals, not_equals, changed_to, changed_from
|
||||
|
||||
### Requirement: Trigger Actions
|
||||
系統 SHALL 支援發送通知動作。
|
||||
|
||||
#### Scenario: 發送通知動作
|
||||
- **GIVEN** 觸發器動作設定為 notify
|
||||
- **WHEN** 觸發器被觸發
|
||||
- **THEN** 系統使用 NotificationService 發送通知
|
||||
- **AND** 通知目標支援: assignee, creator, project_owner, user:<id>
|
||||
- **AND** 通知內容可使用變數模板
|
||||
|
||||
### Requirement: Automated Weekly Report
|
||||
系統 SHALL 每週五下午 4:00 自動彙整本週任務狀態發送給主管。
|
||||
|
||||
#### Scenario: 週報自動生成
|
||||
- **GIVEN** APScheduler 排程設定為每週五 16:00
|
||||
- **WHEN** 到達排程時間
|
||||
- **THEN** ReportService 彙整使用者所屬專案的任務狀態
|
||||
- **AND** 生成週報並透過 NotificationService 發送
|
||||
|
||||
#### Scenario: 週報內容
|
||||
- **GIVEN** 週報生成中
|
||||
- **WHEN** 系統彙整資料
|
||||
- **THEN** 週報 JSON 包含:
|
||||
- completed_count: 本週已完成任務數
|
||||
- in_progress_count: 進行中任務數
|
||||
- overdue_count: 逾期任務數
|
||||
- tasks: 詳細任務清單
|
||||
|
||||
## MODIFIED Data Model
|
||||
|
||||
```
|
||||
pjctrl_triggers
|
||||
├── id: UUID (PK)
|
||||
├── project_id: UUID (FK -> projects)
|
||||
├── name: VARCHAR(200)
|
||||
├── description: TEXT
|
||||
├── trigger_type: ENUM('field_change', 'schedule')
|
||||
├── conditions: JSON
|
||||
│ └── { "field": "status_id", "operator": "changed_to", "value": "uuid" }
|
||||
├── actions: JSON
|
||||
│ └── [{ "type": "notify", "target": "assignee", "template": "..." }]
|
||||
├── is_active: BOOLEAN DEFAULT true
|
||||
├── created_by: UUID (FK -> users)
|
||||
├── created_at: TIMESTAMP
|
||||
└── updated_at: TIMESTAMP
|
||||
|
||||
pjctrl_trigger_logs
|
||||
├── id: UUID (PK)
|
||||
├── trigger_id: UUID (FK -> triggers)
|
||||
├── task_id: UUID (FK -> tasks, nullable)
|
||||
├── executed_at: TIMESTAMP
|
||||
├── status: ENUM('success', 'failed')
|
||||
├── details: JSON
|
||||
└── error_message: TEXT
|
||||
|
||||
pjctrl_scheduled_reports
|
||||
├── id: UUID (PK)
|
||||
├── report_type: ENUM('weekly')
|
||||
├── recipient_id: UUID (FK -> users)
|
||||
├── is_active: BOOLEAN DEFAULT true
|
||||
├── last_sent_at: TIMESTAMP
|
||||
└── created_at: TIMESTAMP
|
||||
|
||||
pjctrl_report_history
|
||||
├── id: UUID (PK)
|
||||
├── report_id: UUID (FK -> scheduled_reports)
|
||||
├── generated_at: TIMESTAMP
|
||||
├── content: JSON
|
||||
├── status: ENUM('sent', 'failed')
|
||||
└── error_message: TEXT
|
||||
```
|
||||
|
||||
## MODIFIED Technical Notes
|
||||
|
||||
- 使用 APScheduler (AsyncIOScheduler) 處理排程任務,嵌入 FastAPI 應用內運行
|
||||
- 觸發器評估採用同步處理,任務更新時直接執行,避免複雜的異步處理
|
||||
- 所有觸發器執行都記錄至 TriggerLog 供追蹤
|
||||
- Phase 1 僅支援 notify 動作
|
||||
- 條件運算子: equals, not_equals, changed_to, changed_from
|
||||
@@ -1,90 +0,0 @@
|
||||
## Phase 1: Event-Based Triggers
|
||||
|
||||
### 1.1 Database Schema
|
||||
- [x] 1.1.1 建立 Trigger model (`pjctrl_triggers`)
|
||||
- [x] 1.1.2 建立 TriggerLog model (`pjctrl_trigger_logs`)
|
||||
- [x] 1.1.3 建立 Alembic migration
|
||||
- [x] 1.1.4 定義 TriggerType enum (field_change, schedule)
|
||||
|
||||
### 1.2 Trigger Service
|
||||
- [x] 1.2.1 建立 TriggerService 類別
|
||||
- [x] 1.2.2 實作 evaluate_triggers(task, old_values, new_values) 方法
|
||||
- [x] 1.2.3 實作 check_condition(condition, old_value, new_value) 方法
|
||||
- [x] 1.2.4 實作 execute_action(action, task, user) 方法
|
||||
- [x] 1.2.5 實作 log_execution(trigger, task, status, error) 方法
|
||||
|
||||
### 1.3 Trigger API
|
||||
- [x] 1.3.1 建立 Trigger schemas (request/response)
|
||||
- [x] 1.3.2 實作 POST `/api/projects/{project_id}/triggers` - 建立
|
||||
- [x] 1.3.3 實作 GET `/api/projects/{project_id}/triggers` - 列表
|
||||
- [x] 1.3.4 實作 GET `/api/triggers/{id}` - 詳情
|
||||
- [x] 1.3.5 實作 PUT `/api/triggers/{id}` - 更新
|
||||
- [x] 1.3.6 實作 DELETE `/api/triggers/{id}` - 刪除
|
||||
- [x] 1.3.7 實作 GET `/api/triggers/{id}/logs` - 執行日誌
|
||||
|
||||
### 1.4 Task Integration
|
||||
- [x] 1.4.1 修改 update_task endpoint 整合觸發器評估
|
||||
- [x] 1.4.2 修改 update_task_status endpoint 整合觸發器評估
|
||||
- [x] 1.4.3 修改 assign_task endpoint 整合觸發器評估
|
||||
|
||||
### 1.5 Frontend - Triggers
|
||||
- [x] 1.5.1 建立 triggers.ts service
|
||||
- [x] 1.5.2 建立 TriggerList 元件
|
||||
- [x] 1.5.3 建立 TriggerForm 元件(條件/動作設定)
|
||||
- [x] 1.5.4 整合至 Project 設定頁面
|
||||
|
||||
### 1.6 Testing - Phase 1
|
||||
- [x] 1.6.1 TriggerService 單元測試
|
||||
- [x] 1.6.2 Trigger API 端點測試
|
||||
- [x] 1.6.3 觸發器執行整合測試
|
||||
|
||||
## Phase 2: Weekly Reports
|
||||
|
||||
### 2.1 Database Schema
|
||||
- [x] 2.1.1 建立 ScheduledReport model (`pjctrl_scheduled_reports`)
|
||||
- [x] 2.1.2 建立 ReportHistory model (`pjctrl_report_history`)
|
||||
- [x] 2.1.3 建立 Alembic migration
|
||||
|
||||
### 2.2 Report Service
|
||||
- [x] 2.2.1 建立 ReportService 類別
|
||||
- [x] 2.2.2 實作 generate_weekly_report(user_id) 方法
|
||||
- [x] 2.2.3 實作 get_weekly_stats(user_id, week_start) 方法
|
||||
- [x] 2.2.4 實作 send_report_notification(user_id, report) 方法
|
||||
- [x] 2.2.5 實作 save_report_history(report) 方法
|
||||
|
||||
### 2.3 Scheduler Setup
|
||||
- [x] 2.3.1 安裝 APScheduler
|
||||
- [x] 2.3.2 建立 scheduler.py 設定檔
|
||||
- [x] 2.3.3 設定週五 16:00 排程任務
|
||||
- [x] 2.3.4 整合至 main.py 啟動流程
|
||||
|
||||
### 2.4 Report API
|
||||
- [x] 2.4.1 建立 Report schemas
|
||||
- [x] 2.4.2 實作 GET `/api/reports/weekly/preview` - 預覽
|
||||
- [x] 2.4.3 實作 POST `/api/reports/weekly/generate` - 手動觸發
|
||||
- [x] 2.4.4 實作 GET `/api/reports/history` - 歷史紀錄
|
||||
|
||||
### 2.5 Frontend - Reports
|
||||
- [x] 2.5.1 建立 reports.ts service
|
||||
- [x] 2.5.2 建立 WeeklyReportPreview 元件
|
||||
- [x] 2.5.3 建立 ReportHistory 元件
|
||||
- [x] 2.5.4 新增管理員報告頁面
|
||||
|
||||
### 2.6 Testing - Phase 2
|
||||
- [x] 2.6.1 ReportService 單元測試
|
||||
- [x] 2.6.2 週報生成測試
|
||||
- [x] 2.6.3 排程執行測試
|
||||
|
||||
## Phase 3: Advanced Features (Optional)
|
||||
|
||||
### 3.1 Schedule Triggers
|
||||
- [ ] 3.1.1 支援 cron 表達式觸發器
|
||||
- [ ] 3.1.2 截止日期提醒觸發器
|
||||
|
||||
### 3.2 Additional Actions
|
||||
- [ ] 3.2.1 更新欄位動作
|
||||
- [ ] 3.2.2 自動指派動作
|
||||
|
||||
### 3.3 Complex Conditions
|
||||
- [ ] 3.3.1 AND/OR 複合條件
|
||||
- [ ] 3.3.2 多欄位條件
|
||||
@@ -1,125 +0,0 @@
|
||||
## Context
|
||||
|
||||
協作功能需要整合多個子系統:留言、@提及、阻礙管理、即時通知。這些功能與現有的 Task 模組緊密耦合,且需要引入 WebSocket 即時通訊機制。
|
||||
|
||||
## Goals / Non-Goals
|
||||
|
||||
**Goals:**
|
||||
- 提供任務內留言討論功能
|
||||
- 實作 @提及與即時通知
|
||||
- 建立阻礙追蹤與主管通知機制
|
||||
- 使用 Redis Pub/Sub 處理即時推播
|
||||
|
||||
**Non-Goals:**
|
||||
- 不實作 Email 通知(未來功能)
|
||||
- 不實作離線訊息佇列
|
||||
- 不實作留言的 Markdown 編輯器(僅純文字)
|
||||
|
||||
## Decisions
|
||||
|
||||
### 1. 留言儲存結構
|
||||
- **決策**: 使用巢狀結構(parent_comment_id)支援回覆
|
||||
- **理由**: 簡單且符合大多數專案管理工具的 UX 模式
|
||||
|
||||
### 2. @提及解析
|
||||
- **決策**: 後端解析留言內容,提取 `@username` 模式並建立 Mention 記錄
|
||||
- **理由**: 前端負責顯示建議,後端負責實際通知邏輯
|
||||
|
||||
### 3. 即時通知架構
|
||||
- **決策**: Redis Pub/Sub + WebSocket (FastAPI WebSocket)
|
||||
- **替代方案**:
|
||||
- Server-Sent Events (SSE) - 單向,不支援雙向互動
|
||||
- Polling - 延遲高,資源浪費
|
||||
- **理由**: WebSocket 提供低延遲雙向通訊,Redis 已在架構中使用
|
||||
|
||||
### 4. 阻礙管理
|
||||
- **決策**: 新增獨立 `pjctrl_blockers` 表記錄阻礙詳情
|
||||
- **理由**: Task 的 `blocker_flag` 只是布林值,需要追蹤原因、處理人、解除說明
|
||||
|
||||
## Risks / Trade-offs
|
||||
|
||||
| 風險 | 緩解措施 |
|
||||
|------|----------|
|
||||
| WebSocket 連線管理複雜度 | 使用連線池,實作心跳機制 |
|
||||
| 通知量過大導致效能問題 | 實作批次處理、設定通知合併策略 |
|
||||
| @提及被濫用 | 限制單則留言最多提及 10 人 |
|
||||
|
||||
## Data Model
|
||||
|
||||
```
|
||||
pjctrl_comments
|
||||
├── id: UUID (PK)
|
||||
├── task_id: UUID (FK -> tasks)
|
||||
├── parent_comment_id: UUID (FK -> comments, nullable)
|
||||
├── author_id: UUID (FK -> users)
|
||||
├── content: TEXT
|
||||
├── is_edited: BOOLEAN DEFAULT false
|
||||
├── created_at: TIMESTAMP
|
||||
└── updated_at: TIMESTAMP
|
||||
|
||||
pjctrl_mentions
|
||||
├── id: UUID (PK)
|
||||
├── comment_id: UUID (FK -> comments)
|
||||
├── mentioned_user_id: UUID (FK -> users)
|
||||
└── created_at: TIMESTAMP
|
||||
|
||||
pjctrl_notifications
|
||||
├── id: UUID (PK)
|
||||
├── user_id: UUID (FK -> users)
|
||||
├── type: ENUM('mention', 'assignment', 'blocker', 'status_change', 'comment')
|
||||
├── reference_type: VARCHAR(50)
|
||||
├── reference_id: UUID
|
||||
├── title: VARCHAR(200)
|
||||
├── message: TEXT
|
||||
├── is_read: BOOLEAN DEFAULT false
|
||||
├── created_at: TIMESTAMP
|
||||
└── read_at: TIMESTAMP
|
||||
|
||||
pjctrl_blockers
|
||||
├── id: UUID (PK)
|
||||
├── task_id: UUID (FK -> tasks)
|
||||
├── reported_by: UUID (FK -> users)
|
||||
├── reason: TEXT
|
||||
├── resolved_by: UUID (FK -> users, nullable)
|
||||
├── resolution_note: TEXT
|
||||
├── created_at: TIMESTAMP
|
||||
└── resolved_at: TIMESTAMP
|
||||
```
|
||||
|
||||
## API Design
|
||||
|
||||
```
|
||||
# Comments
|
||||
POST /api/tasks/{task_id}/comments # 新增留言
|
||||
GET /api/tasks/{task_id}/comments # 取得任務留言列表
|
||||
PUT /api/comments/{comment_id} # 編輯留言
|
||||
DELETE /api/comments/{comment_id} # 刪除留言
|
||||
|
||||
# Notifications
|
||||
GET /api/notifications # 取得通知列表
|
||||
PUT /api/notifications/{id}/read # 標記已讀
|
||||
PUT /api/notifications/read-all # 全部已讀
|
||||
GET /api/notifications/unread-count # 取得未讀數量
|
||||
|
||||
# Blockers
|
||||
POST /api/tasks/{task_id}/blockers # 標記阻礙
|
||||
PUT /api/blockers/{blocker_id}/resolve # 解除阻礙
|
||||
GET /api/tasks/{task_id}/blockers # 取得阻礙歷史
|
||||
|
||||
# WebSocket
|
||||
WS /ws/notifications # 即時通知連線
|
||||
|
||||
# User Search (for @mention autocomplete)
|
||||
GET /api/users/search?q={query} # 搜尋使用者
|
||||
```
|
||||
|
||||
## Migration Plan
|
||||
|
||||
1. 新增資料表(無 breaking changes)
|
||||
2. Task model 新增 comments relationship
|
||||
3. User model 新增 notifications relationship
|
||||
4. 前端漸進式整合
|
||||
|
||||
## Open Questions
|
||||
|
||||
無(已與現有架構對齊)
|
||||
@@ -1,18 +0,0 @@
|
||||
# Change: Add Collaboration Features
|
||||
|
||||
## Why
|
||||
專案管理系統需要協作功能,讓團隊成員能在任務內進行討論、標記相關人員,並處理任務阻礙。目前系統缺乏這些核心協作機制,導致使用者仍需依賴 Email 或其他工具進行溝通。
|
||||
|
||||
## What Changes
|
||||
- 新增任務留言系統(支援巢狀回覆)
|
||||
- 實作 @提及功能(自動完成 + 即時通知)
|
||||
- 建立阻礙管理機制(標記原因、主管通知、解除追蹤)
|
||||
- 實作即時通知系統(Redis Pub/Sub + WebSocket)
|
||||
|
||||
## Impact
|
||||
- Affected specs: `collaboration`
|
||||
- Affected code:
|
||||
- 新增 models: Comment, Mention, Notification, Blocker
|
||||
- 新增 API routes: `/comments`, `/notifications`
|
||||
- 新增 services: NotificationService, WebSocketManager
|
||||
- 前端: 任務詳情頁新增留言區、通知中心
|
||||
@@ -1,97 +0,0 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Task Comments
|
||||
系統 SHALL 支援任務內部的討論留言,減少 Email 往返。
|
||||
|
||||
#### Scenario: 新增留言
|
||||
- **GIVEN** 使用者擁有任務的存取權限
|
||||
- **WHEN** 使用者在任務中新增留言
|
||||
- **THEN** 系統儲存留言並顯示在討論區
|
||||
- **AND** 記錄留言者與時間戳記
|
||||
|
||||
#### Scenario: 回覆留言
|
||||
- **GIVEN** 任務已有留言
|
||||
- **WHEN** 使用者回覆特定留言
|
||||
- **THEN** 系統建立巢狀回覆結構
|
||||
- **AND** 通知原留言者
|
||||
|
||||
#### Scenario: 編輯留言
|
||||
- **GIVEN** 使用者是留言的作者
|
||||
- **WHEN** 使用者編輯自己的留言
|
||||
- **THEN** 系統更新留言內容
|
||||
- **AND** 標示「已編輯」及編輯時間
|
||||
|
||||
#### Scenario: 刪除留言
|
||||
- **GIVEN** 使用者是留言的作者
|
||||
- **WHEN** 使用者刪除自己的留言
|
||||
- **THEN** 系統移除留言
|
||||
- **AND** 若有回覆則顯示「此留言已刪除」
|
||||
|
||||
### Requirement: User Mentions
|
||||
系統 SHALL 支援 @相關人員 功能,提及時發送即時通知。
|
||||
|
||||
#### Scenario: @提及通知
|
||||
- **GIVEN** 使用者在留言中使用 @username 提及某人
|
||||
- **WHEN** 留言送出
|
||||
- **THEN** 被提及者收到即時通知
|
||||
- **AND** 通知包含任務連結與留言摘要
|
||||
|
||||
#### Scenario: @提及自動完成
|
||||
- **GIVEN** 使用者輸入 @ 符號
|
||||
- **WHEN** 使用者繼續輸入
|
||||
- **THEN** 系統顯示符合的使用者名單供選擇
|
||||
- **AND** 可用鍵盤或滑鼠選擇
|
||||
|
||||
#### Scenario: @提及數量限制
|
||||
- **GIVEN** 使用者在單則留言中提及超過 10 人
|
||||
- **WHEN** 留言送出
|
||||
- **THEN** 系統拒絕並顯示錯誤訊息
|
||||
|
||||
### Requirement: Blocker Management
|
||||
系統 SHALL 提供阻礙 (Blocker) 機制,強制要求主管介入排解。
|
||||
|
||||
#### Scenario: 標記阻礙
|
||||
- **GIVEN** 工程師的任務遇到阻礙無法進行
|
||||
- **WHEN** 工程師將任務標記為 "Blocked"
|
||||
- **THEN** 系統設定 Task.blocker_flag = true
|
||||
- **AND** 建立 Blocker 記錄
|
||||
- **AND** 發送即時通知給該任務所屬專案的 Owner
|
||||
|
||||
#### Scenario: 阻礙原因說明
|
||||
- **GIVEN** 任務被標記為 Blocked
|
||||
- **WHEN** 使用者標記阻礙
|
||||
- **THEN** 系統要求填寫阻礙原因
|
||||
- **AND** 原因顯示在任務詳情與通知中
|
||||
|
||||
#### Scenario: 解除阻礙
|
||||
- **GIVEN** 主管或被指派者處理完阻礙
|
||||
- **WHEN** 使用者解除 Blocked 狀態
|
||||
- **THEN** 系統設定 Task.blocker_flag = false
|
||||
- **AND** 記錄解除時間與處理說明
|
||||
|
||||
### Requirement: Real-time Notifications
|
||||
系統 SHALL 透過 Redis Pub/Sub 與 WebSocket 推播即時通知。
|
||||
|
||||
#### Scenario: 即時通知推播
|
||||
- **GIVEN** 發生需要通知的事件(被指派任務、被 @提及、阻礙標記)
|
||||
- **WHEN** 事件發生
|
||||
- **THEN** 系統透過 WebSocket 即時推播通知給相關使用者
|
||||
- **AND** 未讀通知顯示數量標示
|
||||
|
||||
#### Scenario: 通知已讀標記
|
||||
- **GIVEN** 使用者有未讀通知
|
||||
- **WHEN** 使用者查看通知
|
||||
- **THEN** 系統標記為已讀
|
||||
- **AND** 更新未讀數量
|
||||
|
||||
#### Scenario: 通知清單查詢
|
||||
- **GIVEN** 使用者需要查看歷史通知
|
||||
- **WHEN** 使用者開啟通知中心
|
||||
- **THEN** 系統顯示通知列表(依時間降序)
|
||||
- **AND** 支援分頁與已讀/未讀篩選
|
||||
|
||||
#### Scenario: WebSocket 重連
|
||||
- **GIVEN** 使用者的 WebSocket 連線中斷
|
||||
- **WHEN** 連線恢復
|
||||
- **THEN** 系統自動重新建立連線
|
||||
- **AND** 補送中斷期間的未讀通知
|
||||
@@ -1,76 +0,0 @@
|
||||
## 1. Database Schema
|
||||
|
||||
- [x] 1.1 建立 Comment model (`pjctrl_comments`)
|
||||
- [x] 1.2 建立 Mention model (`pjctrl_mentions`)
|
||||
- [x] 1.3 建立 Notification model (`pjctrl_notifications`)
|
||||
- [x] 1.4 建立 Blocker model (`pjctrl_blockers`)
|
||||
- [x] 1.5 建立 Alembic migration
|
||||
- [x] 1.6 更新 Task model 加入 comments relationship
|
||||
- [x] 1.7 更新 User model 加入 notifications relationship
|
||||
|
||||
## 2. Backend API - Comments
|
||||
|
||||
- [x] 2.1 建立 Comment schemas (request/response)
|
||||
- [x] 2.2 實作 POST `/api/tasks/{task_id}/comments` - 新增留言
|
||||
- [x] 2.3 實作 GET `/api/tasks/{task_id}/comments` - 取得留言列表
|
||||
- [x] 2.4 實作 PUT `/api/comments/{comment_id}` - 編輯留言
|
||||
- [x] 2.5 實作 DELETE `/api/comments/{comment_id}` - 刪除留言
|
||||
- [x] 2.6 實作 @mention 解析邏輯(從留言內容提取 @username)
|
||||
|
||||
## 3. Backend API - Notifications
|
||||
|
||||
- [x] 3.1 建立 Notification schemas
|
||||
- [x] 3.2 實作 NotificationService(建立、發送通知)
|
||||
- [x] 3.3 實作 GET `/api/notifications` - 取得通知列表
|
||||
- [x] 3.4 實作 PUT `/api/notifications/{id}/read` - 標記已讀
|
||||
- [x] 3.5 實作 PUT `/api/notifications/read-all` - 全部已讀
|
||||
- [x] 3.6 實作 GET `/api/notifications/unread-count` - 未讀數量
|
||||
|
||||
## 4. Backend API - Blockers
|
||||
|
||||
- [x] 4.1 建立 Blocker schemas
|
||||
- [x] 4.2 實作 POST `/api/tasks/{task_id}/blockers` - 標記阻礙
|
||||
- [x] 4.3 實作 PUT `/api/blockers/{blocker_id}/resolve` - 解除阻礙
|
||||
- [x] 4.4 實作 GET `/api/tasks/{task_id}/blockers` - 取得阻礙歷史
|
||||
- [x] 4.5 整合阻礙通知(通知專案 Owner)
|
||||
|
||||
## 5. WebSocket Integration
|
||||
|
||||
- [x] 5.1 建立 WebSocket connection manager
|
||||
- [x] 5.2 實作 `/ws/notifications` endpoint
|
||||
- [x] 5.3 整合 Redis Pub/Sub 發布通知
|
||||
- [x] 5.4 實作 WebSocket 認證(JWT token)
|
||||
- [x] 5.5 實作心跳機制(keepalive)
|
||||
|
||||
## 6. Backend API - User Search
|
||||
|
||||
- [x] 6.1 實作 GET `/api/users/search?q={query}` - 使用者搜尋(支援 @mention 自動完成)
|
||||
|
||||
## 7. Frontend - Comments
|
||||
|
||||
- [x] 7.1 建立 CommentList 元件
|
||||
- [x] 7.2 建立 CommentItem 元件(支援巢狀回覆)
|
||||
- [x] 7.3 建立 CommentForm 元件(含 @mention 自動完成)
|
||||
- [x] 7.4 整合至 Task 詳情頁
|
||||
|
||||
## 8. Frontend - Notifications
|
||||
|
||||
- [x] 8.1 建立 NotificationContext(狀態管理)
|
||||
- [x] 8.2 建立 NotificationBell 元件(未讀數量 badge)
|
||||
- [x] 8.3 建立 NotificationList 元件
|
||||
- [x] 8.4 建立 NotificationItem 元件
|
||||
- [x] 8.5 整合 WebSocket 連線
|
||||
|
||||
## 9. Frontend - Blockers
|
||||
|
||||
- [x] 9.1 建立 BlockerDialog 元件(標記阻礙表單)
|
||||
- [x] 9.2 建立 BlockerHistory 元件
|
||||
- [x] 9.3 整合至 Task 詳情頁
|
||||
|
||||
## 10. Testing
|
||||
|
||||
- [x] 10.1 Comment API 單元測試
|
||||
- [x] 10.2 Notification API 單元測試
|
||||
- [x] 10.3 Blocker API 單元測試
|
||||
- [x] 10.4 WebSocket 連線測試
|
||||
- [x] 10.5 @mention 解析測試
|
||||
@@ -1,159 +0,0 @@
|
||||
## Context
|
||||
|
||||
文件管理是專案系統的核心功能,需要考慮:
|
||||
- 檔案存儲策略(本地 vs NAS)
|
||||
- 安全需求(加密、浮水印)
|
||||
- 版本控制邏輯
|
||||
- 大檔案處理
|
||||
|
||||
## Goals / Non-Goals
|
||||
|
||||
**Goals:**
|
||||
- 提供任務層級的檔案附件功能
|
||||
- 支援基本 CRUD 操作
|
||||
- 整合現有 Audit Trail
|
||||
- 為未來 NAS 整合預留擴展性
|
||||
|
||||
**Non-Goals:**
|
||||
- 即時協作編輯(不在此範圍)
|
||||
- 全文搜尋(未來功能)
|
||||
- 檔案預覽(未來功能)
|
||||
|
||||
## Decisions
|
||||
|
||||
### 1. 檔案存儲策略
|
||||
|
||||
**Decision:** 使用本地檔案系統 + 環境變數配置路徑
|
||||
|
||||
**Rationale:**
|
||||
- 開發階段使用本地存儲簡化設置
|
||||
- 生產環境透過環境變數指向 NAS 掛載點
|
||||
- 路徑結構:`{UPLOAD_DIR}/{project_id}/{task_id}/{attachment_id}/{version}/`
|
||||
|
||||
**Alternatives considered:**
|
||||
- 直接 NAS 整合 - 開發環境設置複雜
|
||||
- S3 相容存儲 - 增加外部依賴
|
||||
|
||||
### 2. 版本控制模型
|
||||
|
||||
**Decision:** 主表 + 版本歷史表分離
|
||||
|
||||
```
|
||||
pjctrl_attachments (主表,存儲最新版本資訊)
|
||||
├── id, task_id, filename, current_version, ...
|
||||
|
||||
pjctrl_attachment_versions (歷史表)
|
||||
├── id, attachment_id, version, file_path, ...
|
||||
```
|
||||
|
||||
**Rationale:**
|
||||
- 主表快速查詢當前附件
|
||||
- 歷史表保留所有版本
|
||||
- 上傳同名檔案 → 建立新版本 → 更新主表 current_version
|
||||
|
||||
### 3. 加密策略
|
||||
|
||||
**Decision:** 使用 Fernet (基於 AES-128-CBC) 對稱加密
|
||||
|
||||
**Rationale:**
|
||||
- Python cryptography 庫內建支援
|
||||
- 自動處理 IV、padding、HMAC 驗證
|
||||
- 比原生 AES-256 更安全(防止實作錯誤)
|
||||
- 效能足夠(非大規模加密場景)
|
||||
|
||||
**實作方式:**
|
||||
- 加密金鑰存儲於環境變數 `ENCRYPTION_KEY`
|
||||
- 僅對機密專案的附件加密
|
||||
- 加密狀態存於 `is_encrypted` 欄位
|
||||
|
||||
### 4. 浮水印策略
|
||||
|
||||
**Decision:** 下載時動態生成浮水印
|
||||
|
||||
**Rationale:**
|
||||
- 不修改原始檔案
|
||||
- 每次下載包含當下使用者資訊
|
||||
- 使用 Pillow (圖片) 和 PyMuPDF (PDF) 處理
|
||||
|
||||
**浮水印內容:**
|
||||
- 使用者姓名 + 工號
|
||||
- 下載時間
|
||||
- 機密等級(如適用)
|
||||
|
||||
### 5. 檔案大小限制
|
||||
|
||||
**Decision:** 預設 50MB,可透過環境變數調整
|
||||
|
||||
```python
|
||||
MAX_FILE_SIZE = int(os.getenv("MAX_FILE_SIZE_MB", 50)) * 1024 * 1024
|
||||
```
|
||||
|
||||
**Rationale:**
|
||||
- 避免記憶體溢出
|
||||
- 大檔案使用串流處理
|
||||
- 生產環境可依需求調整
|
||||
|
||||
## Data Model
|
||||
|
||||
```sql
|
||||
-- 附件主表
|
||||
pjctrl_attachments
|
||||
├── id: UUID (PK)
|
||||
├── task_id: UUID (FK -> tasks)
|
||||
├── filename: VARCHAR(255) -- 顯示名稱
|
||||
├── original_filename: VARCHAR(255) -- 原始上傳名稱
|
||||
├── mime_type: VARCHAR(100)
|
||||
├── file_size: BIGINT
|
||||
├── current_version: INT DEFAULT 1
|
||||
├── is_encrypted: BOOLEAN DEFAULT false
|
||||
├── uploaded_by: UUID (FK -> users)
|
||||
├── is_deleted: BOOLEAN DEFAULT false
|
||||
├── created_at: TIMESTAMP
|
||||
└── updated_at: TIMESTAMP
|
||||
|
||||
-- 版本歷史表
|
||||
pjctrl_attachment_versions
|
||||
├── id: UUID (PK)
|
||||
├── attachment_id: UUID (FK -> attachments)
|
||||
├── version: INT
|
||||
├── file_path: VARCHAR(1000) -- 實際存儲路徑
|
||||
├── file_size: BIGINT
|
||||
├── checksum: VARCHAR(64) -- SHA-256
|
||||
├── uploaded_by: UUID (FK -> users)
|
||||
├── created_at: TIMESTAMP
|
||||
└── INDEX (attachment_id, version)
|
||||
```
|
||||
|
||||
## API Design
|
||||
|
||||
```
|
||||
POST /api/tasks/{task_id}/attachments # 上傳附件
|
||||
GET /api/tasks/{task_id}/attachments # 列出附件
|
||||
GET /api/attachments/{id} # 取得附件資訊
|
||||
GET /api/attachments/{id}/download # 下載附件
|
||||
GET /api/attachments/{id}/download?version=2 # 下載特定版本
|
||||
DELETE /api/attachments/{id} # 刪除附件(軟刪除)
|
||||
GET /api/attachments/{id}/versions # 版本歷史
|
||||
POST /api/attachments/{id}/restore/{version} # 回復特定版本
|
||||
```
|
||||
|
||||
## Risks / Trade-offs
|
||||
|
||||
| Risk | Mitigation |
|
||||
|------|------------|
|
||||
| 大檔案記憶體溢出 | 使用串流上傳/下載 |
|
||||
| 加密金鑰洩漏 | 僅存於環境變數,定期輪換 |
|
||||
| 浮水印處理耗時 | 限制支援的檔案類型,非同步處理大檔案 |
|
||||
| NAS 不可用 | 本地存儲 fallback,監控告警 |
|
||||
|
||||
## Migration Plan
|
||||
|
||||
1. Phase 1: 建立模型、基本 CRUD、本地存儲
|
||||
2. Phase 2: 版本控制
|
||||
3. Phase 3: 加密與浮水印(可選)
|
||||
|
||||
## Open Questions
|
||||
|
||||
- [ ] NAS 掛載點路徑確認
|
||||
- [ ] 生產環境加密金鑰管理方式(KMS? Vault?)
|
||||
- [ ] 是否需要支援拖放上傳多檔案?
|
||||
@@ -1,44 +0,0 @@
|
||||
# Change: Add Document Management
|
||||
|
||||
## Why
|
||||
專案管理系統需要文件附件功能,讓使用者能在任務層級上傳、下載、管理檔案。半導體產業對機密文件有特殊安全需求(加密存儲、浮水印追溯)。
|
||||
|
||||
## What Changes
|
||||
- **新增 Attachment 模型** - 支援任務層級的檔案附件
|
||||
- **新增 AttachmentVersion 模型** - 檔案版本控制
|
||||
- **新增 File Storage Service** - 本地檔案存儲(可擴展至 NAS)
|
||||
- **新增 Attachment API** - 上傳、下載、刪除、版本管理
|
||||
- **新增加密功能** - AES-256 加密存儲(機密專案)
|
||||
- **新增浮水印功能** - 下載時動態加入使用者資訊
|
||||
- **整合 Audit Trail** - 記錄所有文件操作
|
||||
|
||||
## Impact
|
||||
- Affected specs: `document-management`, `audit-trail` (已實作)
|
||||
- Affected code:
|
||||
- `backend/app/models/` - 新增 attachment 相關模型
|
||||
- `backend/app/api/` - 新增 attachments router
|
||||
- `backend/app/services/` - 新增 file_service, encryption_service
|
||||
- `frontend/src/components/` - 新增附件元件
|
||||
- `backend/migrations/` - 新增資料表
|
||||
|
||||
## Implementation Phases
|
||||
|
||||
### Phase 1: Basic Attachments (MVP)
|
||||
- 檔案上傳/下載/刪除
|
||||
- 本地檔案存儲
|
||||
- 基本 API 與前端整合
|
||||
- Audit 日誌整合
|
||||
|
||||
### Phase 2: Version Control
|
||||
- 同名檔案版本控制
|
||||
- 版本歷史查看
|
||||
- 版本回復
|
||||
|
||||
### Phase 3: Security Features (Optional)
|
||||
- AES-256 加密存儲
|
||||
- 動態浮水印(圖片/PDF)
|
||||
- 加密金鑰管理
|
||||
|
||||
## Dependencies
|
||||
- audit-trail (已完成) - 用於文件操作日誌
|
||||
- collaboration (已完成) - 可在評論中引用附件
|
||||
@@ -1,44 +0,0 @@
|
||||
## MODIFIED Requirements
|
||||
|
||||
### Requirement: Audit Trail
|
||||
系統 SHALL 記錄所有文件操作供稽核追溯,整合現有 audit-trail 模組。
|
||||
|
||||
#### Scenario: 操作日誌記錄
|
||||
- **GIVEN** 使用者對附件執行任何操作(上傳、下載、刪除)
|
||||
- **WHEN** 操作完成
|
||||
- **THEN** 系統透過 AuditService 記錄操作至 `pjctrl_audit_logs`
|
||||
- **AND** 使用 event_type: `attachment.upload`, `attachment.download`, `attachment.delete`
|
||||
|
||||
#### Scenario: 稽核查詢
|
||||
- **GIVEN** 稽核人員需要查詢文件操作歷史
|
||||
- **WHEN** 稽核人員透過 Audit API 執行查詢
|
||||
- **THEN** 可依 resource_type=attachment 篩選
|
||||
- **AND** 顯示完整操作歷史
|
||||
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: File Size Limits
|
||||
系統 SHALL 限制上傳檔案大小以確保系統穩定性。
|
||||
|
||||
#### Scenario: 檔案大小驗證
|
||||
- **GIVEN** 使用者上傳檔案
|
||||
- **WHEN** 檔案大小超過限制(預設 50MB)
|
||||
- **THEN** 系統拒絕上傳並回傳錯誤訊息
|
||||
|
||||
#### Scenario: 大小限制配置
|
||||
- **GIVEN** 管理者需要調整檔案大小限制
|
||||
- **WHEN** 設定環境變數 MAX_FILE_SIZE_MB
|
||||
- **THEN** 系統使用新的限制值
|
||||
|
||||
### Requirement: Mime Type Validation
|
||||
系統 SHALL 驗證上傳檔案類型以確保安全性。
|
||||
|
||||
#### Scenario: 允許的檔案類型
|
||||
- **GIVEN** 使用者上傳附件
|
||||
- **WHEN** 檔案類型為常見文件格式(pdf, doc, xls, jpg, png, zip 等)
|
||||
- **THEN** 系統接受上傳
|
||||
|
||||
#### Scenario: 危險檔案類型拒絕
|
||||
- **GIVEN** 使用者上傳附件
|
||||
- **WHEN** 檔案類型為可執行檔(exe, bat, sh, dll 等)
|
||||
- **THEN** 系統拒絕上傳並回傳錯誤訊息
|
||||
@@ -1,71 +0,0 @@
|
||||
## Phase 1: Basic Attachments
|
||||
|
||||
### 1.1 Database Schema
|
||||
- [x] 1.1.1 建立 Attachment model (`pjctrl_attachments`)
|
||||
- [x] 1.1.2 建立 AttachmentVersion model (`pjctrl_attachment_versions`)
|
||||
- [x] 1.1.3 建立 Alembic migration
|
||||
- [x] 1.1.4 新增 Task model 的 attachments relationship
|
||||
|
||||
### 1.2 File Storage Service
|
||||
- [x] 1.2.1 建立 FileStorageService 類別
|
||||
- [x] 1.2.2 實作 save_file() 方法(串流處理)
|
||||
- [x] 1.2.3 實作 get_file() 方法
|
||||
- [x] 1.2.4 實作 delete_file() 方法
|
||||
- [x] 1.2.5 新增檔案存儲路徑配置 (UPLOAD_DIR)
|
||||
- [x] 1.2.6 實作 checksum 計算 (SHA-256)
|
||||
|
||||
### 1.3 Attachment API
|
||||
- [x] 1.3.1 建立 Attachment schemas (request/response)
|
||||
- [x] 1.3.2 實作 POST `/api/tasks/{task_id}/attachments` - 上傳
|
||||
- [x] 1.3.3 實作 GET `/api/tasks/{task_id}/attachments` - 列表
|
||||
- [x] 1.3.4 實作 GET `/api/attachments/{id}` - 詳情
|
||||
- [x] 1.3.5 實作 GET `/api/attachments/{id}/download` - 下載
|
||||
- [x] 1.3.6 實作 DELETE `/api/attachments/{id}` - 軟刪除
|
||||
- [x] 1.3.7 整合 Audit Trail - 記錄上傳/下載/刪除操作
|
||||
|
||||
### 1.4 Frontend - Basic
|
||||
- [x] 1.4.1 建立 attachments.ts service
|
||||
- [x] 1.4.2 建立 AttachmentList 元件
|
||||
- [x] 1.4.3 建立 AttachmentUpload 元件(支援拖放)
|
||||
- [x] 1.4.4 整合至 Task 詳情頁 (TaskAttachments 元件)
|
||||
|
||||
### 1.5 Testing - Phase 1
|
||||
- [x] 1.5.1 FileStorageService 單元測試
|
||||
- [x] 1.5.2 Attachment API 端點測試
|
||||
- [x] 1.5.3 上傳/下載整合測試
|
||||
|
||||
## Phase 2: Version Control
|
||||
|
||||
### 2.1 Version Logic
|
||||
- [x] 2.1.1 修改上傳邏輯支援版本控制
|
||||
- [x] 2.1.2 實作 GET `/api/attachments/{id}/versions` - 版本歷史
|
||||
- [x] 2.1.3 實作 POST `/api/attachments/{id}/restore/{version}` - 回復版本
|
||||
- [x] 2.1.4 實作 GET `/api/attachments/{id}/download?version=N` - 下載特定版本
|
||||
|
||||
### 2.2 Frontend - Version
|
||||
- [x] 2.2.1 建立 VersionHistory 元件 (integrated in AttachmentList)
|
||||
- [x] 2.2.2 新增版本選擇下載功能 (in attachments service)
|
||||
- [x] 2.2.3 新增版本回復功能 (in attachments service)
|
||||
|
||||
### 2.3 Testing - Phase 2
|
||||
- [x] 2.3.1 版本控制邏輯測試
|
||||
- [x] 2.3.2 版本 API 端點測試
|
||||
|
||||
## Phase 3: Security Features (Optional)
|
||||
|
||||
### 3.1 Encryption
|
||||
- [ ] 3.1.1 建立 EncryptionService 類別
|
||||
- [ ] 3.1.2 實作 encrypt_file() / decrypt_file() 方法
|
||||
- [ ] 3.1.3 新增 Project security_level 欄位(如不存在)
|
||||
- [ ] 3.1.4 修改上傳邏輯:機密專案自動加密
|
||||
- [ ] 3.1.5 修改下載邏輯:自動解密
|
||||
|
||||
### 3.2 Watermarking
|
||||
- [ ] 3.2.1 建立 WatermarkService 類別
|
||||
- [ ] 3.2.2 實作圖片浮水印(Pillow)
|
||||
- [ ] 3.2.3 實作 PDF 浮水印(PyMuPDF)
|
||||
- [ ] 3.2.4 整合至下載流程
|
||||
|
||||
### 3.3 Testing - Phase 3
|
||||
- [ ] 3.3.1 加密/解密測試
|
||||
- [ ] 3.3.2 浮水印生成測試
|
||||
@@ -1,107 +0,0 @@
|
||||
## Context
|
||||
|
||||
audit-trail spec 定義了軟刪除、權限變更記錄、append-only 日誌等需求,但現行實作未完全對齊。
|
||||
|
||||
## Goals / Non-Goals
|
||||
|
||||
**Goals:**
|
||||
- 任務刪除改為軟刪除,保留資料可追溯
|
||||
- 所有權限變更記錄至 audit log
|
||||
- 確保 audit_logs 表不可被修改或刪除
|
||||
|
||||
**Non-Goals:**
|
||||
- 不實作任務還原 UI(僅提供 API)
|
||||
- 不變更現有 checksum 計算邏輯
|
||||
|
||||
## Decisions
|
||||
|
||||
### 1. 軟刪除欄位設計
|
||||
|
||||
**Decision:** 使用 `is_deleted` + `deleted_at` + `deleted_by`
|
||||
|
||||
**Rationale:**
|
||||
- `is_deleted` 簡化查詢過濾
|
||||
- `deleted_at` 提供時間資訊
|
||||
- `deleted_by` 追蹤操作者
|
||||
|
||||
```python
|
||||
is_deleted = Column(Boolean, default=False, nullable=False)
|
||||
deleted_at = Column(DateTime, nullable=True)
|
||||
deleted_by = Column(String(36), ForeignKey("pjctrl_users.id"), nullable=True)
|
||||
```
|
||||
|
||||
### 2. 查詢過濾策略
|
||||
|
||||
**Decision:** API 層過濾,非 ORM 層
|
||||
|
||||
**Rationale:**
|
||||
- 保持彈性,部分查詢需要包含已刪除項目
|
||||
- 避免 ORM 層複雜性
|
||||
|
||||
```python
|
||||
# 預設過濾
|
||||
query = query.filter(Task.is_deleted == False)
|
||||
|
||||
# 管理員可查看已刪除
|
||||
if include_deleted and current_user.is_system_admin:
|
||||
query = query.filter() # 不過濾
|
||||
```
|
||||
|
||||
### 3. Append-Only 實作
|
||||
|
||||
**Decision:** 使用 MySQL/PostgreSQL trigger
|
||||
|
||||
**MySQL:**
|
||||
```sql
|
||||
DELIMITER //
|
||||
CREATE TRIGGER prevent_audit_update BEFORE UPDATE ON pjctrl_audit_logs
|
||||
FOR EACH ROW BEGIN
|
||||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'Audit logs are immutable';
|
||||
END//
|
||||
|
||||
CREATE TRIGGER prevent_audit_delete BEFORE DELETE ON pjctrl_audit_logs
|
||||
FOR EACH ROW BEGIN
|
||||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'Audit logs are immutable';
|
||||
END//
|
||||
DELIMITER ;
|
||||
```
|
||||
|
||||
### 4. 權限變更事件
|
||||
|
||||
**Decision:** 記錄以下變更類型
|
||||
|
||||
| 事件 | event_type | sensitivity |
|
||||
|------|------------|-------------|
|
||||
| 角色指派 | user.role_change | high |
|
||||
| 角色權限更新 | role.permission_change | critical |
|
||||
| 系統管理員變更 | user.admin_change | critical |
|
||||
|
||||
## Data Model Changes
|
||||
|
||||
```sql
|
||||
-- Task 表新增欄位
|
||||
ALTER TABLE pjctrl_tasks ADD COLUMN is_deleted BOOLEAN DEFAULT FALSE NOT NULL;
|
||||
ALTER TABLE pjctrl_tasks ADD COLUMN deleted_at DATETIME NULL;
|
||||
ALTER TABLE pjctrl_tasks ADD COLUMN deleted_by VARCHAR(36) NULL;
|
||||
ALTER TABLE pjctrl_tasks ADD INDEX idx_task_deleted (is_deleted);
|
||||
```
|
||||
|
||||
## API Changes
|
||||
|
||||
```
|
||||
# 現有 API 行為變更
|
||||
DELETE /api/tasks/{id} -> 軟刪除,回傳 200 而非 204
|
||||
GET /api/projects/{id}/tasks -> 預設排除 is_deleted=true
|
||||
|
||||
# 新增 API
|
||||
POST /api/tasks/{id}/restore -> 還原已刪除任務(需權限)
|
||||
GET /api/projects/{id}/tasks?include_deleted=true -> 管理員可查看全部
|
||||
```
|
||||
|
||||
## Risks / Trade-offs
|
||||
|
||||
| Risk | Mitigation |
|
||||
|------|------------|
|
||||
| 軟刪除增加資料量 | 定期歸檔/清理策略 |
|
||||
| DB trigger 影響效能 | trigger 邏輯簡單,影響極小 |
|
||||
| 權限變更頻繁觸發 | 僅記錄實際變更 |
|
||||
@@ -1,45 +0,0 @@
|
||||
# Change: Fix Audit Trail Alignment
|
||||
|
||||
## Why
|
||||
現行實作與 audit-trail spec 有以下差距:
|
||||
1. 任務刪除為硬刪除,spec 要求軟刪除 (`is_deleted` 欄位)
|
||||
2. 權限變更未記錄 `user.permission_change` 事件
|
||||
3. 資料庫層未強制 append-only(可被 UPDATE/DELETE)
|
||||
|
||||
## What Changes
|
||||
- **Task Model** - 新增 `is_deleted`、`deleted_at`、`deleted_by` 欄位
|
||||
- **Task API** - 刪除改為軟刪除,查詢預設過濾已刪除
|
||||
- **User/Role API** - 權限/角色變更時記錄 `user.permission_change` 事件
|
||||
- **Migration** - 新增 Task 軟刪除欄位、設定 audit_logs 表 triggers 防止 UPDATE/DELETE
|
||||
|
||||
## Impact
|
||||
- Affected specs: `audit-trail`
|
||||
- Affected code:
|
||||
- `backend/app/models/task.py` - 新增軟刪除欄位
|
||||
- `backend/app/api/tasks/router.py` - 修改刪除邏輯與查詢過濾
|
||||
- `backend/app/api/users/router.py` - 新增權限變更審計
|
||||
- `backend/migrations/versions/` - 新增遷移
|
||||
|
||||
## Implementation Phases
|
||||
|
||||
### Phase 1: Task Soft Delete
|
||||
- 新增 Task 軟刪除欄位
|
||||
- 修改 delete_task 為軟刪除
|
||||
- 修改查詢過濾已刪除任務
|
||||
- 新增 restore_task API (可選)
|
||||
|
||||
### Phase 2: Permission Change Audit
|
||||
- 角色指派變更記錄
|
||||
- 權限更新記錄
|
||||
- is_system_admin 變更記錄
|
||||
|
||||
### Phase 3: Append-Only Enforcement
|
||||
- DB trigger 防止 UPDATE/DELETE
|
||||
- 驗證 checksum 機制
|
||||
|
||||
## Dependencies
|
||||
- audit-trail (已完成)
|
||||
|
||||
## Technical Decisions
|
||||
- 軟刪除使用 `is_deleted` boolean 而非時間戳,簡化查詢
|
||||
- DB trigger 使用 BEFORE UPDATE/DELETE RAISE EXCEPTION
|
||||
@@ -1,98 +0,0 @@
|
||||
## MODIFIED Requirements
|
||||
|
||||
### Requirement: Delete Operations Tracking
|
||||
系統 SHALL 追蹤所有刪除操作,支援軟刪除與追溯。
|
||||
|
||||
#### Scenario: 任務刪除記錄
|
||||
- **GIVEN** 使用者刪除任務
|
||||
- **WHEN** 刪除操作執行
|
||||
- **THEN** 系統執行軟刪除(設定 is_deleted = true, deleted_at, deleted_by)
|
||||
- **AND** 記錄刪除操作至 audit_logs
|
||||
- **AND** 子任務同步軟刪除
|
||||
|
||||
#### Scenario: 附件刪除記錄
|
||||
- **GIVEN** 使用者刪除附件
|
||||
- **WHEN** 刪除操作執行
|
||||
- **THEN** 系統保留檔案於存檔區
|
||||
- **AND** 記錄刪除操作詳情
|
||||
|
||||
#### Scenario: 任務還原
|
||||
- **GIVEN** 管理員需要還原已刪除任務
|
||||
- **WHEN** 執行還原操作
|
||||
- **THEN** 系統設定 is_deleted = false
|
||||
- **AND** 記錄還原操作
|
||||
|
||||
### Requirement: Change Logging
|
||||
系統 SHALL 記錄所有關鍵變更操作,包含誰在何時改了什麼。
|
||||
|
||||
#### Scenario: 任務欄位變更記錄
|
||||
- **GIVEN** 使用者修改任務的任何欄位(如截止日期、狀態、指派者)
|
||||
- **WHEN** 變更儲存成功
|
||||
- **THEN** 系統記錄變更前後的值
|
||||
- **AND** 記錄操作者、時間、IP 位址
|
||||
|
||||
#### Scenario: 專案設定變更記錄
|
||||
- **GIVEN** 管理者修改專案設定
|
||||
- **WHEN** 設定變更儲存
|
||||
- **THEN** 系統記錄所有變更的設定項目
|
||||
- **AND** 記錄操作者與時間
|
||||
|
||||
#### Scenario: 權限變更記錄
|
||||
- **GIVEN** 管理者修改使用者權限或角色
|
||||
- **WHEN** 權限變更生效
|
||||
- **THEN** 系統記錄權限變更詳情
|
||||
- **AND** 標記為高敏感度操作
|
||||
|
||||
#### Scenario: 角色指派變更記錄
|
||||
- **GIVEN** 管理者變更使用者角色
|
||||
- **WHEN** role_id 變更儲存
|
||||
- **THEN** 系統記錄 user.role_change 事件
|
||||
- **AND** 標記 sensitivity_level = high
|
||||
|
||||
#### Scenario: 系統管理員變更記錄
|
||||
- **GIVEN** 管理者變更使用者 is_system_admin
|
||||
- **WHEN** 變更生效
|
||||
- **THEN** 系統記錄 user.admin_change 事件
|
||||
- **AND** 標記 sensitivity_level = critical
|
||||
- **AND** 觸發即時警示
|
||||
|
||||
### Requirement: Audit Log Immutability
|
||||
系統 SHALL 確保稽核日誌不可竄改。
|
||||
|
||||
#### Scenario: 日誌寫入
|
||||
- **GIVEN** 需要記錄稽核事件
|
||||
- **WHEN** 日誌寫入
|
||||
- **THEN** 日誌記錄不可被修改或刪除
|
||||
- **AND** 包含校驗碼確保完整性
|
||||
|
||||
#### Scenario: 日誌完整性驗證
|
||||
- **GIVEN** 稽核人員需要驗證日誌完整性
|
||||
- **WHEN** 執行完整性檢查
|
||||
- **THEN** 系統驗證所有日誌記錄的校驗碼
|
||||
- **AND** 報告任何異常
|
||||
|
||||
#### Scenario: 防止日誌修改
|
||||
- **GIVEN** 任何對 audit_logs 表的 UPDATE 操作
|
||||
- **WHEN** 操作執行
|
||||
- **THEN** 資料庫 trigger 拒絕操作並拋出錯誤
|
||||
|
||||
#### Scenario: 防止日誌刪除
|
||||
- **GIVEN** 任何對 audit_logs 表的 DELETE 操作
|
||||
- **WHEN** 操作執行
|
||||
- **THEN** 資料庫 trigger 拒絕操作並拋出錯誤
|
||||
|
||||
## MODIFIED Data Model
|
||||
|
||||
```
|
||||
pjctrl_tasks (新增欄位)
|
||||
├── is_deleted: BOOLEAN DEFAULT false
|
||||
├── deleted_at: DATETIME (nullable)
|
||||
├── deleted_by: UUID (FK -> users, nullable)
|
||||
└── INDEX idx_task_deleted (is_deleted)
|
||||
```
|
||||
|
||||
## MODIFIED Technical Notes
|
||||
|
||||
- 任務刪除改為軟刪除,保留 is_deleted, deleted_at, deleted_by
|
||||
- 資料庫使用 BEFORE UPDATE/DELETE trigger 強制 append-only
|
||||
- 查詢 API 預設過濾 is_deleted = true,管理員可用 include_deleted 參數
|
||||
@@ -1,63 +0,0 @@
|
||||
## Phase 1: Task Soft Delete
|
||||
|
||||
### 1.1 Database Schema
|
||||
- [x] 1.1.1 Task model 新增 is_deleted, deleted_at, deleted_by 欄位
|
||||
- [x] 1.1.2 建立 Alembic migration
|
||||
- [x] 1.1.3 新增 idx_task_deleted 索引
|
||||
|
||||
### 1.2 Task API 修改
|
||||
- [x] 1.2.1 修改 delete_task 為軟刪除
|
||||
- [x] 1.2.2 修改 list_tasks 預設過濾 is_deleted
|
||||
- [x] 1.2.3 修改 get_task 檢查 is_deleted
|
||||
- [x] 1.2.4 新增 include_deleted 查詢參數(管理員)
|
||||
- [x] 1.2.5 新增 POST /api/tasks/{id}/restore 還原 API
|
||||
|
||||
### 1.3 Cascading Updates
|
||||
- [x] 1.3.1 子任務隨父任務軟刪除
|
||||
- [x] 1.3.2 更新 subtask_count 計算排除已刪除
|
||||
|
||||
### 1.4 Testing - Phase 1
|
||||
- [x] 1.4.1 軟刪除功能測試
|
||||
- [x] 1.4.2 查詢過濾測試
|
||||
- [x] 1.4.3 還原功能測試
|
||||
|
||||
## Phase 2: Permission Change Audit
|
||||
|
||||
### 2.1 User Role Change
|
||||
- [x] 2.1.1 修改 update_user API 記錄 role_id 變更
|
||||
- [x] 2.1.2 記錄 is_system_admin 變更
|
||||
|
||||
### 2.2 Role Permission Change
|
||||
- [x] 2.2.1 修改 update_role API 記錄 permissions 變更 (事件類型已定義)
|
||||
- [x] 2.2.2 設定 sensitivity_level = critical
|
||||
|
||||
### 2.3 Audit Alert Integration
|
||||
- [x] 2.3.1 權限變更觸發高敏感度警示
|
||||
- [x] 2.3.2 通知系統管理員
|
||||
|
||||
### 2.4 Testing - Phase 2
|
||||
- [x] 2.4.1 角色變更審計測試 (事件類型已定義並整合)
|
||||
- [x] 2.4.2 權限變更審計測試
|
||||
- [x] 2.4.3 警示觸發測試
|
||||
|
||||
## Phase 3: Append-Only Enforcement
|
||||
|
||||
### 3.1 Database Triggers
|
||||
- [x] 3.1.1 建立 prevent_audit_update trigger (需手動執行於 production)
|
||||
- [x] 3.1.2 建立 prevent_audit_delete trigger (需手動執行於 production)
|
||||
- [x] 3.1.3 新增 migration 包含 triggers
|
||||
|
||||
### 3.2 Verification
|
||||
- [x] 3.2.1 測試 UPDATE 被拒絕 (需 production 環境驗證)
|
||||
- [x] 3.2.2 測試 DELETE 被拒絕 (需 production 環境驗證)
|
||||
- [x] 3.2.3 確認 INSERT 正常運作
|
||||
|
||||
### 3.3 Testing - Phase 3
|
||||
- [x] 3.3.1 Append-only 強制測試 (trigger 語法已驗證)
|
||||
- [x] 3.3.2 Checksum 驗證測試 (已有 test_audit.py 測試)
|
||||
|
||||
## Notes
|
||||
|
||||
- **Triggers**: MySQL triggers 需要 SUPER 權限才能在有 binary logging 的環境建立。Migration 會嘗試建立 trigger,失敗時記錄警告。Production 環境需手動執行 trigger SQL。
|
||||
- **Tests**: 新增 11 個軟刪除相關測試於 tests/test_soft_delete.py
|
||||
- **Total Tests**: 153 tests passing
|
||||
@@ -1,134 +0,0 @@
|
||||
## Context
|
||||
|
||||
collaboration spec 要求即時通知透過 WebSocket 推播,但現行 NotificationService 僅寫入資料庫,未實作即時推送。
|
||||
|
||||
## Goals / Non-Goals
|
||||
|
||||
**Goals:**
|
||||
- 建立 WebSocket 連線管理基礎設施
|
||||
- 通知建立時透過 Redis Pub/Sub 廣播
|
||||
- 使用者連線時補送未讀通知
|
||||
- 前端即時接收並更新通知
|
||||
|
||||
**Non-Goals:**
|
||||
- 不實作通知偏好設定(靜音/訂閱)
|
||||
- 不實作 Push Notification (PWA/Mobile)
|
||||
- 不實作通知分組或摺疊
|
||||
|
||||
## Decisions
|
||||
|
||||
### 1. WebSocket 連線管理
|
||||
|
||||
**Decision:** 使用 in-memory dict + Redis Pub/Sub
|
||||
|
||||
**Rationale:**
|
||||
- 單 process 內使用 dict 維護 WebSocket 連線
|
||||
- 跨 process 透過 Redis Pub/Sub 廣播
|
||||
- 簡單且符合現有架構
|
||||
|
||||
```python
|
||||
class ConnectionManager:
|
||||
def __init__(self):
|
||||
self.active_connections: Dict[str, List[WebSocket]] = {}
|
||||
|
||||
async def connect(self, user_id: str, websocket: WebSocket):
|
||||
await websocket.accept()
|
||||
if user_id not in self.active_connections:
|
||||
self.active_connections[user_id] = []
|
||||
self.active_connections[user_id].append(websocket)
|
||||
|
||||
async def disconnect(self, user_id: str, websocket: WebSocket):
|
||||
self.active_connections[user_id].remove(websocket)
|
||||
if not self.active_connections[user_id]:
|
||||
del self.active_connections[user_id]
|
||||
|
||||
async def send_to_user(self, user_id: str, message: dict):
|
||||
if user_id in self.active_connections:
|
||||
for connection in self.active_connections[user_id]:
|
||||
await connection.send_json(message)
|
||||
```
|
||||
|
||||
### 2. Redis Pub/Sub 架構
|
||||
|
||||
**Decision:** 使用 user-specific channel
|
||||
|
||||
**Rationale:**
|
||||
- Channel 命名: `notifications:{user_id}`
|
||||
- 避免 broadcast 給不相關的 worker
|
||||
- 減少訊息處理量
|
||||
|
||||
```python
|
||||
async def publish_notification(user_id: str, notification: dict):
|
||||
channel = f"notifications:{user_id}"
|
||||
await redis.publish(channel, json.dumps(notification))
|
||||
|
||||
async def subscribe_notifications(user_id: str):
|
||||
pubsub = redis.pubsub()
|
||||
await pubsub.subscribe(f"notifications:{user_id}")
|
||||
return pubsub
|
||||
```
|
||||
|
||||
### 3. 連線時補送未讀
|
||||
|
||||
**Decision:** 連線建立後立即查詢並推送
|
||||
|
||||
**Rationale:**
|
||||
- 確保使用者不漏接通知
|
||||
- 簡化前端狀態同步邏輯
|
||||
|
||||
```python
|
||||
@router.websocket("/ws/notifications")
|
||||
async def websocket_endpoint(websocket: WebSocket, token: str = Query(...)):
|
||||
user = await verify_ws_token(token)
|
||||
await manager.connect(user.id, websocket)
|
||||
|
||||
# 連線時補送未讀
|
||||
unread = get_unread_notifications(db, user.id)
|
||||
await websocket.send_json({
|
||||
"type": "unread_sync",
|
||||
"notifications": [n.dict() for n in unread]
|
||||
})
|
||||
|
||||
# 開始監聽
|
||||
await listen_for_notifications(user.id, websocket)
|
||||
```
|
||||
|
||||
### 4. 訊息格式
|
||||
|
||||
**Decision:** 統一 JSON 格式
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "notification",
|
||||
"data": {
|
||||
"id": "uuid",
|
||||
"type": "mention|assignment|blocker|...",
|
||||
"title": "...",
|
||||
"message": "...",
|
||||
"reference_type": "task|comment",
|
||||
"reference_id": "uuid",
|
||||
"created_at": "ISO8601"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## API Changes
|
||||
|
||||
```
|
||||
# 新增 WebSocket endpoint
|
||||
WS /ws/notifications?token={jwt_token}
|
||||
|
||||
# 訊息類型
|
||||
-> {"type": "unread_sync", "notifications": [...]} # 連線時
|
||||
-> {"type": "notification", "data": {...}} # 新通知
|
||||
-> {"type": "mark_read", "notification_id": "..."} # 已讀確認
|
||||
<- {"type": "ping"} # 心跳
|
||||
```
|
||||
|
||||
## Risks / Trade-offs
|
||||
|
||||
| Risk | Mitigation |
|
||||
|------|------------|
|
||||
| WebSocket 連線數量大 | 使用心跳偵測清理斷線 |
|
||||
| Redis Pub/Sub 可靠性 | Pub/Sub 為 fire-and-forget,已有 DB 紀錄作為 fallback |
|
||||
| Token 驗證 in query | WebSocket 標準限制,token 有過期機制 |
|
||||
@@ -1,50 +0,0 @@
|
||||
# Change: Fix Real-time Notifications Alignment
|
||||
|
||||
## Why
|
||||
現行實作與 collaboration spec 的 Real-time Notifications requirement 有以下差距:
|
||||
1. 通知僅寫入資料庫,未透過 WebSocket 即時推播
|
||||
2. 未使用 Redis Pub/Sub 處理多 process 推播
|
||||
3. 使用者連線時未補送未讀通知
|
||||
|
||||
## What Changes
|
||||
- **WebSocket Manager** - 建立 WebSocket 連線管理模組
|
||||
- **Redis Pub/Sub** - 整合 Redis 處理跨 process 通知推播
|
||||
- **NotificationService** - 新增即時推播呼叫
|
||||
- **API** - 新增 `/ws/notifications` WebSocket endpoint
|
||||
- **Frontend** - 整合 WebSocket 接收即時通知
|
||||
|
||||
## Impact
|
||||
- Affected specs: `collaboration`
|
||||
- Affected code:
|
||||
- `backend/app/core/websocket.py` - 新增 WebSocket 管理
|
||||
- `backend/app/core/redis_pubsub.py` - 新增 Redis Pub/Sub 服務
|
||||
- `backend/app/services/notification_service.py` - 加入即時推播
|
||||
- `backend/app/api/notifications/router.py` - 新增 WebSocket endpoint
|
||||
- `frontend/src/services/websocket.ts` - 新增 WebSocket client
|
||||
- `frontend/src/contexts/NotificationContext.tsx` - 整合即時通知
|
||||
|
||||
## Implementation Phases
|
||||
|
||||
### Phase 1: WebSocket Infrastructure
|
||||
- WebSocket 連線管理器
|
||||
- 使用者連線/斷線處理
|
||||
- 連線時補送未讀通知
|
||||
|
||||
### Phase 2: Redis Pub/Sub Integration
|
||||
- Redis Pub/Sub 服務封裝
|
||||
- 多 process 通知廣播
|
||||
- 訊息序列化/反序列化
|
||||
|
||||
### Phase 3: Service Integration
|
||||
- NotificationService 加入推播
|
||||
- 前端 WebSocket client
|
||||
- 未讀數量即時更新
|
||||
|
||||
## Dependencies
|
||||
- collaboration (已完成)
|
||||
- Redis 已在 user-auth 中使用
|
||||
|
||||
## Technical Decisions
|
||||
- 使用 FastAPI WebSocket 原生支援
|
||||
- Redis Pub/Sub 處理多 worker 同步
|
||||
- 使用者以 user_id 為 channel key
|
||||
@@ -1,36 +0,0 @@
|
||||
## MODIFIED Requirements
|
||||
|
||||
### Requirement: Real-time Notifications
|
||||
系統 SHALL 透過 WebSocket 與 Redis Pub/Sub 推播即時通知。
|
||||
|
||||
#### Scenario: 即時通知推播
|
||||
- **GIVEN** 發生需要通知的事件(如:被指派任務、被 @提及、阻礙標記)
|
||||
- **WHEN** NotificationService.create_notification() 執行
|
||||
- **THEN** 系統透過 Redis Pub/Sub 發布通知至 `notifications:{user_id}` channel
|
||||
- **AND** 訂閱該 channel 的 WebSocket 連線接收訊息
|
||||
- **AND** ConnectionManager 推送通知給使用者的 WebSocket
|
||||
|
||||
#### Scenario: 連線時補送未讀
|
||||
- **GIVEN** 使用者建立 WebSocket 連線
|
||||
- **WHEN** 連線驗證成功
|
||||
- **THEN** 系統查詢該使用者的未讀通知 (is_read = false)
|
||||
- **AND** 透過 unread_sync 訊息一次推送所有未讀通知
|
||||
- **AND** 開始訂閱 Redis channel 接收新通知
|
||||
|
||||
#### Scenario: 心跳偵測
|
||||
- **GIVEN** 使用者已建立 WebSocket 連線
|
||||
- **WHEN** 連線超過心跳間隔無回應
|
||||
- **THEN** 系統將連線標記為斷線並從 ConnectionManager 移除
|
||||
|
||||
## MODIFIED Technical Notes
|
||||
|
||||
- 使用 Redis Pub/Sub 處理即時通知推播
|
||||
- WebSocket 連線管理:
|
||||
- ConnectionManager 維護 user_id → WebSocket[] 映射
|
||||
- 心跳偵測清理斷線連線
|
||||
- Token 驗證透過 query parameter
|
||||
- 通知推播流程:
|
||||
1. NotificationService.create_notification() 建立通知
|
||||
2. 呼叫 redis_pubsub.publish_notification() 發布
|
||||
3. 訂閱該 user channel 的 worker 收到訊息
|
||||
4. ConnectionManager.send_to_user() 推送給連線的 WebSocket
|
||||
@@ -1,56 +0,0 @@
|
||||
## Phase 1: WebSocket Infrastructure
|
||||
|
||||
### 1.1 Connection Manager
|
||||
- [ ] 1.1.1 建立 backend/app/core/websocket.py
|
||||
- [ ] 1.1.2 實作 ConnectionManager class
|
||||
- [ ] 1.1.3 實作 connect/disconnect/send_to_user 方法
|
||||
- [ ] 1.1.4 加入心跳偵測機制
|
||||
|
||||
### 1.2 WebSocket Endpoint
|
||||
- [ ] 1.2.1 新增 WS /ws/notifications endpoint
|
||||
- [ ] 1.2.2 實作 WebSocket token 驗證
|
||||
- [ ] 1.2.3 連線時查詢並推送未讀通知
|
||||
- [ ] 1.2.4 處理 WebSocket 異常與斷線
|
||||
|
||||
### 1.3 Testing - Phase 1
|
||||
- [ ] 1.3.1 WebSocket 連線測試
|
||||
- [ ] 1.3.2 未讀通知補送測試
|
||||
- [ ] 1.3.3 斷線處理測試
|
||||
|
||||
## Phase 2: Redis Pub/Sub Integration
|
||||
|
||||
### 2.1 Redis Pub/Sub Service
|
||||
- [ ] 2.1.1 建立 backend/app/core/redis_pubsub.py
|
||||
- [ ] 2.1.2 實作 publish_notification 函數
|
||||
- [ ] 2.1.3 實作 subscribe_user_channel 函數
|
||||
- [ ] 2.1.4 訊息 JSON 序列化處理
|
||||
|
||||
### 2.2 Cross-Process Broadcasting
|
||||
- [ ] 2.2.1 WebSocket endpoint 訂閱 user channel
|
||||
- [ ] 2.2.2 收到 Redis 訊息時推送給連線
|
||||
- [ ] 2.2.3 處理訂閱錯誤與重連
|
||||
|
||||
### 2.3 Testing - Phase 2
|
||||
- [ ] 2.3.1 Redis Pub/Sub 單元測試
|
||||
- [ ] 2.3.2 跨 process 通知測試(手動驗證)
|
||||
|
||||
## Phase 3: Service Integration
|
||||
|
||||
### 3.1 NotificationService 整合
|
||||
- [ ] 3.1.1 create_notification 後呼叫 publish_notification
|
||||
- [ ] 3.1.2 確保所有通知類型都即時推播
|
||||
- [ ] 3.1.3 處理 Redis 連線失敗 gracefully
|
||||
|
||||
### 3.2 Frontend WebSocket Client
|
||||
- [ ] 3.2.1 建立 frontend/src/services/websocket.ts
|
||||
- [ ] 3.2.2 實作 WebSocket 連線與重連邏輯
|
||||
- [ ] 3.2.3 訊息處理與分發
|
||||
|
||||
### 3.3 NotificationContext 整合
|
||||
- [ ] 3.3.1 修改 NotificationContext 使用 WebSocket
|
||||
- [ ] 3.3.2 收到通知時更新未讀數量
|
||||
- [ ] 3.3.3 收到 unread_sync 時同步狀態
|
||||
|
||||
### 3.4 Testing - Phase 3
|
||||
- [ ] 3.4.1 完整即時通知流程測試
|
||||
- [ ] 3.4.2 前端 WebSocket 整合測試
|
||||
@@ -1,119 +0,0 @@
|
||||
## Context
|
||||
|
||||
automation spec 定義週報須包含:已完成、進行中、逾期、阻礙中、下週預計任務清單。現行實作僅提供數量統計與部分摘要。
|
||||
|
||||
## Goals / Non-Goals
|
||||
|
||||
**Goals:**
|
||||
- 週報內容包含完整任務清單
|
||||
- 新增阻礙中任務清單
|
||||
- 新增下週預計完成任務清單
|
||||
|
||||
**Non-Goals:**
|
||||
- 不實作週報自訂欄位篩選
|
||||
- 不實作週報匯出 PDF/Excel
|
||||
|
||||
## Decisions
|
||||
|
||||
### 1. 週報內容結構
|
||||
|
||||
**Decision:** 擴充 content JSON 結構
|
||||
|
||||
**Current:**
|
||||
```json
|
||||
{
|
||||
"summary": { "completed_count": 5, "in_progress_count": 3, ... },
|
||||
"projects": [
|
||||
{
|
||||
"completed_tasks": [{"id": "...", "title": "..."}], // 限制 5 筆
|
||||
"overdue_tasks": [...] // 限制 5 筆
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**Proposed:**
|
||||
```json
|
||||
{
|
||||
"summary": {
|
||||
"completed_count": 5,
|
||||
"in_progress_count": 3,
|
||||
"overdue_count": 2,
|
||||
"blocked_count": 1,
|
||||
"next_week_count": 4,
|
||||
"total_tasks": 15
|
||||
},
|
||||
"projects": [
|
||||
{
|
||||
"project_id": "uuid",
|
||||
"project_title": "Project Name",
|
||||
"completed_tasks": [
|
||||
{"id": "...", "title": "...", "completed_at": "ISO8601", "assignee_name": "..."}
|
||||
],
|
||||
"in_progress_tasks": [
|
||||
{"id": "...", "title": "...", "assignee_name": "...", "due_date": "ISO8601"}
|
||||
],
|
||||
"overdue_tasks": [
|
||||
{"id": "...", "title": "...", "due_date": "ISO8601", "days_overdue": 3}
|
||||
],
|
||||
"blocked_tasks": [
|
||||
{"id": "...", "title": "...", "blocker_reason": "...", "blocked_since": "ISO8601"}
|
||||
],
|
||||
"next_week_tasks": [
|
||||
{"id": "...", "title": "...", "due_date": "ISO8601", "assignee_name": "..."}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### 2. 阻礙任務識別
|
||||
|
||||
**Decision:** 查詢 Blocker 表關聯
|
||||
|
||||
```python
|
||||
# 取得有未解除阻礙的任務
|
||||
blocked_task_ids = db.query(Blocker.task_id).filter(
|
||||
Blocker.resolved_at.is_(None)
|
||||
).subquery()
|
||||
|
||||
blocked_tasks = db.query(Task).filter(
|
||||
Task.project_id.in_(project_ids),
|
||||
Task.id.in_(blocked_task_ids)
|
||||
).all()
|
||||
```
|
||||
|
||||
### 3. 下週預計任務
|
||||
|
||||
**Decision:** due_date 在下週一至週日
|
||||
|
||||
```python
|
||||
next_week_start = week_end # 本週末 = 下週初
|
||||
next_week_end = next_week_start + timedelta(days=7)
|
||||
|
||||
next_week_tasks = db.query(Task).filter(
|
||||
Task.project_id.in_(project_ids),
|
||||
Task.due_date >= next_week_start,
|
||||
Task.due_date < next_week_end,
|
||||
Task.status.not_in(["done", "completed"])
|
||||
).all()
|
||||
```
|
||||
|
||||
### 4. 任務明細欄位
|
||||
|
||||
**Decision:** 包含以下欄位供顯示
|
||||
|
||||
| 任務類型 | 額外欄位 |
|
||||
|---------|---------|
|
||||
| completed | completed_at (updated_at), assignee_name |
|
||||
| in_progress | assignee_name, due_date |
|
||||
| overdue | due_date, days_overdue |
|
||||
| blocked | blocker_reason, blocked_since |
|
||||
| next_week | due_date, assignee_name |
|
||||
|
||||
## Risks / Trade-offs
|
||||
|
||||
| Risk | Mitigation |
|
||||
|------|------------|
|
||||
| 大量任務導致 content JSON 過大 | 評估後續加入分頁或限制 |
|
||||
| 阻礙查詢需 JOIN | 使用 subquery 減少 N+1 |
|
||||
@@ -1,37 +0,0 @@
|
||||
# Change: Fix Weekly Report Content Alignment
|
||||
|
||||
## Why
|
||||
現行 ReportService.get_weekly_stats 與 automation spec 的週報內容要求有差距:
|
||||
1. 任務清單僅顯示 5 筆摘要,spec 要求完整清單
|
||||
2. 未包含阻礙中任務清單 (blocker_flag = true)
|
||||
3. 未包含下週預計完成任務 (due_date 在下週)
|
||||
|
||||
## What Changes
|
||||
- **ReportService** - 擴充 get_weekly_stats 回傳完整任務明細
|
||||
- **Report Content** - 新增 blocked_tasks 與 next_week_tasks 欄位
|
||||
- **ReportHistory** - content JSON 結構擴充
|
||||
|
||||
## Impact
|
||||
- Affected specs: `automation`
|
||||
- Affected code:
|
||||
- `backend/app/services/report_service.py` - 擴充週報內容
|
||||
- `frontend/src/components/WeeklyReportPreview.tsx` - 顯示完整清單
|
||||
|
||||
## Implementation Phases
|
||||
|
||||
### Phase 1: Backend Report Enhancement
|
||||
- 擴充 get_weekly_stats 回傳完整任務清單
|
||||
- 新增 blocked_tasks 欄位
|
||||
- 新增 next_week_tasks 欄位
|
||||
|
||||
### Phase 2: Frontend Display
|
||||
- 更新 WeeklyReportPreview 顯示完整清單
|
||||
- 可摺疊/展開的任務分類區塊
|
||||
|
||||
## Dependencies
|
||||
- automation (已完成)
|
||||
- collaboration (blocker 功能)
|
||||
|
||||
## Technical Decisions
|
||||
- 任務清單不設上限,由前端分頁或摺疊處理
|
||||
- 下週預計任務以 due_date 在下週一至週日為準
|
||||
@@ -1,38 +0,0 @@
|
||||
## MODIFIED Requirements
|
||||
|
||||
### Requirement: Automated Weekly Report
|
||||
系統 SHALL 每週五下午 4:00 自動彙整完整任務清單發送給主管。
|
||||
|
||||
#### Scenario: 週報內容完整清單
|
||||
- **GIVEN** 週報生成中
|
||||
- **WHEN** 系統彙整資料
|
||||
- **THEN** 週報包含各專案的:
|
||||
- 本週已完成任務清單(含 completed_at, assignee_name)
|
||||
- 進行中任務清單(含 assignee_name, due_date)
|
||||
- 逾期任務警示(含 due_date, days_overdue)
|
||||
- 阻礙中任務清單(含 blocker_reason, blocked_since)
|
||||
- 下週預計完成任務(含 due_date, assignee_name)
|
||||
- **AND** 不設任務數量上限
|
||||
|
||||
#### Scenario: 阻礙任務識別
|
||||
- **GIVEN** 任務有未解除的 Blocker 記錄
|
||||
- **WHEN** 週報查詢阻礙任務
|
||||
- **THEN** 系統查詢 Blocker 表 resolved_at IS NULL 的任務
|
||||
- **AND** 顯示阻礙原因與開始時間
|
||||
|
||||
#### Scenario: 下週預計任務
|
||||
- **GIVEN** 任務的 due_date 在下週範圍內
|
||||
- **WHEN** 週報查詢下週預計任務
|
||||
- **THEN** 系統篩選 due_date >= 下週一 且 < 下週日
|
||||
- **AND** 排除已完成狀態的任務
|
||||
|
||||
## MODIFIED Technical Notes
|
||||
|
||||
- 週報 content JSON 結構擴充:
|
||||
- summary: 包含 blocked_count, next_week_count
|
||||
- projects[].completed_tasks: 無數量限制,含 completed_at, assignee_name
|
||||
- projects[].in_progress_tasks: 新增欄位
|
||||
- projects[].blocked_tasks: 新增欄位,含 blocker_reason, blocked_since
|
||||
- projects[].next_week_tasks: 新增欄位,含 due_date, assignee_name
|
||||
- 阻礙任務透過 Blocker 表 subquery 查詢
|
||||
- 下週計算以本週結束後 7 天為範圍
|
||||
@@ -1,38 +0,0 @@
|
||||
## Phase 1: Backend Report Enhancement
|
||||
|
||||
### 1.1 ReportService 擴充
|
||||
- [x] 1.1.1 移除 completed_tasks/overdue_tasks 的 5 筆限制
|
||||
- [x] 1.1.2 新增 in_progress_tasks 完整清單
|
||||
- [x] 1.1.3 新增 blocked_tasks 查詢與清單
|
||||
- [x] 1.1.4 新增 next_week_tasks 查詢與清單
|
||||
- [x] 1.1.5 擴充 summary 包含 blocked_count 與 next_week_count
|
||||
|
||||
### 1.2 任務明細欄位
|
||||
- [x] 1.2.1 completed_tasks 加入 completed_at, assignee_name
|
||||
- [x] 1.2.2 in_progress_tasks 加入 assignee_name, due_date
|
||||
- [x] 1.2.3 overdue_tasks 加入 days_overdue 計算
|
||||
- [x] 1.2.4 blocked_tasks 加入 blocker_reason, blocked_since
|
||||
- [x] 1.2.5 next_week_tasks 加入 due_date, assignee_name
|
||||
|
||||
### 1.3 Testing - Phase 1
|
||||
- [x] 1.3.1 週報內容結構測試
|
||||
- [x] 1.3.2 阻礙任務查詢測試
|
||||
- [x] 1.3.3 下週預計任務測試
|
||||
|
||||
## Phase 2: Frontend Display
|
||||
|
||||
### 2.1 WeeklyReportPreview 更新
|
||||
- [x] 2.1.1 新增 BlockedTasksSection 元件
|
||||
- [x] 2.1.2 新增 NextWeekTasksSection 元件
|
||||
- [x] 2.1.3 更新 CompletedTasksSection 顯示完整清單
|
||||
- [x] 2.1.4 更新 InProgressTasksSection 顯示完整清單
|
||||
- [x] 2.1.5 更新 OverdueTasksSection 顯示 days_overdue
|
||||
|
||||
### 2.2 UI 改善
|
||||
- [x] 2.2.1 可摺疊區塊設計
|
||||
- [x] 2.2.2 任務項目樣式統一
|
||||
- [x] 2.2.3 逾期/阻礙 highlight 樣式
|
||||
|
||||
### 2.3 Testing - Phase 2
|
||||
- [x] 2.3.1 前端週報顯示測試
|
||||
- [x] 2.3.2 空清單狀態測試
|
||||
@@ -1,15 +0,0 @@
|
||||
# Change: Add Capacity Update API
|
||||
|
||||
## Why
|
||||
MED-007: The `Capacity Planning` requirement exists but there is no API to update user capacity. The original design had `PUT /api/users/{id}/capacity` but it was replaced with `GET /api/workload/me`. Managers cannot adjust team members' weekly capacity.
|
||||
|
||||
## What Changes
|
||||
- Add `PUT /api/users/{user_id}/capacity` endpoint
|
||||
- Add capacity validation logic
|
||||
- Record capacity changes in audit trail
|
||||
|
||||
## Impact
|
||||
- Affected specs: resource-management
|
||||
- Affected code:
|
||||
- `backend/app/api/users/router.py` (modify)
|
||||
- `backend/app/schemas/user.py` (modify)
|
||||
@@ -1,29 +0,0 @@
|
||||
## MODIFIED Requirements
|
||||
|
||||
### Requirement: Capacity Planning
|
||||
|
||||
系統 SHALL 支援人員容量規劃,包含預設容量與臨時調整。
|
||||
|
||||
#### Scenario: 設定人員預設容量
|
||||
- **GIVEN** 管理者需要設定人員的週工時上限
|
||||
- **WHEN** 管理者更新使用者的 `capacity` 值
|
||||
- **THEN** 系統儲存新的容量設定
|
||||
- **AND** 後續負載計算使用新容量值
|
||||
|
||||
#### Scenario: 容量為零處理
|
||||
- **GIVEN** 使用者的容量設為 0
|
||||
- **WHEN** 系統計算該使用者的負載
|
||||
- **THEN** `load_percentage` 顯示為 `null`
|
||||
- **AND** `load_level` 顯示為 `unavailable`
|
||||
|
||||
#### Scenario: 容量更新 API
|
||||
- **GIVEN** 管理者需要更新團隊成員的容量
|
||||
- **WHEN** 管理者呼叫 `PUT /api/users/{user_id}/capacity` 並提供新容量值
|
||||
- **THEN** 系統驗證容量值在有效範圍內 (0-168 小時)
|
||||
- **AND** 更新使用者的 capacity 欄位
|
||||
- **AND** 記錄變更至稽核日誌
|
||||
|
||||
#### Scenario: 容量更新權限控制
|
||||
- **GIVEN** 一般使用者嘗試更新他人容量
|
||||
- **WHEN** 使用者呼叫 `PUT /api/users/{other_id}/capacity`
|
||||
- **THEN** 系統拒絕請求並回傳 403 Forbidden
|
||||
@@ -1,24 +0,0 @@
|
||||
# Tasks: add-capacity-update-api
|
||||
|
||||
## Phase 1: Backend - Schema & Validation
|
||||
|
||||
- [x] 1.1 Add CapacityUpdate schema to `backend/app/schemas/user.py`
|
||||
- [x] 1.2 Add capacity validation (must be >= 0, <= 168 hours/week)
|
||||
|
||||
## Phase 2: Backend - API Endpoint
|
||||
|
||||
- [x] 2.1 Implement `PUT /api/users/{user_id}/capacity` endpoint
|
||||
- [x] 2.2 Add permission check (only admin/manager can update others)
|
||||
- [x] 2.3 Record capacity change in audit trail
|
||||
- [x] 2.4 Invalidate workload cache after capacity update
|
||||
|
||||
## Phase 3: Backend - Testing
|
||||
|
||||
- [x] 3.1 Unit tests for capacity update endpoint
|
||||
- [x] 3.2 Permission tests (admin vs regular user)
|
||||
|
||||
## Validation Criteria
|
||||
|
||||
- Only authorized users can update capacity
|
||||
- Capacity changes are audit logged
|
||||
- Workload calculations reflect new capacity immediately
|
||||
@@ -1,18 +0,0 @@
|
||||
# Change: Add Project Health Dashboard
|
||||
|
||||
## Why
|
||||
MED-006: The `Multi-Project Health Dashboard` requirement exists in the spec but has no implementation. Managers cannot view an overview of all projects' health status, making it difficult to identify at-risk projects.
|
||||
|
||||
## What Changes
|
||||
- Add `pjctrl_project_health` database table and migration
|
||||
- Implement backend API endpoints for project health data
|
||||
- Create frontend Project Health Dashboard page
|
||||
- Add health score calculation service
|
||||
|
||||
## Impact
|
||||
- Affected specs: resource-management
|
||||
- Affected code:
|
||||
- `backend/app/models/project_health.py` (new)
|
||||
- `backend/app/api/health/router.py` (new)
|
||||
- `backend/app/services/health_service.py` (new)
|
||||
- `frontend/src/pages/ProjectHealthPage.tsx` (new)
|
||||
@@ -1,28 +0,0 @@
|
||||
## MODIFIED Requirements
|
||||
|
||||
### Requirement: Multi-Project Health Dashboard
|
||||
系統 SHALL 提供多專案健康看板,讓主管一覽所有專案狀態。
|
||||
|
||||
#### Scenario: 專案健康總覽
|
||||
- **GIVEN** 主管負責多個專案
|
||||
- **WHEN** 主管開啟健康看板
|
||||
- **THEN** 顯示所有專案的進度、風險指標、延遲任務數
|
||||
- **AND** 可依風險程度排序
|
||||
|
||||
#### Scenario: 專案延遲警示
|
||||
- **GIVEN** 專案有任務超過截止日期
|
||||
- **WHEN** 主管查看健康看板
|
||||
- **THEN** 該專案標示為延遲狀態
|
||||
- **AND** 顯示延遲任務數量與影響
|
||||
|
||||
#### Scenario: 專案健康 API
|
||||
- **GIVEN** 後端系統運行中
|
||||
- **WHEN** 客戶端請求 `GET /api/projects/health`
|
||||
- **THEN** 系統回傳所有可存取專案的健康數據
|
||||
- **AND** 包含 `total_tasks`, `completed_tasks`, `overdue_tasks`, `blocked_tasks`, `risk_score`
|
||||
|
||||
#### Scenario: 單一專案健康詳情
|
||||
- **GIVEN** 主管需要查看特定專案詳情
|
||||
- **WHEN** 客戶端請求 `GET /api/projects/{id}/health`
|
||||
- **THEN** 系統回傳該專案的詳細健康數據
|
||||
- **AND** 包含任務分類統計與風險評估
|
||||
@@ -1,35 +0,0 @@
|
||||
# Tasks: add-project-health-dashboard
|
||||
|
||||
## Phase 1: Backend - Database & Model
|
||||
|
||||
- [x] 1.1 Create ProjectHealth model (`backend/app/models/project_health.py`)
|
||||
- [x] 1.2 Create Alembic migration for `pjctrl_project_health` table
|
||||
- [x] 1.3 Create ProjectHealth schemas (`backend/app/schemas/project_health.py`)
|
||||
|
||||
## Phase 2: Backend - Service & API
|
||||
|
||||
- [x] 2.1 Create HealthService class (`backend/app/services/health_service.py`)
|
||||
- Calculate risk score based on overdue/blocked tasks
|
||||
- Aggregate project statistics
|
||||
- [x] 2.2 Create health router (`backend/app/api/health/router.py`)
|
||||
- [x] 2.3 Implement `GET /api/projects/health` - List all projects health
|
||||
- [x] 2.4 Implement `GET /api/projects/{id}/health` - Single project health
|
||||
- [x] 2.5 Register health router in main.py
|
||||
|
||||
## Phase 3: Backend - Testing
|
||||
|
||||
- [x] 3.1 Unit tests for HealthService
|
||||
- [x] 3.2 API endpoint tests
|
||||
|
||||
## Phase 4: Frontend - UI Components
|
||||
|
||||
- [x] 4.1 Create ProjectHealthPage.tsx
|
||||
- [x] 4.2 Create ProjectHealthCard component
|
||||
- [x] 4.3 Add route to App.tsx
|
||||
- [x] 4.4 Add navigation link in Layout
|
||||
|
||||
## Validation Criteria
|
||||
|
||||
- Risk score correctly reflects overdue and blocked tasks
|
||||
- Dashboard shows all accessible projects
|
||||
- Color-coded status indicators (green/yellow/red)
|
||||
@@ -1,18 +0,0 @@
|
||||
# Change: Add Rate Limiting for API Security
|
||||
|
||||
## Why
|
||||
Login endpoint and other sensitive APIs lack rate limiting protection, making them vulnerable to brute force attacks and DoS attempts. This is a critical security gap identified in the code review (HIGH-003).
|
||||
|
||||
## What Changes
|
||||
- Add slowapi dependency for rate limiting
|
||||
- Implement rate limiting middleware
|
||||
- Apply rate limits to login endpoint (5 requests/minute)
|
||||
- Apply rate limits to other sensitive endpoints
|
||||
- Return proper 429 Too Many Requests responses
|
||||
|
||||
## Impact
|
||||
- Affected specs: user-auth
|
||||
- Affected code:
|
||||
- `backend/requirements.txt` - add slowapi
|
||||
- `backend/app/main.py` - initialize limiter
|
||||
- `backend/app/api/auth/router.py` - apply rate limits
|
||||
@@ -1,20 +0,0 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: API Rate Limiting
|
||||
The system SHALL implement rate limiting to protect against brute force attacks and DoS attempts.
|
||||
|
||||
#### Scenario: Login rate limit enforcement
|
||||
- **GIVEN** a client IP has made 5 login attempts within 1 minute
|
||||
- **WHEN** the client attempts another login
|
||||
- **THEN** the system returns HTTP 429 Too Many Requests
|
||||
- **AND** the response includes a Retry-After header
|
||||
|
||||
#### Scenario: Rate limit window reset
|
||||
- **GIVEN** a client has exceeded the rate limit
|
||||
- **WHEN** the rate limit window expires (1 minute)
|
||||
- **THEN** the client can make new requests
|
||||
|
||||
#### Scenario: Rate limit per IP
|
||||
- **GIVEN** rate limiting is IP-based
|
||||
- **WHEN** different IPs make requests
|
||||
- **THEN** each IP has its own rate limit counter
|
||||
@@ -1,16 +0,0 @@
|
||||
# Tasks: Add Rate Limiting
|
||||
|
||||
## 1. Backend Implementation
|
||||
- [x] 1.1 Add slowapi to requirements.txt
|
||||
- [x] 1.2 Create rate limiter configuration in `app/core/rate_limiter.py`
|
||||
- [x] 1.3 Initialize limiter in main.py with exception handlers
|
||||
- [x] 1.4 Apply @limiter.limit("5/minute") to login endpoint
|
||||
- [x] 1.5 Apply appropriate limits to password reset and registration endpoints (if exist) - N/A, no such endpoints exist
|
||||
|
||||
## 2. Testing
|
||||
- [x] 2.1 Write test for rate limit enforcement
|
||||
- [x] 2.2 Verify 429 response format matches API standards
|
||||
- [x] 2.3 Test rate limit reset after window expires - covered by memory storage reset in test fixtures
|
||||
|
||||
## 3. Documentation
|
||||
- [x] 3.1 Update API documentation with rate limit information - inline comments in code
|
||||
@@ -1,23 +0,0 @@
|
||||
# Change: Add Resource Management UI
|
||||
|
||||
## Why
|
||||
HIGH-006: The Resource Management module backend API exists but there is no frontend UI. Managers cannot visualize team workload distribution, making capacity planning difficult.
|
||||
|
||||
Issues addressed:
|
||||
- HIGH-006: 資源管理模組前端 UI 未開發
|
||||
|
||||
## What Changes
|
||||
- Create WorkloadPage.tsx with workload heatmap visualization
|
||||
- Create WorkloadHeatmap.tsx component for color-coded user load display
|
||||
- Create WorkloadUserDetail.tsx component for detailed task breakdown
|
||||
- Add /workload route to App.tsx router
|
||||
- Add navigation link in Dashboard or sidebar
|
||||
|
||||
## Impact
|
||||
- Affected specs: resource-management
|
||||
- Affected code:
|
||||
- `frontend/src/pages/WorkloadPage.tsx` - new page
|
||||
- `frontend/src/components/WorkloadHeatmap.tsx` - new component
|
||||
- `frontend/src/components/WorkloadUserDetail.tsx` - new component
|
||||
- `frontend/src/services/workload.ts` - new API service
|
||||
- `frontend/src/App.tsx` - add route
|
||||
@@ -1,26 +0,0 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Workload Heatmap UI
|
||||
The system SHALL provide a visual workload heatmap interface for managers.
|
||||
|
||||
#### Scenario: View workload heatmap
|
||||
- **GIVEN** user is logged in as manager or admin
|
||||
- **WHEN** user navigates to /workload page
|
||||
- **THEN** system displays a heatmap showing all accessible users' workload
|
||||
- **AND** each user cell is color-coded by load level (green/yellow/red)
|
||||
|
||||
#### Scenario: Navigate between weeks
|
||||
- **GIVEN** user is viewing the workload page
|
||||
- **WHEN** user clicks previous/next week buttons
|
||||
- **THEN** the heatmap updates to show that week's workload data
|
||||
|
||||
#### Scenario: View user workload details
|
||||
- **GIVEN** user is viewing the workload heatmap
|
||||
- **WHEN** user clicks on a specific user's cell
|
||||
- **THEN** a modal/drawer opens showing that user's task breakdown
|
||||
- **AND** tasks show title, project, time estimate, and due date
|
||||
|
||||
#### Scenario: Filter by department
|
||||
- **GIVEN** user is a system admin
|
||||
- **WHEN** user selects a department from the filter
|
||||
- **THEN** the heatmap shows only users from that department
|
||||
@@ -1,29 +0,0 @@
|
||||
# Tasks: Add Resource Management UI
|
||||
|
||||
## 1. API Service
|
||||
- [x] 1.1 Create workload.ts service with API calls for heatmap, user detail, and my workload
|
||||
|
||||
## 2. Components
|
||||
- [x] 2.1 Create WorkloadHeatmap.tsx component
|
||||
- Display users in a grid/table
|
||||
- Color-coded load levels (green=normal, yellow=warning, red=overloaded)
|
||||
- Show allocated/capacity hours and percentage
|
||||
- [x] 2.2 Create WorkloadUserDetail.tsx component
|
||||
- Show user's task list for the selected week
|
||||
- Display task title, project, time estimate, due date
|
||||
|
||||
## 3. Page
|
||||
- [x] 3.1 Create WorkloadPage.tsx
|
||||
- Week selector (navigate between weeks)
|
||||
- Department filter (for admins) - Note: Basic implementation, can be enhanced
|
||||
- Integrate WorkloadHeatmap component
|
||||
- Click user to show WorkloadUserDetail in modal/drawer
|
||||
|
||||
## 4. Integration
|
||||
- [x] 4.1 Add /workload route to App.tsx
|
||||
- [x] 4.2 Add navigation link in Layout sidebar
|
||||
|
||||
## 5. Testing
|
||||
- [x] 5.1 Verify heatmap loads correctly
|
||||
- [x] 5.2 Verify user detail modal shows tasks
|
||||
- [x] 5.3 Verify week navigation works
|
||||
@@ -1,16 +0,0 @@
|
||||
# Change: Add Schedule Triggers
|
||||
|
||||
## Why
|
||||
MED-008: The `Trigger Conditions` requirement includes time-based triggers (cron expressions), but the execution logic is not implemented. The scheduler setup exists for weekly reports but schedule-type triggers cannot be executed.
|
||||
|
||||
## What Changes
|
||||
- Implement cron expression parsing for schedule triggers
|
||||
- Add scheduler job for evaluating schedule triggers
|
||||
- Support deadline reminder triggers
|
||||
|
||||
## Impact
|
||||
- Affected specs: automation
|
||||
- Affected code:
|
||||
- `backend/app/services/trigger_service.py` (modify)
|
||||
- `backend/app/services/trigger_scheduler.py` (new)
|
||||
- `backend/app/scheduler.py` (modify)
|
||||
@@ -1,31 +0,0 @@
|
||||
## MODIFIED Requirements
|
||||
|
||||
### Requirement: Trigger Conditions
|
||||
系統 SHALL 支援多種觸發條件類型。
|
||||
|
||||
#### Scenario: 欄位變更條件
|
||||
- **GIVEN** 觸發器設定為「當 Status 欄位變更為特定值」
|
||||
- **WHEN** 任務的 Status 欄位變更為該值
|
||||
- **THEN** 觸發器被觸發
|
||||
|
||||
#### Scenario: 時間條件
|
||||
- **GIVEN** 觸發器設定為「每週五下午 4:00」
|
||||
- **WHEN** 系統時間達到設定時間
|
||||
- **THEN** 觸發器被觸發
|
||||
|
||||
#### Scenario: 複合條件
|
||||
- **GIVEN** 觸發器設定為「當 Status = 完成 且 Priority = 高」
|
||||
- **WHEN** 任務同時滿足兩個條件
|
||||
- **THEN** 觸發器被觸發
|
||||
|
||||
#### Scenario: Cron 表達式觸發
|
||||
- **GIVEN** 觸發器設定為 cron 表達式 (如 `0 9 * * 1` 每週一早上 9 點)
|
||||
- **WHEN** 系統時間匹配 cron 表達式
|
||||
- **THEN** 系統評估並執行該觸發器
|
||||
- **AND** 記錄執行結果至 trigger_logs
|
||||
|
||||
#### Scenario: 截止日期提醒
|
||||
- **GIVEN** 觸發器設定為「截止日前 N 天提醒」
|
||||
- **WHEN** 任務距離截止日剩餘 N 天
|
||||
- **THEN** 系統發送提醒通知給任務指派者
|
||||
- **AND** 每個任務每個提醒設定只觸發一次
|
||||
@@ -1,26 +0,0 @@
|
||||
# Tasks: add-schedule-triggers
|
||||
|
||||
## Phase 1: Backend - Cron Support
|
||||
|
||||
- [x] 1.1 Add croniter dependency to requirements.txt
|
||||
- [x] 1.2 Create TriggerSchedulerService (`backend/app/services/trigger_scheduler.py`)
|
||||
- [x] 1.3 Implement cron expression validation in trigger creation
|
||||
- [x] 1.4 Implement `evaluate_schedule_triggers()` method
|
||||
|
||||
## Phase 2: Backend - Scheduler Integration
|
||||
|
||||
- [x] 2.1 Add scheduled job to evaluate schedule triggers (every minute)
|
||||
- [x] 2.2 Implement deadline reminder logic (check tasks N days before due)
|
||||
- [x] 2.3 Update trigger logs for schedule trigger executions
|
||||
|
||||
## Phase 3: Backend - Testing
|
||||
|
||||
- [x] 3.1 Unit tests for cron expression parsing
|
||||
- [x] 3.2 Unit tests for deadline reminder logic
|
||||
- [x] 3.3 Integration tests for schedule trigger execution
|
||||
|
||||
## Validation Criteria
|
||||
|
||||
- [x] Cron expressions are validated on trigger creation
|
||||
- [x] Schedule triggers execute at specified times
|
||||
- [x] Deadline reminders sent N days before task due date
|
||||
@@ -1,16 +0,0 @@
|
||||
# Change: Add Watermark Feature
|
||||
|
||||
## Why
|
||||
MED-009: The `Dynamic Watermarking` requirement exists in the spec but is not implemented. Downloaded files do not contain user watermarks, making it impossible to trace document leaks.
|
||||
|
||||
## What Changes
|
||||
- Create WatermarkService for image and PDF watermarking
|
||||
- Integrate watermark generation into download flow
|
||||
- Support configurable watermark content (name, employee ID, timestamp)
|
||||
|
||||
## Impact
|
||||
- Affected specs: document-management
|
||||
- Affected code:
|
||||
- `backend/app/services/watermark_service.py` (new)
|
||||
- `backend/app/api/attachments/router.py` (modify download endpoint)
|
||||
- `backend/requirements.txt` (add Pillow, PyMuPDF)
|
||||
@@ -1,37 +0,0 @@
|
||||
## MODIFIED Requirements
|
||||
|
||||
### Requirement: Dynamic Watermarking
|
||||
系統 SHALL 在下載時自動為檔案加上使用者浮水印。
|
||||
|
||||
#### Scenario: 圖片浮水印
|
||||
- **GIVEN** 使用者下載圖片類型附件 (PNG, JPG, JPEG)
|
||||
- **WHEN** 系統處理下載請求
|
||||
- **THEN** 自動加上包含使用者姓名、工號、下載時間的浮水印
|
||||
- **AND** 浮水印位置不影響主要內容
|
||||
|
||||
#### Scenario: PDF 浮水印
|
||||
- **GIVEN** 使用者下載 PDF 類型附件
|
||||
- **WHEN** 系統處理下載請求
|
||||
- **THEN** 每頁加上浮水印
|
||||
- **AND** 浮水印透明度適中
|
||||
|
||||
#### Scenario: 浮水印內容
|
||||
- **GIVEN** 需要加上浮水印
|
||||
- **WHEN** 系統生成浮水印
|
||||
- **THEN** 浮水印包含:
|
||||
- 使用者姓名
|
||||
- 使用者工號
|
||||
- 下載日期時間
|
||||
- 機密等級標示(如適用)
|
||||
|
||||
#### Scenario: 不支援的檔案類型
|
||||
- **GIVEN** 使用者下載非圖片/PDF 類型附件
|
||||
- **WHEN** 系統處理下載請求
|
||||
- **THEN** 直接提供原始檔案下載
|
||||
- **AND** 不嘗試加上浮水印
|
||||
|
||||
#### Scenario: 浮水印服務異常處理
|
||||
- **GIVEN** 浮水印生成過程發生錯誤
|
||||
- **WHEN** 系統無法完成浮水印處理
|
||||
- **THEN** 記錄錯誤日誌
|
||||
- **AND** 提供原始檔案下載(降級處理)
|
||||
@@ -1,27 +0,0 @@
|
||||
# Tasks: add-watermark-feature
|
||||
|
||||
## Phase 1: Backend - Dependencies & Service
|
||||
|
||||
- [x] 1.1 Add Pillow and PyMuPDF (fitz) to requirements.txt
|
||||
- [x] 1.2 Create WatermarkService class (`backend/app/services/watermark_service.py`)
|
||||
- [x] 1.3 Implement `add_image_watermark(image_path, user, output_path)` method
|
||||
- [x] 1.4 Implement `add_pdf_watermark(pdf_path, user, output_path)` method
|
||||
|
||||
## Phase 2: Backend - Integration
|
||||
|
||||
- [x] 2.1 Modify download endpoint to apply watermark
|
||||
- [x] 2.2 Add watermark configuration (enable/disable per project)
|
||||
- [x] 2.3 Handle unsupported file types gracefully (skip watermark)
|
||||
|
||||
## Phase 3: Backend - Testing
|
||||
|
||||
- [x] 3.1 Unit tests for image watermarking
|
||||
- [x] 3.2 Unit tests for PDF watermarking
|
||||
- [x] 3.3 Integration tests for download with watermark
|
||||
|
||||
## Validation Criteria
|
||||
|
||||
- Downloaded images contain visible watermark with user info
|
||||
- Downloaded PDFs have watermark on each page
|
||||
- Watermark includes: user name, employee ID, download timestamp
|
||||
- Non-image/PDF files download without modification
|
||||
@@ -1,26 +0,0 @@
|
||||
# Change: Enhance Frontend UX with Kanban View and Component Integration
|
||||
|
||||
## Why
|
||||
The current frontend only implements a basic list view for tasks and has several components that are built but not integrated. This limits user productivity and leaves implemented features inaccessible.
|
||||
|
||||
Issues addressed:
|
||||
- HIGH-005: Only list view exists, need Kanban view
|
||||
- HIGH-007: Comments, Attachments, Triggers components exist but aren't integrated
|
||||
- HIGH-008: Task creation lacks assignee selection and time estimation
|
||||
|
||||
## What Changes
|
||||
- Add Kanban board view with drag-and-drop status changes
|
||||
- Add view toggle between List and Kanban views
|
||||
- Integrate TaskAttachments component into task detail
|
||||
- Integrate Comments component into task detail
|
||||
- Add assignee dropdown to task creation/editing
|
||||
- Add due date and time estimate fields to task form
|
||||
- Create task detail modal/drawer component
|
||||
|
||||
## Impact
|
||||
- Affected specs: task-management
|
||||
- Affected code:
|
||||
- `frontend/src/pages/Tasks.tsx` - add view toggle, enhance create modal
|
||||
- `frontend/src/components/KanbanBoard.tsx` - new component
|
||||
- `frontend/src/components/TaskDetailModal.tsx` - new component
|
||||
- `frontend/src/services/api.ts` - add user list endpoint call
|
||||
@@ -1,58 +0,0 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Kanban View
|
||||
The system SHALL provide a Kanban board view for tasks with drag-and-drop status management.
|
||||
|
||||
#### Scenario: View Kanban board
|
||||
- **GIVEN** user is on the Tasks page
|
||||
- **WHEN** user selects Kanban view
|
||||
- **THEN** tasks are displayed in columns grouped by status
|
||||
- **AND** each column header shows the status name and task count
|
||||
|
||||
#### Scenario: Drag task to change status
|
||||
- **GIVEN** user is viewing the Kanban board
|
||||
- **WHEN** user drags a task card to a different status column
|
||||
- **THEN** the task status is updated via API
|
||||
- **AND** the card moves to the new column
|
||||
- **AND** other users viewing the board see the update
|
||||
|
||||
#### Scenario: View toggle persistence
|
||||
- **GIVEN** user switches to Kanban view
|
||||
- **WHEN** user navigates away and returns
|
||||
- **THEN** the Kanban view is still selected
|
||||
|
||||
### Requirement: Task Detail Modal
|
||||
The system SHALL provide a task detail modal with comments and attachments.
|
||||
|
||||
#### Scenario: Open task detail
|
||||
- **GIVEN** user is viewing tasks in any view
|
||||
- **WHEN** user clicks on a task
|
||||
- **THEN** a modal opens showing task details
|
||||
- **AND** the modal includes comments section
|
||||
- **AND** the modal includes attachments section
|
||||
|
||||
#### Scenario: Edit task in modal
|
||||
- **GIVEN** user has task detail modal open
|
||||
- **WHEN** user modifies task fields and saves
|
||||
- **THEN** the task is updated via API
|
||||
- **AND** the task list/board reflects the changes
|
||||
|
||||
### Requirement: Task Assignment UI
|
||||
The system SHALL allow assigning tasks to users during creation and editing.
|
||||
|
||||
#### Scenario: Assign task during creation
|
||||
- **GIVEN** user is creating a new task
|
||||
- **WHEN** user selects an assignee from the dropdown
|
||||
- **THEN** the task is created with the selected assignee
|
||||
|
||||
#### Scenario: Change task assignee
|
||||
- **GIVEN** user has task detail modal open
|
||||
- **WHEN** user changes the assignee
|
||||
- **THEN** the task assignee is updated
|
||||
- **AND** the new assignee receives a notification
|
||||
|
||||
#### Scenario: Set due date and time estimate
|
||||
- **GIVEN** user is creating or editing a task
|
||||
- **WHEN** user sets due date and time estimate
|
||||
- **THEN** the values are saved with the task
|
||||
- **AND** the task appears on the appropriate date in calendar view
|
||||
@@ -1,27 +0,0 @@
|
||||
# Tasks: Enhance Frontend UX
|
||||
|
||||
## 1. Kanban View Implementation
|
||||
- [x] 1.1 Create KanbanBoard.tsx component with column layout
|
||||
- [x] 1.2 Implement drag-and-drop using native HTML5 drag API
|
||||
- [x] 1.3 Call PATCH /tasks/{id}/status on drop
|
||||
- [x] 1.4 Add view toggle (List/Kanban) to Tasks page header
|
||||
- [x] 1.5 Persist view preference in localStorage
|
||||
|
||||
## 2. Task Detail Modal
|
||||
- [x] 2.1 Create TaskDetailModal.tsx component
|
||||
- [x] 2.2 Integrate existing Comments component
|
||||
- [x] 2.3 Integrate existing TaskAttachments component
|
||||
- [x] 2.4 Add task editing capability within modal
|
||||
- [x] 2.5 Wire up modal opening on task row/card click
|
||||
|
||||
## 3. Task Assignment UI
|
||||
- [x] 3.1 Add user search/dropdown component for assignee
|
||||
- [x] 3.2 Integrate assignee field in task create modal
|
||||
- [x] 3.3 Integrate assignee field in task detail modal
|
||||
- [x] 3.4 Add due date picker component
|
||||
- [x] 3.5 Add time estimate input fields
|
||||
|
||||
## 4. Testing
|
||||
- [x] 4.1 Test Kanban drag-and-drop functionality - QA reviewed
|
||||
- [x] 4.2 Verify task assignment updates correctly - QA reviewed
|
||||
- [x] 4.3 Test comments and attachments integration - QA reviewed
|
||||
@@ -1,91 +0,0 @@
|
||||
# Proposal: Add Calendar View
|
||||
|
||||
**Change ID:** `add-calendar-view`
|
||||
**Issue Reference:** `FEAT-004` (issues.md)
|
||||
**Status:** Draft
|
||||
**Author:** Claude
|
||||
**Date:** 2026-01-05
|
||||
|
||||
## Summary
|
||||
|
||||
實作行事曆視角,以月/週/日視圖呈現任務截止日期,方便使用者規劃工作時程。
|
||||
|
||||
## Motivation
|
||||
|
||||
行事曆視角提供直覺的日期導向任務規劃:
|
||||
- 快速掌握本週/本月截止的任務
|
||||
- 識別特定日期的工作量
|
||||
- 避免任務截止日期過度集中
|
||||
- 與個人行事曆習慣一致
|
||||
|
||||
目前系統需要在列表或看板中逐一檢視任務日期,無法一覽全局。
|
||||
|
||||
## Scope
|
||||
|
||||
### In Scope
|
||||
- Frontend: Calendar 元件(月視圖、週視圖、日視圖)
|
||||
- Frontend: 任務依截止日期顯示在行事曆上
|
||||
- Frontend: 點擊任務開啟詳情 Modal
|
||||
- Frontend: 拖拉任務更改截止日期
|
||||
- Frontend: 篩選器(依指派者、狀態、優先級)
|
||||
|
||||
### Out of Scope
|
||||
- 與外部行事曆(Google Calendar、Outlook)同步
|
||||
- 重複任務功能
|
||||
- 行事曆分享
|
||||
|
||||
## Design Decisions
|
||||
|
||||
### 前端函式庫選擇
|
||||
建議使用 **FullCalendar**:
|
||||
|
||||
| 函式庫 | 優點 | 缺點 |
|
||||
|--------|------|------|
|
||||
| FullCalendar | 功能完整、React 支援好、拖拉支援 | 需要 premium 功能要付費 |
|
||||
| react-big-calendar | 開源、輕量 | 功能較少 |
|
||||
| @schedule-x/react | 現代設計 | 社群較小 |
|
||||
|
||||
**建議**: 使用 FullCalendar(MIT 授權核心功能足夠)
|
||||
|
||||
### 視圖模式
|
||||
| 視圖 | 說明 |
|
||||
|------|------|
|
||||
| Month | 月曆格式,每格顯示當日任務(最多 3 個,其餘 "+N more")|
|
||||
| Week | 週視圖,每格顯示當日所有任務 |
|
||||
| Day | 日視圖,當天任務完整列表 |
|
||||
|
||||
### 任務顯示規則
|
||||
- 依 due_date 顯示在對應日期
|
||||
- 任務卡片顯示:標題、狀態顏色標記、優先級圖示
|
||||
- 已完成任務可選顯示/隱藏
|
||||
- 已過期未完成任務特殊標記(紅色邊框)
|
||||
|
||||
### 拖拉更新日期
|
||||
- 拖拉任務至其他日期更新 due_date
|
||||
- 使用現有 PUT /api/tasks/{id} API
|
||||
- 樂觀更新,失敗時回滾
|
||||
|
||||
### API 使用
|
||||
```
|
||||
# 使用現有 API,加入日期範圍篩選
|
||||
GET /api/projects/{project_id}/tasks?due_after=2026-01-01&due_before=2026-01-31
|
||||
```
|
||||
|
||||
## Affected Specs
|
||||
|
||||
| Spec | Change Type |
|
||||
|------|-------------|
|
||||
| task-management | MODIFIED (Multiple Views requirement 行事曆細節) |
|
||||
|
||||
## Risks & Mitigations
|
||||
|
||||
| Risk | Mitigation |
|
||||
|------|------------|
|
||||
| 單日任務過多影響顯示 | 月視圖最多顯示 3 個,其餘收合 |
|
||||
| 效能問題 | 只載入當前視圖月份的任務 |
|
||||
| 與甘特圖日期衝突 | 行事曆編輯 due_date,甘特圖可編輯 start_date + due_date |
|
||||
|
||||
## Dependencies
|
||||
|
||||
- FullCalendar(或其他行事曆函式庫)
|
||||
- 現有 Task API 的 due_date 篩選支援
|
||||
@@ -1,51 +0,0 @@
|
||||
## MODIFIED Requirements
|
||||
|
||||
### Requirement: Multiple Views
|
||||
系統 SHALL 支援多維視角:看板 (Kanban)、甘特圖 (Gantt)、列表 (List)、行事曆 (Calendar)。
|
||||
|
||||
#### Scenario: 行事曆視角
|
||||
- **GIVEN** 使用者選擇行事曆視角
|
||||
- **WHEN** 系統載入專案任務
|
||||
- **THEN** 任務依截止日期顯示在行事曆上
|
||||
|
||||
#### Scenario: 行事曆視圖切換
|
||||
- **GIVEN** 使用者正在查看行事曆
|
||||
- **WHEN** 使用者切換視圖模式(月、週、日)
|
||||
- **THEN** 行事曆相應調整顯示格式
|
||||
- **AND** 任務正確顯示在對應日期
|
||||
|
||||
#### Scenario: 月視圖任務顯示
|
||||
- **GIVEN** 使用者選擇月視圖
|
||||
- **WHEN** 某日有超過 3 個任務
|
||||
- **THEN** 顯示前 3 個任務
|
||||
- **AND** 顯示 "+N more" 連結可展開查看全部
|
||||
|
||||
#### Scenario: 點擊任務查看詳情
|
||||
- **GIVEN** 使用者正在查看行事曆
|
||||
- **WHEN** 使用者點擊任務
|
||||
- **THEN** 系統開啟任務詳情 Modal
|
||||
- **AND** 可在 Modal 中編輯任務
|
||||
|
||||
#### Scenario: 拖拉調整截止日期
|
||||
- **GIVEN** 使用者正在查看行事曆
|
||||
- **WHEN** 使用者拖拉任務至其他日期
|
||||
- **THEN** 系統更新任務的 due_date
|
||||
- **AND** 任務顯示在新日期
|
||||
|
||||
#### Scenario: 已過期任務標示
|
||||
- **GIVEN** 任務的 due_date 已過期
|
||||
- **WHEN** 任務狀態不是「已完成」
|
||||
- **THEN** 任務顯示特殊標記(紅色邊框或背景)
|
||||
- **AND** 提醒使用者注意
|
||||
|
||||
#### Scenario: 日期範圍載入
|
||||
- **GIVEN** 使用者查看行事曆
|
||||
- **WHEN** 系統載入任務
|
||||
- **THEN** 只載入當前視圖日期範圍內的任務
|
||||
- **AND** 切換月份時動態載入對應任務
|
||||
|
||||
#### Scenario: 行事曆篩選
|
||||
- **GIVEN** 使用者正在查看行事曆
|
||||
- **WHEN** 使用者設定篩選條件(指派者、狀態、優先級)
|
||||
- **THEN** 行事曆只顯示符合條件的任務
|
||||
- **AND** 篩選條件在視圖切換時保留
|
||||
@@ -1,80 +0,0 @@
|
||||
# Tasks: Add Calendar View
|
||||
|
||||
## Backend Tasks
|
||||
|
||||
### 1. Add date range filter to Tasks API
|
||||
- [x] Modify GET /api/projects/{project_id}/tasks to accept `due_after` and `due_before` query params
|
||||
- [x] Add filter logic to query
|
||||
- [x] Add API tests
|
||||
- **驗證**: 可依日期範圍篩選任務 ✅
|
||||
|
||||
## Frontend Tasks
|
||||
|
||||
### 2. Install and configure FullCalendar
|
||||
- [x] Install @fullcalendar/react and related packages
|
||||
- [x] Install @fullcalendar/daygrid (month view)
|
||||
- [x] Install @fullcalendar/timegrid (week/day view)
|
||||
- [x] Install @fullcalendar/interaction (drag & drop)
|
||||
- **驗證**: 套件安裝成功 ✅
|
||||
|
||||
### 3. Create CalendarView component
|
||||
- [x] Create `frontend/src/components/CalendarView.tsx`
|
||||
- [x] Configure calendar with month/week/day views
|
||||
- [x] Style calendar to match application theme
|
||||
- [x] Add view toggle buttons
|
||||
- **驗證**: 行事曆正確渲染 ✅
|
||||
|
||||
### 4. Load and display tasks on calendar
|
||||
- [x] Fetch tasks from API with date range
|
||||
- [x] Transform tasks to FullCalendar event format
|
||||
- [x] Display task title, status color, priority icon
|
||||
- [x] Handle overdue tasks (special styling)
|
||||
- **驗證**: 任務正確顯示在對應日期 ✅
|
||||
|
||||
### 5. Implement task click to open detail modal
|
||||
- [x] Handle event click in calendar
|
||||
- [x] Open TaskDetailModal with selected task
|
||||
- [x] Refresh calendar on task update
|
||||
- **驗證**: 點擊任務開啟詳情 ✅
|
||||
|
||||
### 6. Implement drag-to-change date
|
||||
- [x] Enable drag & drop in FullCalendar
|
||||
- [x] Handle event drop to get new date
|
||||
- [x] Call PUT /api/tasks/{id} with new due_date
|
||||
- [x] Show optimistic update, rollback on error
|
||||
- **驗證**: 拖拉更新日期可正常運作 ✅
|
||||
|
||||
### 7. Add filter controls
|
||||
- [x] Add assignee filter dropdown
|
||||
- [x] Add status filter (show/hide completed)
|
||||
- [x] Add priority filter
|
||||
- [x] Persist filter in URL or localStorage
|
||||
- **驗證**: 篩選器正確過濾任務 ✅
|
||||
|
||||
### 8. Integrate Calendar view into Tasks page
|
||||
- [x] Add "Calendar" option to view toggle
|
||||
- [x] Store view preference in localStorage
|
||||
- [x] Handle view switching
|
||||
- **驗證**: 可在 List/Kanban/Gantt/Calendar 之間切換 ✅
|
||||
|
||||
## Task Dependencies
|
||||
|
||||
```
|
||||
[1] Date Range Filter API
|
||||
↓
|
||||
[2] FullCalendar Setup → [3] CalendarView Component
|
||||
↓
|
||||
[4] Task Display
|
||||
↓
|
||||
[5] Click → Modal
|
||||
↓
|
||||
[6] Drag to Change Date
|
||||
↓
|
||||
[7] Filters
|
||||
↓
|
||||
[8] View Integration
|
||||
```
|
||||
|
||||
- Task 1 (Backend) 可平行於 Tasks 2-3 (Frontend) 開發
|
||||
- Task 4 需要 Task 1 完成(需要日期範圍 API)
|
||||
- Tasks 5-8 為循序開發
|
||||
@@ -1,83 +0,0 @@
|
||||
# Proposal: Add Custom Fields
|
||||
|
||||
**Change ID:** `add-custom-fields`
|
||||
**Issue Reference:** `FEAT-001` (issues.md)
|
||||
**Status:** Draft
|
||||
**Author:** Claude
|
||||
**Date:** 2026-01-05
|
||||
|
||||
## Summary
|
||||
|
||||
實作自定義欄位功能,允許專案管理者為任務新增自定義欄位,包含文字、數字、下拉選單、日期、人員標籤和公式等類型。
|
||||
|
||||
## Motivation
|
||||
|
||||
目前系統僅支援固定的任務欄位(標題、描述、狀態、優先級等)。不同專案可能需要追蹤特定資料,例如:
|
||||
- 半導體製程:封裝類型、機台編號、預計良率
|
||||
- 軟體開發:Sprint 編號、Story Points、影響版本
|
||||
- 行銷活動:活動代碼、預算類別、目標受眾
|
||||
|
||||
自定義欄位讓專案管理者可以根據需求彈性擴展任務資料結構。
|
||||
|
||||
## Scope
|
||||
|
||||
### In Scope
|
||||
- Backend: CustomField 和 TaskCustomValue models
|
||||
- Backend: Custom fields CRUD API endpoints
|
||||
- Backend: Formula field 計算引擎
|
||||
- Frontend: Custom fields 管理 UI(專案設定頁面)
|
||||
- Frontend: Task form 動態渲染自定義欄位
|
||||
- Frontend: Task list/kanban 顯示自定義欄位值
|
||||
|
||||
### Out of Scope
|
||||
- 跨專案共用欄位定義
|
||||
- 自定義欄位匯入/匯出
|
||||
- 複雜公式函數(僅支援基本數學運算)
|
||||
|
||||
## Design Decisions
|
||||
|
||||
### 欄位類型
|
||||
| Type | 說明 | 儲存格式 |
|
||||
|------|------|----------|
|
||||
| text | 單行文字 | VARCHAR |
|
||||
| number | 數字 | DECIMAL |
|
||||
| dropdown | 下拉選單 | VARCHAR (選項儲存於 options JSON) |
|
||||
| date | 日期 | DATE |
|
||||
| person | 人員標籤 | UUID (FK -> users) |
|
||||
| formula | 公式計算 | DECIMAL (計算結果) |
|
||||
|
||||
### 公式欄位
|
||||
- 支援基本數學運算:`+`, `-`, `*`, `/`
|
||||
- 可引用其他數字欄位:`{field_name}`
|
||||
- 可引用任務內建欄位:`{original_estimate}`, `{time_spent}`
|
||||
- 範例公式:`{time_spent} / {original_estimate} * 100`
|
||||
|
||||
### API 設計
|
||||
```
|
||||
POST /api/projects/{project_id}/custom-fields # 新增欄位定義
|
||||
GET /api/projects/{project_id}/custom-fields # 列出所有欄位
|
||||
PUT /api/custom-fields/{field_id} # 更新欄位定義
|
||||
DELETE /api/custom-fields/{field_id} # 刪除欄位(含所有值)
|
||||
|
||||
# 欄位值透過現有 task API 操作
|
||||
PUT /api/tasks/{task_id} # body 包含 custom_values
|
||||
```
|
||||
|
||||
## Affected Specs
|
||||
|
||||
| Spec | Change Type |
|
||||
|------|-------------|
|
||||
| task-management | MODIFIED (Custom Fields requirement 實作細節) |
|
||||
|
||||
## Risks & Mitigations
|
||||
|
||||
| Risk | Mitigation |
|
||||
|------|------------|
|
||||
| 公式欄位循環引用 | 建立欄位時驗證公式不引用自己或形成循環 |
|
||||
| 大量自定義欄位影響效能 | 限制每專案最多 20 個自定義欄位 |
|
||||
| 刪除欄位遺失資料 | 刪除前顯示確認對話框,說明將刪除所有相關值 |
|
||||
|
||||
## Dependencies
|
||||
|
||||
- 無外部依賴
|
||||
- 需要現有 tasks API 支援
|
||||
@@ -1,54 +0,0 @@
|
||||
## MODIFIED Requirements
|
||||
|
||||
### Requirement: Custom Fields
|
||||
系統 SHALL 支援自定義欄位,包含下拉選單、公式、人員標籤等類型。
|
||||
|
||||
#### Scenario: 新增自定義欄位
|
||||
- **GIVEN** 專案管理者需要追蹤特定資料(如:封裝類型、機台編號、預計良率)
|
||||
- **WHEN** 管理者在專案中新增自定義欄位
|
||||
- **THEN** 系統建立欄位定義並套用至該專案所有任務
|
||||
|
||||
#### Scenario: 編輯自定義欄位
|
||||
- **GIVEN** 專案已有自定義欄位
|
||||
- **WHEN** 管理者修改欄位名稱或選項
|
||||
- **THEN** 系統更新欄位定義
|
||||
- **AND** 現有任務的欄位值保持不變
|
||||
|
||||
#### Scenario: 刪除自定義欄位
|
||||
- **GIVEN** 專案已有自定義欄位且有任務包含該欄位的值
|
||||
- **WHEN** 管理者刪除該欄位
|
||||
- **THEN** 系統顯示確認對話框說明將刪除所有相關值
|
||||
- **AND** 確認後刪除欄位定義及所有任務的該欄位值
|
||||
|
||||
#### Scenario: 公式欄位計算
|
||||
- **GIVEN** 任務包含公式類型的自定義欄位
|
||||
- **WHEN** 相依欄位的值發生變更
|
||||
- **THEN** 系統自動重新計算公式欄位的值
|
||||
|
||||
#### Scenario: 公式欄位循環引用檢查
|
||||
- **GIVEN** 管理者建立公式欄位
|
||||
- **WHEN** 公式引用自己或形成循環引用
|
||||
- **THEN** 系統拒絕建立並顯示錯誤訊息
|
||||
|
||||
#### Scenario: 人員標籤欄位
|
||||
- **GIVEN** 任務包含人員標籤類型的自定義欄位
|
||||
- **WHEN** 使用者選擇人員
|
||||
- **THEN** 系統驗證人員存在並建立關聯
|
||||
- **AND** 被標籤的人員可收到相關通知
|
||||
|
||||
#### Scenario: 下拉選單欄位
|
||||
- **GIVEN** 任務包含下拉選單類型的自定義欄位
|
||||
- **WHEN** 使用者選擇選項
|
||||
- **THEN** 系統儲存選擇的值
|
||||
- **AND** 選項列表由欄位定義提供
|
||||
|
||||
#### Scenario: 自定義欄位值顯示
|
||||
- **GIVEN** 任務有自定義欄位值
|
||||
- **WHEN** 使用者在列表或看板視角查看任務
|
||||
- **THEN** 自定義欄位值顯示在任務資訊中
|
||||
- **AND** 公式欄位顯示計算結果(唯讀)
|
||||
|
||||
#### Scenario: 欄位數量限制
|
||||
- **GIVEN** 專案已有 20 個自定義欄位
|
||||
- **WHEN** 管理者嘗試新增第 21 個欄位
|
||||
- **THEN** 系統拒絕新增並顯示數量已達上限的訊息
|
||||
@@ -1,82 +0,0 @@
|
||||
# Tasks: Add Custom Fields
|
||||
|
||||
## Backend Tasks
|
||||
|
||||
### 1. Create CustomField and TaskCustomValue models
|
||||
- [x] Create `backend/app/models/custom_field.py` with CustomField model
|
||||
- [x] Create `backend/app/models/task_custom_value.py` with TaskCustomValue model
|
||||
- [x] Update `backend/app/models/__init__.py` to export new models
|
||||
- [x] Create Alembic migration for new tables
|
||||
- **驗證**: Migration 成功執行,tables 建立正確
|
||||
|
||||
### 2. Create Custom Fields API endpoints
|
||||
- [x] Create `backend/app/api/custom_fields/router.py`
|
||||
- [x] Implement `POST /api/projects/{project_id}/custom-fields` - 新增欄位
|
||||
- [x] Implement `GET /api/projects/{project_id}/custom-fields` - 列出欄位
|
||||
- [x] Implement `PUT /api/custom-fields/{field_id}` - 更新欄位
|
||||
- [x] Implement `DELETE /api/custom-fields/{field_id}` - 刪除欄位
|
||||
- [x] Add permission checks (only project owner/manager can manage fields)
|
||||
- [x] Register router in main.py
|
||||
- **驗證**: API endpoints 可正常呼叫,權限檢查正確
|
||||
|
||||
### 3. Implement formula calculation engine
|
||||
- [x] Create `backend/app/services/formula_service.py`
|
||||
- [x] Implement formula parsing (extract field references)
|
||||
- [x] Implement formula validation (detect circular references)
|
||||
- [x] Implement formula calculation
|
||||
- [x] Add unit tests for formula service
|
||||
- **驗證**: 公式計算正確,循環引用被拒絕
|
||||
|
||||
### 4. Integrate custom values with Tasks API
|
||||
- [x] Modify `TaskCreate` schema to accept `custom_values`
|
||||
- [x] Modify `TaskUpdate` schema to accept `custom_values`
|
||||
- [x] Modify `TaskResponse` schema to include `custom_values`
|
||||
- [x] Update `create_task` endpoint to save custom values
|
||||
- [x] Update `update_task` endpoint to save custom values
|
||||
- [x] Update `get_task` endpoint to return custom values
|
||||
- [x] Trigger formula recalculation on value change
|
||||
- **驗證**: 任務 CRUD 包含自定義欄位值
|
||||
|
||||
### 5. Add backend tests
|
||||
- [x] Test custom field CRUD operations
|
||||
- [x] Test permission checks
|
||||
- [x] Test formula calculation
|
||||
- [x] Test task integration with custom values
|
||||
- **驗證**: 所有測試通過 (20/20 tests passed)
|
||||
|
||||
## Frontend Tasks
|
||||
|
||||
### 6. Create Custom Fields management UI
|
||||
- [x] Create `frontend/src/pages/ProjectSettings.tsx` or extend existing
|
||||
- [x] Create `frontend/src/components/CustomFieldEditor.tsx` - 欄位編輯表單
|
||||
- [x] Create `frontend/src/components/CustomFieldList.tsx` - 欄位列表
|
||||
- [x] Add custom fields API service in `frontend/src/services/customFields.ts`
|
||||
- **驗證**: 可在專案設定中管理自定義欄位
|
||||
|
||||
### 7. Dynamic field rendering in Task forms
|
||||
- [x] Create `frontend/src/components/CustomFieldInput.tsx` - 動態欄位輸入
|
||||
- [x] Integrate into TaskDetailModal.tsx
|
||||
- [x] Integrate into task creation form
|
||||
- [x] Handle different field types (text, number, dropdown, date, person)
|
||||
- [x] Display formula fields as read-only
|
||||
- **驗證**: 任務表單正確顯示和儲存自定義欄位
|
||||
|
||||
### 8. Display custom fields in task views
|
||||
- [x] Add custom field columns to List view
|
||||
- [x] Display custom field values in Kanban cards (optional, configurable)
|
||||
- [x] Add column visibility toggle for custom fields
|
||||
- **驗證**: 自定義欄位值在各視角中正確顯示
|
||||
|
||||
## Task Dependencies
|
||||
|
||||
```
|
||||
[1] Models -> [2] API -> [4] Task Integration -> [5] Tests
|
||||
\
|
||||
[3] Formula Engine /
|
||||
|
||||
[6] Management UI -> [7] Task Forms -> [8] View Display
|
||||
```
|
||||
|
||||
- Tasks 1-5 (Backend) 可平行於 Tasks 6-8 (Frontend) 開發
|
||||
- Task 2 完成後 Task 6 可開始(需要 API)
|
||||
- Task 4 完成後 Task 7 可開始(需要 Task API 支援 custom values)
|
||||
@@ -1,110 +0,0 @@
|
||||
# Proposal: Add File Encryption
|
||||
|
||||
**Change ID:** `add-file-encryption`
|
||||
**Issue Reference:** `FEAT-010` (issues.md)
|
||||
**Status:** Draft
|
||||
**Author:** Claude
|
||||
**Date:** 2026-01-05
|
||||
|
||||
## Summary
|
||||
|
||||
實作 AES-256 加密存儲功能,對機密等級專案的附件進行加密保護,並提供金鑰管理與輪換機制。
|
||||
|
||||
## Motivation
|
||||
|
||||
半導體產業的設計文件、製程參數和良率報告屬於高度機密資料。目前系統的附件以明文形式儲存於 NAS,若發生以下情況可能導致資料外洩:
|
||||
- NAS 遭未授權存取
|
||||
- 備份媒體遺失
|
||||
- 儲存設備報廢未妥善處理
|
||||
|
||||
透過 AES-256 加密,即使檔案被取得也無法解讀內容,提供額外的安全層。
|
||||
|
||||
## Scope
|
||||
|
||||
### In Scope
|
||||
- Backend: EncryptionKey model 和 key 管理 API
|
||||
- Backend: 加密服務(encrypt/decrypt)
|
||||
- Backend: 附件上傳時自動加密(依專案機密等級)
|
||||
- Backend: 附件下載時自動解密
|
||||
- Backend: 金鑰輪換功能
|
||||
- 稽核日誌:加密相關操作記錄
|
||||
|
||||
### Out of Scope
|
||||
- 客戶端加密(End-to-End Encryption)
|
||||
- Hardware Security Module (HSM) 整合
|
||||
- 現有檔案的批次加密遷移(需另案處理)
|
||||
|
||||
## Design Decisions
|
||||
|
||||
### 加密策略
|
||||
| 專案機密等級 | 加密行為 |
|
||||
|-------------|---------|
|
||||
| public | 不加密 |
|
||||
| department | 不加密 |
|
||||
| confidential | 強制加密 |
|
||||
|
||||
### 金鑰管理
|
||||
- 採用對稱金鑰加密(AES-256-GCM)
|
||||
- 每個金鑰有唯一 ID,儲存於資料庫
|
||||
- 金鑰本身使用 Master Key 加密後存儲
|
||||
- Master Key 從環境變數讀取,不存於資料庫
|
||||
|
||||
### 加密流程
|
||||
```
|
||||
1. 上傳檔案
|
||||
2. 檢查專案機密等級
|
||||
3. 若為 confidential:
|
||||
a. 取得當前有效金鑰
|
||||
b. 使用 AES-256-GCM 加密檔案
|
||||
c. 儲存加密後的檔案
|
||||
d. 記錄 encryption_key_id
|
||||
4. 儲存 Attachment 記錄
|
||||
```
|
||||
|
||||
### 解密流程
|
||||
```
|
||||
1. 請求下載檔案
|
||||
2. 驗證存取權限
|
||||
3. 若 is_encrypted = true:
|
||||
a. 取得 encryption_key_id 對應的金鑰
|
||||
b. 解密檔案
|
||||
c. 返回解密後的內容
|
||||
4. 若 is_encrypted = false:
|
||||
a. 直接返回檔案
|
||||
```
|
||||
|
||||
### 金鑰輪換
|
||||
- 建立新金鑰並標記為 active
|
||||
- 舊金鑰標記為 inactive(但保留用於解密舊檔案)
|
||||
- 新上傳檔案使用新金鑰
|
||||
- 可選:批次重新加密舊檔案(背景任務)
|
||||
|
||||
### API 設計
|
||||
```
|
||||
# 金鑰管理 (Admin only)
|
||||
GET /api/admin/encryption-keys # 列出所有金鑰
|
||||
POST /api/admin/encryption-keys # 建立新金鑰
|
||||
POST /api/admin/encryption-keys/rotate # 金鑰輪換
|
||||
DELETE /api/admin/encryption-keys/{id} # 停用金鑰(不刪除)
|
||||
|
||||
# 附件加密狀態由系統自動處理,無需額外 API
|
||||
```
|
||||
|
||||
## Affected Specs
|
||||
|
||||
| Spec | Change Type |
|
||||
|------|-------------|
|
||||
| document-management | MODIFIED (File Encryption requirement 實作細節) |
|
||||
|
||||
## Risks & Mitigations
|
||||
|
||||
| Risk | Mitigation |
|
||||
|------|------------|
|
||||
| Master Key 遺失導致資料無法解密 | 提供 Master Key 備份指引,建議異地保存 |
|
||||
| 加密效能影響 | 使用串流加密避免大檔案記憶體問題 |
|
||||
| 金鑰輪換中斷 | 使用資料庫交易確保原子性 |
|
||||
|
||||
## Dependencies
|
||||
|
||||
- Python cryptography 套件
|
||||
- Master Key 環境變數配置
|
||||
@@ -1,52 +0,0 @@
|
||||
## MODIFIED Requirements
|
||||
|
||||
### Requirement: File Encryption
|
||||
系統 SHALL 對半導體敏感圖檔進行 AES-256 加密存儲。
|
||||
|
||||
#### Scenario: 自動加密判斷
|
||||
- **GIVEN** 使用者上傳檔案至任務
|
||||
- **WHEN** 該任務所屬專案的 security_level 為 "confidential"
|
||||
- **THEN** 系統自動使用 AES-256-GCM 加密檔案
|
||||
- **AND** 設定 is_encrypted = true 及 encryption_key_id
|
||||
|
||||
#### Scenario: 加密存儲
|
||||
- **GIVEN** 專案設定為機密等級
|
||||
- **WHEN** 使用者上傳檔案
|
||||
- **THEN** 系統使用 AES-256 加密後存儲
|
||||
- **AND** 加密金鑰安全管理
|
||||
|
||||
#### Scenario: 解密讀取
|
||||
- **GIVEN** 使用者請求下載加密檔案
|
||||
- **WHEN** 系統驗證權限通過
|
||||
- **THEN** 系統解密檔案後提供下載
|
||||
- **AND** 解密過程透明,使用者無感
|
||||
|
||||
#### Scenario: 串流處理大檔案
|
||||
- **GIVEN** 使用者上傳或下載大型加密檔案
|
||||
- **WHEN** 系統處理加密或解密
|
||||
- **THEN** 使用串流方式處理避免記憶體溢出
|
||||
- **AND** 效能損耗在可接受範圍內
|
||||
|
||||
#### Scenario: 金鑰輪換
|
||||
- **GIVEN** 安全政策要求金鑰輪換
|
||||
- **WHEN** 管理員執行金鑰輪換
|
||||
- **THEN** 系統建立新金鑰並標記為 active
|
||||
- **AND** 舊金鑰保留用於解密既有檔案
|
||||
- **AND** 新上傳檔案使用新金鑰加密
|
||||
|
||||
#### Scenario: Master Key 管理
|
||||
- **GIVEN** 系統需要加解密檔案
|
||||
- **WHEN** 系統取得加密金鑰
|
||||
- **THEN** 使用 Master Key 解密金鑰後使用
|
||||
- **AND** Master Key 從環境變數讀取,不存於資料庫
|
||||
|
||||
#### Scenario: 加密操作稽核
|
||||
- **GIVEN** 發生加密相關操作
|
||||
- **WHEN** 操作完成
|
||||
- **THEN** 系統記錄操作類型、金鑰 ID、檔案 ID、操作者、時間
|
||||
- **AND** 日誌不可竄改
|
||||
|
||||
#### Scenario: 金鑰管理權限
|
||||
- **GIVEN** 使用者嘗試管理加密金鑰
|
||||
- **WHEN** 使用者不是系統管理員
|
||||
- **THEN** 系統拒絕操作並返回 403 錯誤
|
||||
@@ -1,89 +0,0 @@
|
||||
# Tasks: Add File Encryption
|
||||
|
||||
## Backend Tasks
|
||||
|
||||
### 1. Create EncryptionKey model
|
||||
- [x] Create `backend/app/models/encryption_key.py`
|
||||
- [x] Update `backend/app/models/__init__.py`
|
||||
- [x] Add `encryption_key_id` FK to Attachment model
|
||||
- [x] Create Alembic migration
|
||||
- **驗證**: Migration 成功執行
|
||||
|
||||
### 2. Implement encryption service
|
||||
- [x] Create `backend/app/services/encryption_service.py`
|
||||
- [x] Add `MASTER_KEY` to config.py (from env var)
|
||||
- [x] Implement `generate_key()` - 產生新的 AES-256 金鑰
|
||||
- [x] Implement `encrypt_key()` - 使用 Master Key 加密金鑰
|
||||
- [x] Implement `decrypt_key()` - 使用 Master Key 解密金鑰
|
||||
- [x] Implement `encrypt_file()` - 串流式檔案加密 (AES-256-GCM)
|
||||
- [x] Implement `decrypt_file()` - 串流式檔案解密
|
||||
- [x] Add unit tests for encryption service
|
||||
- **驗證**: 加密解密測試通過
|
||||
|
||||
### 3. Create encryption key management API
|
||||
- [x] Create `backend/app/api/admin/encryption_keys.py`
|
||||
- [x] Implement `GET /api/admin/encryption-keys` - 列出金鑰(不含實際金鑰值)
|
||||
- [x] Implement `POST /api/admin/encryption-keys` - 建立新金鑰
|
||||
- [x] Implement `POST /api/admin/encryption-keys/rotate` - 金鑰輪換
|
||||
- [x] Add system admin only permission check
|
||||
- [x] Register router in main.py
|
||||
- **驗證**: API 可正常呼叫
|
||||
|
||||
### 4. Integrate encryption with attachment upload
|
||||
- [x] Modify `backend/app/api/attachments/router.py` upload endpoint
|
||||
- [x] Check project security_level before upload
|
||||
- [x] If confidential: encrypt file using encryption service
|
||||
- [x] Set is_encrypted = True and encryption_key_id
|
||||
- [x] Store encrypted file to NAS
|
||||
- **驗證**: 機密專案上傳的檔案為加密狀態
|
||||
|
||||
### 5. Integrate decryption with attachment download
|
||||
- [x] Modify `backend/app/api/attachments/router.py` download endpoint
|
||||
- [x] Check is_encrypted flag
|
||||
- [x] If encrypted: decrypt using encryption service before returning
|
||||
- [x] Maintain streaming for large files
|
||||
- **驗證**: 下載加密檔案可正確解密
|
||||
|
||||
### 6. Add encryption audit logging
|
||||
- [x] Log encryption operations (encrypt, decrypt, key_create, key_rotate)
|
||||
- [x] Include key_id, file_id, user_id, timestamp
|
||||
- **驗證**: 稽核日誌正確記錄加密操作
|
||||
|
||||
### 7. Add backend tests
|
||||
- [x] Test encryption service (encrypt/decrypt)
|
||||
- [x] Test key management API
|
||||
- [x] Test attachment upload with encryption
|
||||
- [x] Test attachment download with decryption
|
||||
- [x] Test key rotation
|
||||
- **驗證**: 所有測試通過
|
||||
|
||||
## Configuration Tasks
|
||||
|
||||
### 8. Environment configuration
|
||||
- [x] Add `MASTER_KEY` to .env.example
|
||||
- [x] Document key generation procedure
|
||||
- [x] Document key backup recommendations
|
||||
- **驗證**: 文件完整
|
||||
|
||||
## Task Dependencies
|
||||
|
||||
```
|
||||
[1] EncryptionKey Model
|
||||
↓
|
||||
[2] Encryption Service
|
||||
↓
|
||||
[3] Key Management API ─────┐
|
||||
↓ │
|
||||
[4] Upload Integration │
|
||||
↓ │
|
||||
[5] Download Integration │
|
||||
↓ │
|
||||
[6] Audit Logging │
|
||||
↓ │
|
||||
[7] Tests ←─────────────────┘
|
||||
↓
|
||||
[8] Configuration
|
||||
```
|
||||
|
||||
- Tasks 1-7 為循序依賴
|
||||
- Task 8 可平行進行
|
||||
@@ -1,111 +0,0 @@
|
||||
# Proposal: Add Gantt View
|
||||
|
||||
**Change ID:** `add-gantt-view`
|
||||
**Issue Reference:** `FEAT-003` (issues.md)
|
||||
**Status:** Draft
|
||||
**Author:** Claude
|
||||
**Date:** 2026-01-05
|
||||
|
||||
## Summary
|
||||
|
||||
實作甘特圖視角,以時間軸方式呈現專案任務,顯示任務期間、依賴關係和里程碑。
|
||||
|
||||
## Motivation
|
||||
|
||||
甘特圖是專案管理的核心視覺化工具,可幫助團隊:
|
||||
- 了解任務時程與排程
|
||||
- 識別關鍵路徑
|
||||
- 發現資源衝突和瓶頸
|
||||
- 追蹤進度與延遲
|
||||
|
||||
目前系統僅有看板和列表視角,缺少時間軸視圖,無法直觀地規劃和追蹤專案進度。
|
||||
|
||||
## Scope
|
||||
|
||||
### In Scope
|
||||
- Backend: Task model 新增 start_date 欄位
|
||||
- Backend: TaskDependency model(前置任務關係)
|
||||
- Backend: 依賴關係 CRUD API
|
||||
- Frontend: Gantt chart 元件(使用第三方函式庫)
|
||||
- Frontend: 任務時程編輯(拖拉調整日期)
|
||||
- Frontend: 依賴關係視覺化(箭頭連線)
|
||||
|
||||
### Out of Scope
|
||||
- 關鍵路徑計算
|
||||
- 資源分配視圖
|
||||
- 基線對比功能
|
||||
- 匯出為圖片/PDF
|
||||
|
||||
## Design Decisions
|
||||
|
||||
### 任務依賴類型
|
||||
| Type | 說明 | 範例 |
|
||||
|------|------|------|
|
||||
| finish_to_start (FS) | 前置任務完成後才能開始 | 設計完成 → 開發開始 |
|
||||
| start_to_start (SS) | 前置任務開始後才能開始 | 設計開始 → 文件開始 |
|
||||
| finish_to_finish (FF) | 前置任務完成後才能完成 | 開發完成 → 測試完成 |
|
||||
| start_to_finish (SF) | 前置任務開始後才能完成 | 較少使用 |
|
||||
|
||||
**預設**: finish_to_start (FS) 為最常見類型
|
||||
|
||||
### Data Model 變更
|
||||
```sql
|
||||
-- Task 新增欄位
|
||||
ALTER TABLE pjctrl_tasks ADD COLUMN start_date DATETIME;
|
||||
|
||||
-- 新增依賴關係表
|
||||
CREATE TABLE pjctrl_task_dependencies (
|
||||
id UUID PRIMARY KEY,
|
||||
predecessor_id UUID REFERENCES pjctrl_tasks(id),
|
||||
successor_id UUID REFERENCES pjctrl_tasks(id),
|
||||
dependency_type ENUM('FS', 'SS', 'FF', 'SF') DEFAULT 'FS',
|
||||
lag_days INT DEFAULT 0,
|
||||
created_at TIMESTAMP
|
||||
);
|
||||
```
|
||||
|
||||
### 前端函式庫選擇
|
||||
建議使用 **DHTMLX Gantt** 或 **Frappe Gantt**:
|
||||
|
||||
| 函式庫 | 優點 | 缺點 |
|
||||
|--------|------|------|
|
||||
| DHTMLX Gantt | 功能完整、效能好 | 商業授權 |
|
||||
| Frappe Gantt | 開源、輕量 | 功能較少 |
|
||||
| React-Gantt-Chart | React 原生 | 社群較小 |
|
||||
|
||||
**建議**: 使用 Frappe Gantt(MIT 授權),足夠基本需求
|
||||
|
||||
### API 設計
|
||||
```
|
||||
# 依賴關係 API
|
||||
POST /api/tasks/{task_id}/dependencies # 新增依賴
|
||||
GET /api/tasks/{task_id}/dependencies # 取得依賴
|
||||
DELETE /api/task-dependencies/{dependency_id} # 刪除依賴
|
||||
|
||||
# 任務日期更新(使用現有 API)
|
||||
PUT /api/tasks/{task_id} # body 包含 start_date, due_date
|
||||
```
|
||||
|
||||
### 日期驗證規則
|
||||
- start_date 不可晚於 due_date
|
||||
- 有依賴關係時,根據 dependency_type 驗證日期合理性
|
||||
- 循環依賴檢測
|
||||
|
||||
## Affected Specs
|
||||
|
||||
| Spec | Change Type |
|
||||
|------|-------------|
|
||||
| task-management | MODIFIED (Multiple Views requirement 甘特圖細節) |
|
||||
|
||||
## Risks & Mitigations
|
||||
|
||||
| Risk | Mitigation |
|
||||
|------|------------|
|
||||
| 複雜依賴關係影響效能 | 限制單任務最多 10 個直接依賴 |
|
||||
| 循環依賴 | 新增/修改依賴時進行循環檢測 |
|
||||
| 大型專案載入慢 | 分頁載入或虛擬滾動 |
|
||||
|
||||
## Dependencies
|
||||
|
||||
- Frappe Gantt(或其他甘特圖函式庫)
|
||||
- 需要 Task model 已有 due_date 欄位(✓ 已存在)
|
||||
@@ -1,67 +0,0 @@
|
||||
## MODIFIED Requirements
|
||||
|
||||
### Requirement: Multiple Views
|
||||
系統 SHALL 支援多維視角:看板 (Kanban)、甘特圖 (Gantt)、列表 (List)、行事曆 (Calendar)。
|
||||
|
||||
#### Scenario: 甘特圖視角
|
||||
- **GIVEN** 使用者選擇甘特圖視角
|
||||
- **WHEN** 系統載入專案任務
|
||||
- **THEN** 任務依時間軸顯示為水平條狀
|
||||
- **AND** 顯示任務相依關係與里程碑
|
||||
|
||||
#### Scenario: 甘特圖時間軸縮放
|
||||
- **GIVEN** 使用者正在查看甘特圖
|
||||
- **WHEN** 使用者切換縮放層級(日、週、月)
|
||||
- **THEN** 時間軸相應調整顯示密度
|
||||
- **AND** 任務條保持正確的相對位置
|
||||
|
||||
#### Scenario: 拖拉調整任務日期
|
||||
- **GIVEN** 使用者正在查看甘特圖
|
||||
- **WHEN** 使用者拖拉任務條改變位置或長度
|
||||
- **THEN** 系統更新任務的 start_date 和 due_date
|
||||
- **AND** 驗證日期合理性(start_date <= due_date)
|
||||
|
||||
#### Scenario: 顯示任務依賴關係
|
||||
- **GIVEN** 任務之間存在依賴關係
|
||||
- **WHEN** 使用者查看甘特圖
|
||||
- **THEN** 系統顯示連接任務的箭頭
|
||||
- **AND** 箭頭方向表示依賴方向(前置任務 → 後續任務)
|
||||
|
||||
#### Scenario: 新增任務依賴
|
||||
- **GIVEN** 使用者在甘特圖上選擇兩個任務
|
||||
- **WHEN** 使用者建立依賴關係
|
||||
- **THEN** 系統儲存依賴關係
|
||||
- **AND** 顯示連接箭頭
|
||||
|
||||
#### Scenario: 刪除任務依賴
|
||||
- **GIVEN** 任務之間存在依賴關係
|
||||
- **WHEN** 使用者刪除該依賴
|
||||
- **THEN** 系統移除依賴記錄
|
||||
- **AND** 連接箭頭消失
|
||||
|
||||
#### Scenario: 循環依賴檢測
|
||||
- **GIVEN** 使用者嘗試建立依賴關係
|
||||
- **WHEN** 該依賴會形成循環(A → B → C → A)
|
||||
- **THEN** 系統拒絕建立並顯示錯誤訊息
|
||||
- **AND** 現有依賴關係保持不變
|
||||
|
||||
#### Scenario: 依賴類型支援
|
||||
- **GIVEN** 使用者建立任務依賴
|
||||
- **WHEN** 使用者選擇依賴類型
|
||||
- **THEN** 系統支援以下類型:
|
||||
- Finish-to-Start (FS): 前置完成後開始
|
||||
- Start-to-Start (SS): 前置開始後開始
|
||||
- Finish-to-Finish (FF): 前置完成後完成
|
||||
- Start-to-Finish (SF): 前置開始後完成
|
||||
|
||||
## ADDED Data Model
|
||||
|
||||
```
|
||||
pjctrl_task_dependencies
|
||||
├── id: UUID (PK)
|
||||
├── predecessor_id: UUID (FK -> tasks)
|
||||
├── successor_id: UUID (FK -> tasks)
|
||||
├── dependency_type: ENUM('FS', 'SS', 'FF', 'SF') DEFAULT 'FS'
|
||||
├── lag_days: INT DEFAULT 0
|
||||
└── created_at: TIMESTAMP
|
||||
```
|
||||
@@ -1,92 +0,0 @@
|
||||
# Tasks: Add Gantt View
|
||||
|
||||
## Backend Tasks
|
||||
|
||||
### 1. Add start_date to Task model
|
||||
- [x] Add `start_date` column to Task model
|
||||
- [x] Create Alembic migration
|
||||
- [x] Update TaskCreate/TaskUpdate schemas to include start_date
|
||||
- [x] Update TaskResponse schema
|
||||
- **驗證**: Migration 成功,API 可設定 start_date
|
||||
|
||||
### 2. Create TaskDependency model
|
||||
- [x] Create `backend/app/models/task_dependency.py`
|
||||
- [x] Define predecessor_id, successor_id, dependency_type, lag_days
|
||||
- [x] Update `backend/app/models/__init__.py`
|
||||
- [x] Create Alembic migration
|
||||
- **驗證**: Migration 成功
|
||||
|
||||
### 3. Implement dependency CRUD API
|
||||
- [x] Create `backend/app/api/task_dependencies/router.py`
|
||||
- [x] Implement `POST /api/tasks/{task_id}/dependencies` - 新增依賴
|
||||
- [x] Implement `GET /api/tasks/{task_id}/dependencies` - 取得依賴
|
||||
- [x] Implement `DELETE /api/task-dependencies/{dependency_id}` - 刪除依賴
|
||||
- [x] Add circular dependency detection
|
||||
- [x] Register router in main.py
|
||||
- **驗證**: 依賴關係 CRUD 可正常操作,循環依賴被拒絕
|
||||
|
||||
### 4. Add date validation
|
||||
- [x] Validate start_date <= due_date
|
||||
- [x] Validate dependency constraints on date change
|
||||
- [x] Add validation tests
|
||||
- **驗證**: 不合理日期被拒絕
|
||||
|
||||
### 5. Add backend tests
|
||||
- [x] Test TaskDependency CRUD
|
||||
- [x] Test circular dependency detection
|
||||
- [x] Test date validation
|
||||
- **驗證**: 所有測試通過
|
||||
|
||||
## Frontend Tasks
|
||||
|
||||
### 6. Install and configure Gantt library
|
||||
- [x] Install Frappe Gantt (or chosen library)
|
||||
- [x] Create wrapper component for React integration
|
||||
- [x] Configure styling to match application theme
|
||||
- **驗證**: Gantt 元件可正常渲染
|
||||
|
||||
### 7. Create GanttChart component
|
||||
- [x] Create `frontend/src/components/GanttChart.tsx`
|
||||
- [x] Load tasks with start_date and due_date
|
||||
- [x] Display tasks as horizontal bars on timeline
|
||||
- [x] Show task title, assignee, progress
|
||||
- [x] Support zoom levels (day, week, month)
|
||||
- **驗證**: 任務正確顯示在時間軸上
|
||||
|
||||
### 8. Implement drag-to-edit dates
|
||||
- [x] Handle bar drag to move task dates
|
||||
- [x] Handle bar resize to change duration
|
||||
- [x] Call API to update task dates
|
||||
- [x] Show optimistic update, rollback on error
|
||||
- **驗證**: 拖拉調整日期可正確更新
|
||||
|
||||
### 9. Implement dependency visualization
|
||||
- [x] Add dependency arrows between tasks
|
||||
- [x] Create dependency API service
|
||||
- [x] Implement add/remove dependency UI (right-click or toolbar)
|
||||
- [x] Validate and show error for circular dependencies
|
||||
- **驗證**: 依賴關係正確顯示和編輯
|
||||
|
||||
### 10. Integrate Gantt view into Tasks page
|
||||
- [x] Add "Gantt" option to view toggle
|
||||
- [x] Store view preference in localStorage
|
||||
- [x] Handle view switching
|
||||
- **驗證**: 可在 List/Kanban/Gantt 之間切換
|
||||
|
||||
## Task Dependencies
|
||||
|
||||
```
|
||||
[1] start_date 欄位
|
||||
↓
|
||||
[2] TaskDependency Model → [3] Dependency API → [4] Date Validation → [5] Tests
|
||||
↓
|
||||
[6] Gantt Library Setup → [7] GanttChart Component
|
||||
↓
|
||||
[8] Drag Edit → [9] Dependency UI
|
||||
↓
|
||||
[10] View Integration
|
||||
```
|
||||
|
||||
- Tasks 1-5 (Backend) 可平行於 Tasks 6-10 (Frontend) 開發
|
||||
- Task 7 需要 Task 1 完成(需要 start_date 欄位)
|
||||
- Task 9 需要 Task 3 完成(需要依賴 API)
|
||||
@@ -1,87 +0,0 @@
|
||||
# Proposal: Add Kanban Real-time Sync
|
||||
|
||||
## Change ID
|
||||
add-kanban-realtime-sync
|
||||
|
||||
## Status
|
||||
PROPOSED
|
||||
|
||||
## Summary
|
||||
實作看板 (Kanban) 多人即時同步功能,讓多位使用者同時查看同一專案看板時能即時看到任務狀態變更。
|
||||
|
||||
## Problem Statement
|
||||
目前系統的 WebSocket 僅用於通知推播 (`/ws/notifications`),看板頁面沒有即時同步機制:
|
||||
- 當使用者 A 拖曳任務改變狀態時,使用者 B 需要手動重新整理才能看到更新
|
||||
- 這導致協作效率降低,可能造成衝突或重複操作
|
||||
- 不符合 `project.md` 中「Real-time Sync: WebSocket for live collaboration」的要求
|
||||
|
||||
## Proposed Solution
|
||||
擴展現有 WebSocket 基礎架構,新增專案房間訂閱 (Project Room Subscription) 機制:
|
||||
|
||||
### 核心功能
|
||||
1. **專案房間訂閱**:使用者進入專案看板時自動訂閱該專案的即時更新
|
||||
2. **任務變更廣播**:任務狀態變更時,透過 Redis Pub/Sub 廣播給同一專案的所有訂閱者
|
||||
3. **增量更新**:僅推送變更的任務資料,而非整個看板重載
|
||||
|
||||
### 技術架構
|
||||
```
|
||||
Frontend (KanbanBoard)
|
||||
│
|
||||
▼ WebSocket: /ws/projects/{project_id}
|
||||
Backend (WebSocket Router)
|
||||
│
|
||||
▼ Redis Pub/Sub: channel:project:{project_id}:tasks
|
||||
Backend (Tasks API)
|
||||
```
|
||||
|
||||
### 訊息類型
|
||||
- `task_created`: 新任務建立
|
||||
- `task_updated`: 任務更新(含欄位變更)
|
||||
- `task_status_changed`: 任務狀態變更(拖曳看板)
|
||||
- `task_deleted`: 任務刪除
|
||||
- `task_assigned`: 任務指派變更
|
||||
|
||||
## Scope
|
||||
|
||||
### In Scope
|
||||
- 專案級 WebSocket 連線端點
|
||||
- 任務 CRUD 事件廣播
|
||||
- 前端看板即時更新
|
||||
- 樂觀更新 (Optimistic Update) 搭配衝突回滾
|
||||
|
||||
### Out of Scope
|
||||
- 任務欄位鎖定 (Field Locking) - 未來可擴展
|
||||
- 離線編輯同步 - 未來可擴展
|
||||
- 多專案同時訂閱 - 目前一次訂閱一個專案
|
||||
|
||||
## Impact Analysis
|
||||
|
||||
### Affected Components
|
||||
- `backend/app/api/websocket/router.py` - 新增專案 WebSocket 端點
|
||||
- `backend/app/services/websocket_manager.py` - 新增專案房間管理
|
||||
- `backend/app/api/tasks/router.py` - 任務變更時發送事件
|
||||
- `frontend/src/components/KanbanBoard.tsx` - 接收即時更新
|
||||
- `frontend/src/contexts/NotificationContext.tsx` - 可能需要擴展或新增專案同步 Context
|
||||
|
||||
### Performance Considerations
|
||||
- Redis Pub/Sub 已用於通知系統,可直接復用
|
||||
- 每專案一個 channel,避免全域廣播
|
||||
- 增量更新減少資料傳輸量
|
||||
|
||||
### Security Considerations
|
||||
- WebSocket 連線需驗證 JWT Token
|
||||
- 確認使用者有權限存取該專案
|
||||
- 防止未授權的專案訂閱
|
||||
|
||||
## Dependencies
|
||||
- 現有 WebSocket 基礎架構 (collaboration spec)
|
||||
- Redis Pub/Sub (已實作)
|
||||
- JWT Token 驗證 (user-auth spec)
|
||||
- 專案權限檢查 (resource-management spec)
|
||||
|
||||
## Related Specs
|
||||
- `collaboration` - 擴展 Real-time Notifications requirement
|
||||
- `task-management` - 任務 CRUD 操作
|
||||
|
||||
## Author
|
||||
Claude Code
|
||||
@@ -1,112 +0,0 @@
|
||||
# Collaboration - Spec Delta
|
||||
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Project Real-time Sync
|
||||
系統 SHALL 提供專案級即時同步機制,讓多位使用者同時協作時能即時看到任務變更。
|
||||
|
||||
#### Scenario: 訂閱專案即時更新
|
||||
- **GIVEN** 使用者擁有專案的存取權限
|
||||
- **WHEN** 使用者進入專案看板頁面
|
||||
- **THEN** 系統自動建立 WebSocket 連線並訂閱該專案的即時更新
|
||||
- **AND** 連線使用 JWT Token 進行身份驗證
|
||||
|
||||
#### Scenario: 接收任務狀態變更
|
||||
- **GIVEN** 使用者已訂閱專案即時更新
|
||||
- **WHEN** 其他使用者在同一專案中變更任務狀態
|
||||
- **THEN** 系統透過 WebSocket 即時推送 `task_status_changed` 事件
|
||||
- **AND** 看板 UI 自動更新任務位置
|
||||
|
||||
#### Scenario: 接收任務建立事件
|
||||
- **GIVEN** 使用者已訂閱專案即時更新
|
||||
- **WHEN** 其他使用者在同一專案中建立新任務
|
||||
- **THEN** 系統透過 WebSocket 即時推送 `task_created` 事件
|
||||
- **AND** 看板 UI 自動顯示新任務
|
||||
|
||||
#### Scenario: 接收任務刪除事件
|
||||
- **GIVEN** 使用者已訂閱專案即時更新
|
||||
- **WHEN** 其他使用者在同一專案中刪除任務
|
||||
- **THEN** 系統透過 WebSocket 即時推送 `task_deleted` 事件
|
||||
- **AND** 看板 UI 自動移除該任務
|
||||
|
||||
#### Scenario: 取消訂閱專案
|
||||
- **GIVEN** 使用者已訂閱專案即時更新
|
||||
- **WHEN** 使用者離開專案看板頁面
|
||||
- **THEN** 系統自動關閉 WebSocket 連線並取消訂閱
|
||||
- **AND** 釋放相關資源
|
||||
|
||||
#### Scenario: 樂觀更新與衝突處理
|
||||
- **GIVEN** 使用者拖曳任務改變狀態
|
||||
- **WHEN** 本地先套用變更(樂觀更新)但 API 呼叫失敗
|
||||
- **THEN** 系統回滾任務到原本位置
|
||||
- **AND** 顯示錯誤訊息通知使用者
|
||||
|
||||
#### Scenario: 避免重複應用自己的事件
|
||||
- **GIVEN** 使用者變更任務狀態並收到自己發起的事件
|
||||
- **WHEN** WebSocket 收到該事件
|
||||
- **THEN** 系統識別為本地發起的變更
|
||||
- **AND** 不重複應用該變更
|
||||
|
||||
## MODIFIED Requirements
|
||||
|
||||
### Requirement: Real-time Notifications
|
||||
系統 SHALL 透過 Redis 推播即時通知。
|
||||
|
||||
#### Scenario: 即時通知推播
|
||||
- **GIVEN** 發生需要通知的事件(如:被指派任務、被 @提及、阻礙標記)
|
||||
- **WHEN** 事件發生
|
||||
- **THEN** 系統透過 WebSocket 即時推播通知給相關使用者
|
||||
- **AND** 未讀通知顯示數量標示
|
||||
|
||||
#### Scenario: 通知已讀標記
|
||||
- **GIVEN** 使用者有未讀通知
|
||||
- **WHEN** 使用者查看通知
|
||||
- **THEN** 系統標記為已讀
|
||||
- **AND** 更新未讀數量
|
||||
|
||||
#### Scenario: 專案事件頻道分離
|
||||
- **GIVEN** 系統運作中
|
||||
- **WHEN** 發生任務變更事件
|
||||
- **THEN** 通知系統使用 `notifications:{user_id}` 頻道推送個人通知
|
||||
- **AND** 即時同步系統使用 `project:{project_id}:tasks` 頻道推送專案事件
|
||||
- **AND** 兩者互不干擾
|
||||
|
||||
## Technical Notes
|
||||
|
||||
### WebSocket 端點
|
||||
- 通知端點: `GET /ws/notifications?token=<jwt>`
|
||||
- 專案同步端點: `GET /ws/projects/{project_id}?token=<jwt>`
|
||||
|
||||
### Redis Pub/Sub Channels
|
||||
- 通知頻道: `notifications:{user_id}` (現有實作)
|
||||
- 專案任務事件頻道: `project:{project_id}:tasks` (新增)
|
||||
|
||||
### 事件訊息格式
|
||||
```json
|
||||
{
|
||||
"type": "task_created | task_updated | task_status_changed | task_deleted | task_assigned",
|
||||
"event_id": "uuid",
|
||||
"data": {
|
||||
"task_id": "uuid",
|
||||
"project_id": "uuid",
|
||||
"title": "string",
|
||||
"status_id": "uuid | null",
|
||||
"status_name": "string | null",
|
||||
"status_color": "string | null",
|
||||
"assignee_id": "uuid | null",
|
||||
"time_estimate": "number | null",
|
||||
/* 其他任務欄位 */
|
||||
},
|
||||
"triggered_by": "user_id",
|
||||
"timestamp": "ISO8601"
|
||||
}
|
||||
```
|
||||
|
||||
**欄位說明**:
|
||||
- `event_id`: 唯一事件識別碼,用於多分頁/多裝置事件去重
|
||||
- `triggered_by`: 觸發事件的使用者 ID,位於頂層便於前端過濾
|
||||
|
||||
### 連線管理
|
||||
- 使用與通知系統相同的心跳機制 (PING/PONG)
|
||||
- 支援同一使用者多個連線 (多分頁/裝置)
|
||||
- 連線中斷自動重連 (Frontend 實作)
|
||||
@@ -1,106 +0,0 @@
|
||||
# Tasks: Add Kanban Real-time Sync
|
||||
|
||||
## Phase 1: Backend Infrastructure
|
||||
|
||||
### 1.1 擴展 WebSocket Manager
|
||||
- [x] 在 `websocket_manager.py` 新增專案房間管理
|
||||
- 新增 `project_connections: Dict[str, Set[WebSocket]]`
|
||||
- 實作 `join_project(websocket, project_id)` 方法
|
||||
- 實作 `leave_project(websocket, project_id)` 方法
|
||||
- 實作 `broadcast_to_project(project_id, message)` 方法
|
||||
- **驗證**: ✅ 單元測試確認房間管理邏輯
|
||||
|
||||
### 1.2 新增專案 WebSocket 端點
|
||||
- [x] 在 `websocket/router.py` 新增 `/ws/projects/{project_id}` 端點
|
||||
- JWT Token 驗證 (復用現有 `get_user_from_token`)
|
||||
- 專案存取權限驗證
|
||||
- 心跳機制 (復用現有 PING/PONG 邏輯)
|
||||
- Redis Pub/Sub 訂閱 channel `project:{project_id}:tasks`
|
||||
- **驗證**: ✅ 整合測試確認連線建立與權限檢查
|
||||
|
||||
### 1.3 新增 Redis Pub/Sub Channel
|
||||
- [x] 在 `redis_pubsub.py` 新增 `ProjectTaskSubscriber` 類別
|
||||
- 訂閱 `project:{project_id}:tasks` channel
|
||||
- 轉發訊息到 WebSocket
|
||||
- [x] 新增 `publish_task_event(project_id, event_type, task_data)` 函數
|
||||
- 含 `event_id` 用於多分頁事件去重
|
||||
- 含 Redis 連線重試機制
|
||||
- **驗證**: ✅ 單元測試確認訊息發送與接收
|
||||
|
||||
## Phase 2: Backend Integration
|
||||
|
||||
### 2.1 任務 CRUD 事件發送
|
||||
- [x] 修改 `tasks/router.py` 的以下端點:
|
||||
- `create_task`: 發送 `task_created` 事件
|
||||
- `update_task`: 發送 `task_updated` 事件
|
||||
- `update_task_status`: 發送 `task_status_changed` 事件
|
||||
- `delete_task`: 發送 `task_deleted` 事件
|
||||
- `assign_task`: 發送 `task_assigned` 事件
|
||||
- **驗證**: ✅ API 測試確認事件在 CRUD 後正確發送 (8/8 tests passed)
|
||||
|
||||
### 2.2 批次操作支援
|
||||
- [ ] 確保批次狀態更新也發送事件 (延後實作)
|
||||
- [ ] 考慮事件合併/節流以避免大量更新時的效能問題 (延後實作)
|
||||
- **驗證**: 壓力測試確認批次操作不影響系統效能
|
||||
|
||||
## Phase 3: Frontend Integration
|
||||
|
||||
### 3.1 新增專案同步 Context
|
||||
- [x] 建立 `ProjectSyncContext.tsx`
|
||||
- 管理專案 WebSocket 連線
|
||||
- 提供訂閱/取消訂閱方法
|
||||
- 處理重連邏輯 (指數退避 + 最大重試次數)
|
||||
- 使用 `event_id` 進行多分頁事件去重
|
||||
- **驗證**: ✅ 開發者工具確認連線狀態
|
||||
|
||||
### 3.2 更新 KanbanBoard 組件
|
||||
- [x] 整合 `ProjectSyncContext`
|
||||
- 進入頁面時訂閱專案
|
||||
- 離開頁面時取消訂閱
|
||||
- [x] 處理即時事件:
|
||||
- `task_created`: 新增任務到對應欄位
|
||||
- `task_updated`: 更新任務資料
|
||||
- `task_status_changed`: 移動任務到新欄位 (含 status_color)
|
||||
- `task_deleted`: 從看板移除任務
|
||||
- [x] 實作樂觀更新 (Optimistic Update)
|
||||
- 拖曳時立即更新 UI
|
||||
- API 失敗時回滾
|
||||
- 使用 `event_id` 避免重複應用事件
|
||||
- **驗證**: ✅ 手動測試多瀏覽器同步
|
||||
|
||||
### 3.3 視覺回饋
|
||||
- [x] 新增連線狀態指示器 (Live / Offline)
|
||||
- [ ] 新增任務更新時的動畫效果 (延後實作)
|
||||
- [ ] 顯示「其他使用者正在編輯」提示 (延後實作)
|
||||
- **驗證**: ✅ UI 審查確認視覺效果
|
||||
|
||||
## Phase 4: Testing & Documentation
|
||||
|
||||
### 4.1 後端測試
|
||||
- [x] WebSocket 連線測試
|
||||
- [x] 專案權限驗證測試
|
||||
- [x] Redis Pub/Sub 整合測試
|
||||
- [ ] 併發更新測試 (延後實作)
|
||||
- **驗證**: ✅ 8/8 測試通過
|
||||
|
||||
### 4.2 前端測試
|
||||
- [x] Context 單元測試 (Build 通過)
|
||||
- [ ] KanbanBoard 整合測試 (延後實作)
|
||||
- [ ] 多使用者同步 E2E 測試 (延後實作)
|
||||
- **驗證**: ✅ Build 成功
|
||||
|
||||
### 4.3 效能驗證
|
||||
- [ ] 壓力測試:100+ 併發使用者 (延後實作)
|
||||
- [ ] 延遲測試:事件傳播 < 500ms (延後實作)
|
||||
- **驗證**: 效能指標符合預期
|
||||
|
||||
## 完成狀態
|
||||
|
||||
| Phase | 完成度 | 備註 |
|
||||
|-------|--------|------|
|
||||
| Phase 1 | 100% | 基礎架構完成 |
|
||||
| Phase 2 | 80% | 批次操作延後 |
|
||||
| Phase 3 | 90% | 動畫效果延後 |
|
||||
| Phase 4 | 60% | E2E/壓力測試延後 |
|
||||
|
||||
**整體完成度**: 核心功能 100%,進階功能延後
|
||||
@@ -1,101 +0,0 @@
|
||||
# Design: Dashboard Widgets
|
||||
|
||||
## Architecture Overview
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ Dashboard Page │
|
||||
├─────────────────────────────────────────────────────────────┤
|
||||
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌────────┐ │
|
||||
│ │ My Tasks │ │ Due This │ │ Overdue │ │ Done % │ │
|
||||
│ │ 12 │ │ Week: 5 │ │ 2 │ │ 78% │ │
|
||||
│ └─────────────┘ └─────────────┘ └─────────────┘ └────────┘ │
|
||||
├─────────────────────────────────────────────────────────────┤
|
||||
│ ┌──────────────────────────┐ ┌───────────────────────────┐ │
|
||||
│ │ My Workload │ │ Project Health │ │
|
||||
│ │ ┌────────────────────┐ │ │ ┌───────────────────────┐ │ │
|
||||
│ │ │ 32h / 40h (80%) │ │ │ │ 8 Healthy | 2 At Risk │ │ │
|
||||
│ │ │ ████████░░ │ │ │ │ Avg Score: 76% │ │ │
|
||||
│ │ └────────────────────┘ │ │ └───────────────────────┘ │ │
|
||||
│ └──────────────────────────┘ └───────────────────────────┘ │
|
||||
├─────────────────────────────────────────────────────────────┤
|
||||
│ Quick Actions: [Spaces] [Workload] [Health] [Audit*] │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
## API Design
|
||||
|
||||
### GET /api/dashboard
|
||||
|
||||
Single endpoint aggregates all dashboard data to minimize frontend requests.
|
||||
|
||||
```typescript
|
||||
interface DashboardResponse {
|
||||
task_stats: {
|
||||
assigned_count: number // Total tasks assigned to user
|
||||
due_this_week: number // Tasks with due_date in current week
|
||||
overdue_count: number // Tasks past due_date, not completed
|
||||
completion_rate: number // Percentage (0-100)
|
||||
}
|
||||
workload: {
|
||||
allocated_hours: number
|
||||
capacity_hours: number
|
||||
load_percentage: number
|
||||
load_level: 'normal' | 'warning' | 'overloaded'
|
||||
}
|
||||
health_summary: {
|
||||
total_projects: number
|
||||
healthy_count: number
|
||||
at_risk_count: number
|
||||
critical_count: number
|
||||
average_health_score: number
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Data Sources
|
||||
|
||||
| Widget | Source | Notes |
|
||||
|--------|--------|-------|
|
||||
| Task Statistics | `pjctrl_tasks` table | Filter by assignee_id = current user |
|
||||
| Workload | Existing `WorkloadService` | Reuse get_user_workload_detail() |
|
||||
| Health Summary | Existing `HealthService` | Reuse get_dashboard() summary |
|
||||
|
||||
## Performance Considerations
|
||||
|
||||
1. **Single API Call**: Frontend makes one request instead of three
|
||||
2. **Query Optimization**: Task stats use COUNT queries, not full rows
|
||||
3. **Caching**: Health summary already cached in Redis (5 min TTL)
|
||||
4. **No N+1**: Avoid loading related entities for counts
|
||||
|
||||
## Component Structure
|
||||
|
||||
```
|
||||
frontend/src/
|
||||
├── pages/
|
||||
│ └── Dashboard.tsx # Main page (modified)
|
||||
├── components/
|
||||
│ ├── dashboard/
|
||||
│ │ ├── StatisticsCard.tsx # Single stat card
|
||||
│ │ ├── WorkloadWidget.tsx # Workload progress bar
|
||||
│ │ ├── HealthWidget.tsx # Health summary
|
||||
│ │ └── QuickActions.tsx # Navigation links
|
||||
└── services/
|
||||
└── dashboard.ts # API service (new)
|
||||
```
|
||||
|
||||
## Trade-offs
|
||||
|
||||
### Decision: Single vs Multiple API Endpoints
|
||||
|
||||
**Chosen**: Single `/api/dashboard` endpoint
|
||||
|
||||
**Rationale**:
|
||||
- Reduces network round-trips (1 vs 3)
|
||||
- Simpler frontend loading state management
|
||||
- Data is always consistent (same timestamp)
|
||||
|
||||
**Rejected Alternative**: Use existing endpoints directly
|
||||
- Would require 3 parallel requests
|
||||
- More complex error handling
|
||||
- Harder to ensure data consistency
|
||||
@@ -1,38 +0,0 @@
|
||||
# Proposal: add-dashboard-widgets
|
||||
|
||||
## Summary
|
||||
|
||||
Replace the placeholder Dashboard page with functional dashboard widgets that provide users with an at-a-glance overview of their work and system status.
|
||||
|
||||
## Motivation
|
||||
|
||||
The current Dashboard displays only a welcome message and placeholder text ("Features will be added as development progresses"). Users need a centralized view of:
|
||||
- Their assigned tasks and workload
|
||||
- Project health across the organization
|
||||
- Quick access to common actions
|
||||
- System statistics
|
||||
|
||||
## Scope
|
||||
|
||||
### In Scope
|
||||
- Dashboard statistics cards (my tasks, overdue, completion rate)
|
||||
- My workload summary widget (reuses existing `/workload/me` API)
|
||||
- Project health summary widget (reuses existing `/projects/health/dashboard` API)
|
||||
- Quick actions section with navigation links
|
||||
- Backend API endpoint for aggregated dashboard data
|
||||
|
||||
### Out of Scope
|
||||
- Customizable widget layout (future enhancement)
|
||||
- Real-time WebSocket updates for dashboard (can be added later)
|
||||
- Dashboard for specific roles (manager vs engineer views)
|
||||
|
||||
## Dependencies
|
||||
|
||||
Reuses existing APIs:
|
||||
- `GET /workload/me` - User workload summary
|
||||
- `GET /projects/health/dashboard` - Project health overview
|
||||
- `GET /api/projects/{project_id}/tasks` with `assignee_id` filter
|
||||
|
||||
## Risk Assessment
|
||||
|
||||
**Low Risk** - This change primarily adds a new frontend page using existing backend APIs. A new lightweight aggregation endpoint is added but doesn't modify existing data structures.
|
||||
@@ -1,69 +0,0 @@
|
||||
# Dashboard Capability
|
||||
|
||||
Provides users with an at-a-glance overview of their work, project health, and system statistics.
|
||||
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Dashboard Statistics
|
||||
|
||||
The system SHALL display aggregated statistics showing the user's task overview.
|
||||
|
||||
#### Scenario: User views task statistics
|
||||
- Given: User is authenticated
|
||||
- When: User navigates to Dashboard
|
||||
- Then: System displays:
|
||||
- Total tasks assigned to user
|
||||
- Tasks due this week
|
||||
- Overdue tasks count
|
||||
- Completion rate percentage
|
||||
|
||||
### Requirement: My Workload Widget
|
||||
|
||||
The system SHALL display the current user's workload summary for the current week.
|
||||
|
||||
#### Scenario: User views personal workload
|
||||
- Given: User is authenticated
|
||||
- When: User views Dashboard
|
||||
- Then: System displays:
|
||||
- Allocated hours vs capacity hours
|
||||
- Load percentage with visual indicator
|
||||
- Load level status (normal/warning/overloaded)
|
||||
|
||||
### Requirement: Project Health Summary
|
||||
|
||||
The system SHALL display an aggregated project health summary.
|
||||
|
||||
#### Scenario: User views project health overview
|
||||
- Given: User is authenticated
|
||||
- When: User views Dashboard
|
||||
- Then: System displays:
|
||||
- Total projects count
|
||||
- Healthy/At-Risk/Critical breakdown
|
||||
- Average health score
|
||||
- Projects with blockers count
|
||||
|
||||
### Requirement: Quick Actions
|
||||
|
||||
The system SHALL provide quick navigation links to common actions.
|
||||
|
||||
#### Scenario: User accesses quick actions
|
||||
- Given: User is authenticated
|
||||
- When: User views Dashboard
|
||||
- Then: System displays navigation links to:
|
||||
- Spaces page
|
||||
- Workload page
|
||||
- Project Health page
|
||||
- (Admin only) Audit page
|
||||
|
||||
### Requirement: Dashboard API Endpoint
|
||||
|
||||
The backend SHALL provide a single aggregated endpoint for dashboard data.
|
||||
|
||||
#### Scenario: Frontend fetches dashboard data
|
||||
- Given: User is authenticated
|
||||
- When: Frontend requests GET /api/dashboard
|
||||
- Then: Backend returns:
|
||||
- User task statistics
|
||||
- Current week workload summary
|
||||
- Project health summary
|
||||
- And: Response is optimized with single database query where possible
|
||||
@@ -1,59 +0,0 @@
|
||||
# Implementation Tasks
|
||||
|
||||
## Phase 1: Backend API
|
||||
|
||||
### Task 1.1: Create Dashboard Schema
|
||||
- [ ] Create `backend/app/schemas/dashboard.py` with response models
|
||||
- `TaskStatistics`: assigned_count, due_this_week, overdue_count, completion_rate
|
||||
- `DashboardResponse`: task_stats, workload_summary, health_summary
|
||||
- **Validation**: Schema imports without errors
|
||||
|
||||
### Task 1.2: Create Dashboard Router
|
||||
- [ ] Create `backend/app/api/dashboard/router.py`
|
||||
- [ ] Implement `GET /api/dashboard` endpoint
|
||||
- Query tasks assigned to current user
|
||||
- Reuse workload service for current week summary
|
||||
- Reuse health service for project summary
|
||||
- [ ] Register router in `backend/app/main.py`
|
||||
- **Validation**: `curl -H "Authorization: Bearer $TOKEN" http://localhost:8000/api/dashboard` returns valid JSON
|
||||
|
||||
## Phase 2: Frontend Implementation
|
||||
|
||||
### Task 2.1: Create Dashboard Service
|
||||
- [ ] Create `frontend/src/services/dashboard.ts`
|
||||
- [ ] Define TypeScript interfaces matching backend schema
|
||||
- [ ] Implement `getDashboard()` API function
|
||||
- **Validation**: TypeScript compiles without errors
|
||||
|
||||
### Task 2.2: Implement Dashboard Components
|
||||
- [ ] Create `StatisticsCard` component for task stats
|
||||
- [ ] Create `WorkloadWidget` component (reuse styles from WorkloadPage)
|
||||
- [ ] Create `HealthSummaryWidget` component
|
||||
- [ ] Create `QuickActions` component with navigation links
|
||||
- **Validation**: Components render without console errors
|
||||
|
||||
### Task 2.3: Integrate Dashboard Page
|
||||
- [ ] Replace placeholder content in `Dashboard.tsx`
|
||||
- [ ] Add loading state with Skeleton components
|
||||
- [ ] Add error handling with retry
|
||||
- [ ] Style components to match existing UI
|
||||
- **Validation**: Dashboard displays data correctly after login
|
||||
|
||||
## Phase 3: Testing & Polish
|
||||
|
||||
### Task 3.1: Add Backend Tests
|
||||
- [ ] Create `backend/tests/test_dashboard.py`
|
||||
- [ ] Test endpoint returns correct structure
|
||||
- [ ] Test data aggregation logic
|
||||
- **Validation**: `pytest tests/test_dashboard.py -v` passes
|
||||
|
||||
### Task 3.2: Visual Polish
|
||||
- [ ] Ensure responsive layout for mobile
|
||||
- [ ] Match color scheme with existing pages
|
||||
- [ ] Add subtle animations for loading states
|
||||
- **Validation**: Manual visual review on different screen sizes
|
||||
|
||||
## Dependencies
|
||||
|
||||
- Tasks 2.x depend on Task 1.2 completion
|
||||
- Task 3.1 can run in parallel with Phase 2
|
||||
@@ -1,66 +0,0 @@
|
||||
# Proposal: Add Delete Capability for Spaces and Projects
|
||||
|
||||
## Summary
|
||||
Enable users to delete workspaces (spaces) and projects from the frontend UI. The backend already supports soft deletion, but the frontend lacks the necessary UI components and service functions.
|
||||
|
||||
## Problem Statement
|
||||
Currently, users cannot delete spaces or projects from the UI even though:
|
||||
1. Backend DELETE endpoints exist (`DELETE /spaces/{id}`, `DELETE /projects/{id}`)
|
||||
2. Both implement soft-delete (setting `is_active = False`)
|
||||
3. Project deletion includes audit logging
|
||||
|
||||
Users must access the database directly or use API tools to delete items, which is not user-friendly.
|
||||
|
||||
## Proposed Solution
|
||||
Add frontend UI components and service functions to enable deletion:
|
||||
|
||||
1. **Frontend Services**: Add `deleteSpace()` and `deleteProject()` functions
|
||||
2. **Spaces Page**: Add delete button with confirmation dialog
|
||||
3. **Projects Page**: Add delete button with confirmation dialog
|
||||
4. **Translations**: Add i18n strings for delete UI elements
|
||||
|
||||
## Scope
|
||||
|
||||
### In Scope
|
||||
- Delete button UI for spaces (owner only)
|
||||
- Delete button UI for projects (owner only)
|
||||
- Confirmation dialogs with clear warning messages
|
||||
- i18n translations (zh-TW, en)
|
||||
- Audit trail (already implemented in backend for projects)
|
||||
|
||||
### Out of Scope
|
||||
- Hard delete (permanent removal from database)
|
||||
- Restore/undelete functionality
|
||||
- Cascading delete behavior changes (current soft-delete preserves child items)
|
||||
- Bulk delete operations
|
||||
|
||||
## Impact Analysis
|
||||
|
||||
### Affected Components
|
||||
| Component | Change Type | Description |
|
||||
|-----------|-------------|-------------|
|
||||
| `frontend/src/services/spaces.ts` | NEW | Add deleteSpace function |
|
||||
| `frontend/src/services/projects.ts` | MODIFY | Add deleteProject function |
|
||||
| `frontend/src/pages/Spaces.tsx` | MODIFY | Add delete button and dialog |
|
||||
| `frontend/src/pages/Projects.tsx` | MODIFY | Add delete button and dialog |
|
||||
| `frontend/public/locales/*/spaces.json` | MODIFY | Add delete translations |
|
||||
| `frontend/public/locales/*/projects.json` | MODIFY | Add delete translations |
|
||||
|
||||
### Dependencies
|
||||
- Backend DELETE endpoints (already implemented)
|
||||
- Audit service (already integrated for project deletion)
|
||||
- ToastContext for success/error notifications (already available)
|
||||
|
||||
## Risks & Mitigations
|
||||
|
||||
| Risk | Likelihood | Impact | Mitigation |
|
||||
|------|------------|--------|------------|
|
||||
| Accidental deletion | Medium | High | Require typed confirmation for spaces with projects |
|
||||
| Permission confusion | Low | Medium | Clear "owner only" messaging |
|
||||
|
||||
## Success Criteria
|
||||
1. Space owner can delete empty spaces with confirmation
|
||||
2. Space owner can delete spaces with projects (with strong warning)
|
||||
3. Project owner can delete projects with confirmation
|
||||
4. All deletions are logged in audit trail
|
||||
5. UI shows appropriate error messages for non-owners
|
||||
@@ -1,64 +0,0 @@
|
||||
# Task Management - Delete Capability Delta
|
||||
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Space Deletion
|
||||
系統 SHALL 允許空間擁有者刪除空間(軟刪除)。
|
||||
|
||||
#### Scenario: 刪除空白空間
|
||||
- **GIVEN** 使用者是空間的擁有者
|
||||
- **AND** 空間內沒有任何專案
|
||||
- **WHEN** 使用者點擊刪除按鈕並確認
|
||||
- **THEN** 系統將空間標記為已刪除 (is_active = false)
|
||||
- **AND** 空間不再顯示於列表中
|
||||
- **AND** 顯示成功通知
|
||||
|
||||
#### Scenario: 刪除含專案的空間
|
||||
- **GIVEN** 使用者是空間的擁有者
|
||||
- **AND** 空間內包含一個或多個專案
|
||||
- **WHEN** 使用者點擊刪除按鈕
|
||||
- **THEN** 系統顯示警告對話框,說明包含 N 個專案
|
||||
- **AND** 要求使用者輸入空間名稱以確認刪除
|
||||
- **WHEN** 使用者正確輸入名稱並確認
|
||||
- **THEN** 系統將空間標記為已刪除
|
||||
- **AND** 空間內的專案同時被軟刪除
|
||||
- **AND** 顯示成功通知
|
||||
|
||||
#### Scenario: 非擁有者無法刪除空間
|
||||
- **GIVEN** 使用者不是空間的擁有者
|
||||
- **WHEN** 使用者嘗試刪除空間
|
||||
- **THEN** 系統拒絕操作
|
||||
- **AND** 顯示權限不足的錯誤訊息
|
||||
|
||||
### Requirement: Project Deletion
|
||||
系統 SHALL 允許專案擁有者刪除專案(軟刪除),並記錄於稽核日誌。
|
||||
|
||||
#### Scenario: 刪除專案
|
||||
- **GIVEN** 使用者是專案的擁有者
|
||||
- **WHEN** 使用者點擊刪除按鈕
|
||||
- **THEN** 系統顯示確認對話框,說明專案內的任務數量
|
||||
- **WHEN** 使用者確認刪除
|
||||
- **THEN** 系統將專案標記為已刪除 (is_active = false)
|
||||
- **AND** 專案不再顯示於空間的專案列表中
|
||||
- **AND** 系統記錄刪除事件至稽核日誌
|
||||
- **AND** 顯示成功通知
|
||||
|
||||
#### Scenario: 非擁有者無法刪除專案
|
||||
- **GIVEN** 使用者不是專案的擁有者
|
||||
- **WHEN** 使用者嘗試刪除專案
|
||||
- **THEN** 系統拒絕操作
|
||||
- **AND** 顯示權限不足的錯誤訊息
|
||||
|
||||
#### Scenario: 刪除專案的稽核記錄
|
||||
- **GIVEN** 專案擁有者刪除專案
|
||||
- **WHEN** 刪除操作完成
|
||||
- **THEN** 稽核日誌記錄以下資訊:
|
||||
- event_type: "project.delete"
|
||||
- resource_type: "project"
|
||||
- action: DELETE
|
||||
- user_id: 執行刪除的使用者
|
||||
- resource_id: 被刪除的專案 ID
|
||||
- changes: [{ field: "is_active", old_value: true, new_value: false }]
|
||||
|
||||
## Cross-references
|
||||
- Relates to: `audit-trail` spec (project deletion triggers audit event)
|
||||
@@ -1,55 +0,0 @@
|
||||
# Tasks: Add Delete Capability
|
||||
|
||||
## Task List
|
||||
|
||||
### Phase 1: Frontend Services
|
||||
- [x] **T1**: Create `frontend/src/services/spaces.ts` with CRUD operations including `deleteSpace()`
|
||||
- Note: Integrated directly into Spaces.tsx following existing pattern (uses api.delete directly)
|
||||
- [x] **T2**: Add `deleteProject()` function to projects service (or create if missing)
|
||||
- Note: Integrated directly into Projects.tsx following existing pattern (uses api.delete directly)
|
||||
|
||||
### Phase 2: Spaces Page UI
|
||||
- [x] **T3**: Add delete button to space cards in Spaces.tsx (visible to owner only)
|
||||
- [x] **T4**: Implement delete confirmation dialog for spaces
|
||||
- [x] **T5**: Handle delete success/error with toast notifications
|
||||
- [x] **T6**: Add spaces delete translations (zh-TW, en)
|
||||
- Note: Translations already existed in locale files
|
||||
|
||||
### Phase 3: Projects Page UI
|
||||
- [x] **T7**: Add delete button to project cards in Projects.tsx (visible to owner only)
|
||||
- [x] **T8**: Implement delete confirmation dialog for projects
|
||||
- [x] **T9**: Handle delete success/error with toast notifications
|
||||
- [x] **T10**: Add projects delete translations (zh-TW, en)
|
||||
- Note: Translations already existed in locale files
|
||||
|
||||
### Phase 4: Verification
|
||||
- [x] **T11**: Test space deletion flow (empty space, space with projects)
|
||||
- TypeScript compilation passed
|
||||
- [x] **T12**: Test project deletion flow
|
||||
- TypeScript compilation passed
|
||||
- [x] **T13**: Verify audit trail entries for deletions
|
||||
- Backend already implements audit logging for project deletions
|
||||
- [x] **T14**: Verify permission checks (non-owner cannot delete)
|
||||
- Frontend only shows delete button to owner or system admin
|
||||
|
||||
## Dependencies
|
||||
```
|
||||
T1 ──┬──> T3 ──> T4 ──> T5 ──> T6
|
||||
│
|
||||
T2 ──┴──> T7 ──> T8 ──> T9 ──> T10
|
||||
│
|
||||
T11, T12, T13, T14 ─────────────┘
|
||||
```
|
||||
|
||||
## Parallelizable Work
|
||||
- T1 and T2 can run in parallel
|
||||
- T3-T6 (Spaces) and T7-T10 (Projects) can run in parallel after T1/T2
|
||||
|
||||
## Verification Checklist
|
||||
- [x] Space delete button only visible to owner
|
||||
- [x] Project delete button only visible to owner
|
||||
- [x] Confirmation dialog shows for both
|
||||
- [x] Delete refreshes list correctly
|
||||
- [x] Toast notifications work for success/error
|
||||
- [x] Translations complete for zh-TW and en
|
||||
- [x] Audit log captures project deletions
|
||||
@@ -1,18 +0,0 @@
|
||||
# Change: Add API Enhancements and Future-Proofing
|
||||
|
||||
## Why
|
||||
To improve API maintainability and prepare for future growth: standardize API response format for consistent client handling, add API versioning for backwards compatibility, enhance health check for better monitoring, and add project templates for improved user productivity.
|
||||
|
||||
## What Changes
|
||||
- Implement standardized API response wrapper
|
||||
- Add API version prefix (/api/v1/)
|
||||
- Enhance health check with database and Redis status
|
||||
- Add project template feature for creating projects from templates
|
||||
|
||||
## Impact
|
||||
- Affected specs: dashboard
|
||||
- Affected code:
|
||||
- `backend/app/main.py` - API versioning, health check
|
||||
- `backend/app/core/response.py` - Response wrapper (new)
|
||||
- `backend/app/models/project_template.py` - Template model (new)
|
||||
- `backend/app/api/projects/router.py` - Template endpoints
|
||||
@@ -1,57 +0,0 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Standardized API Response Format
|
||||
The system SHALL return all API responses in a consistent standardized format.
|
||||
|
||||
#### Scenario: Successful API response
|
||||
- **WHEN** API request succeeds
|
||||
- **THEN** response includes success=true
|
||||
- **THEN** response includes data field with result
|
||||
- **THEN** response includes timestamp field
|
||||
|
||||
#### Scenario: Error API response
|
||||
- **WHEN** API request fails
|
||||
- **THEN** response includes success=false
|
||||
- **THEN** response includes error_code field
|
||||
- **THEN** response includes message field with description
|
||||
|
||||
### Requirement: API Versioning
|
||||
The system SHALL support API versioning to enable backwards compatibility during upgrades.
|
||||
|
||||
#### Scenario: Versioned API endpoint
|
||||
- **WHEN** client calls /api/v1/tasks
|
||||
- **THEN** system routes to current version implementation
|
||||
- **THEN** response works with v1 client expectations
|
||||
|
||||
#### Scenario: Legacy API route
|
||||
- **WHEN** client calls /api/tasks (unversioned)
|
||||
- **THEN** system routes to default version
|
||||
- **THEN** response includes deprecation warning header
|
||||
|
||||
### Requirement: Comprehensive Health Check
|
||||
The system SHALL provide detailed health check endpoints for monitoring.
|
||||
|
||||
#### Scenario: All systems healthy
|
||||
- **WHEN** health check is called and all dependencies are available
|
||||
- **THEN** response includes status=healthy
|
||||
- **THEN** response includes checks object with database=ok, redis=ok
|
||||
|
||||
#### Scenario: Partial system failure
|
||||
- **WHEN** health check is called and Redis is unavailable
|
||||
- **THEN** response includes status=degraded
|
||||
- **THEN** response includes checks object with database=ok, redis=error
|
||||
|
||||
### Requirement: Project Templates
|
||||
The system SHALL support project templates to standardize project creation.
|
||||
|
||||
#### Scenario: Create project from template
|
||||
- **WHEN** user creates project selecting a template
|
||||
- **THEN** system copies TaskStatus definitions from template
|
||||
- **THEN** system copies CustomField definitions from template
|
||||
- **THEN** project is created with predefined structure
|
||||
|
||||
#### Scenario: Save project as template
|
||||
- **WHEN** user saves existing project as template
|
||||
- **THEN** system creates template with project's TaskStatus definitions
|
||||
- **THEN** system creates template with project's CustomField definitions
|
||||
- **THEN** template is available for future project creation
|
||||
@@ -1,35 +0,0 @@
|
||||
# Tasks: Add API Enhancements
|
||||
|
||||
## 1. API Response Standardization
|
||||
- [x] 1.1 Create response wrapper utility class
|
||||
- [x] 1.2 Define standard response structure {success, data, message, timestamp}
|
||||
- [x] 1.3 Create error response format with error_code field
|
||||
- [x] 1.4 Apply wrapper to sample endpoints for validation (deferred - breaking change, use incrementally)
|
||||
- [x] 1.5 Document response format in API docs (documented in code comments)
|
||||
|
||||
## 2. API Versioning
|
||||
- [x] 2.1 Add /api/v1/ prefix to all routes
|
||||
- [x] 2.2 Update frontend API base URL configuration
|
||||
- [x] 2.3 Keep /api/ routes as alias during transition
|
||||
- [x] 2.4 Add deprecation headers to old routes
|
||||
|
||||
## 3. Enhanced Health Check
|
||||
- [x] 3.1 Add database connectivity check
|
||||
- [x] 3.2 Add Redis connectivity check
|
||||
- [x] 3.3 Add scheduler status check
|
||||
- [x] 3.4 Return detailed status object with check results
|
||||
- [x] 3.5 Add /health/ready and /health/live endpoints
|
||||
|
||||
## 4. Project Templates
|
||||
- [x] 4.1 Create ProjectTemplate model with fields
|
||||
- [x] 4.2 Create database migration
|
||||
- [x] 4.3 Add template CRUD API endpoints
|
||||
- [x] 4.4 Implement "create project from template" endpoint
|
||||
- [x] 4.5 Copy TaskStatus and CustomField definitions from template
|
||||
- [x] 4.6 Add frontend template selection in project creation
|
||||
|
||||
## 5. Testing
|
||||
- [x] 5.1 Test standardized response format
|
||||
- [x] 5.2 Test API version routing
|
||||
- [x] 5.3 Test enhanced health check scenarios
|
||||
- [x] 5.4 Test project template creation flow
|
||||
@@ -1,18 +0,0 @@
|
||||
# Change: Add Backend Reliability Improvements
|
||||
|
||||
## Why
|
||||
Several backend reliability issues need addressing: database connection pool may be undersized for production load, Redis publish failures have no fallback mechanism, tasks with active blockers can be deleted without warning, and NAS storage configuration needs validation.
|
||||
|
||||
## What Changes
|
||||
- Optimize database connection pool configuration
|
||||
- Add local queue fallback for Redis publish failures
|
||||
- Add pre-deletion check for active blockers on tasks
|
||||
- Validate NAS storage path configuration on startup
|
||||
|
||||
## Impact
|
||||
- Affected specs: document-management
|
||||
- Affected code:
|
||||
- `backend/app/core/database.py` - Connection pool config
|
||||
- `backend/app/services/notification_service.py` - Redis fallback
|
||||
- `backend/app/api/tasks/router.py` - Blocker check
|
||||
- `backend/app/services/file_storage_service.py` - NAS validation
|
||||
@@ -1,46 +0,0 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Storage Path Validation
|
||||
The system SHALL validate file storage configuration on startup to ensure reliability.
|
||||
|
||||
#### Scenario: Valid NAS storage path
|
||||
- **WHEN** application starts with valid UPLOAD_DIR configuration
|
||||
- **THEN** system verifies path exists and is writable
|
||||
- **THEN** system logs confirmation of storage configuration
|
||||
|
||||
#### Scenario: Invalid storage path
|
||||
- **WHEN** application starts with invalid or inaccessible UPLOAD_DIR
|
||||
- **THEN** system logs error with specific issue (not found, not writable)
|
||||
- **THEN** system falls back to local storage with warning
|
||||
|
||||
#### Scenario: Storage health check
|
||||
- **WHEN** health check endpoint is called
|
||||
- **THEN** response includes storage availability status
|
||||
- **THEN** response includes available disk space if accessible
|
||||
|
||||
### Requirement: Notification Delivery Reliability
|
||||
The system SHALL ensure notification delivery even during temporary Redis failures.
|
||||
|
||||
#### Scenario: Redis temporarily unavailable
|
||||
- **WHEN** Redis publish fails due to connection error
|
||||
- **THEN** system queues message in local memory
|
||||
- **WHEN** Redis connection recovers
|
||||
- **THEN** system retries queued messages
|
||||
|
||||
#### Scenario: Queue overflow prevention
|
||||
- **WHEN** local message queue exceeds maximum size
|
||||
- **THEN** oldest messages are dropped
|
||||
- **THEN** system logs warning about dropped messages
|
||||
|
||||
### Requirement: Task Deletion Safety
|
||||
The system SHALL warn users when deleting tasks with unresolved blockers.
|
||||
|
||||
#### Scenario: Delete task with active blockers
|
||||
- **WHEN** user attempts to delete task with unresolved blockers
|
||||
- **THEN** system returns warning with blocker count
|
||||
- **THEN** user must confirm or use force_delete flag
|
||||
|
||||
#### Scenario: Force delete with blockers
|
||||
- **WHEN** user force deletes task with blockers
|
||||
- **THEN** system auto-resolves all blockers with "task deleted" reason
|
||||
- **THEN** system proceeds with task deletion
|
||||
@@ -1,31 +0,0 @@
|
||||
# Tasks: Add Backend Reliability Improvements
|
||||
|
||||
## 1. Database Connection Pool
|
||||
- [x] 1.1 Add pool_size=10 configuration
|
||||
- [x] 1.2 Add max_overflow=20 configuration
|
||||
- [x] 1.3 Add pool_timeout=30 configuration
|
||||
- [x] 1.4 Add environment variable overrides for pool settings
|
||||
- [x] 1.5 Log connection pool statistics periodically
|
||||
|
||||
## 2. Redis Fallback Mechanism
|
||||
- [x] 2.1 Create in-memory queue for failed Redis publishes
|
||||
- [x] 2.2 Implement background retry for queued messages
|
||||
- [x] 2.3 Add max queue size limit to prevent memory issues
|
||||
- [x] 2.4 Log Redis failures and recovery events
|
||||
|
||||
## 3. Blocker Deletion Check
|
||||
- [x] 3.1 Add check for unresolved blockers before task deletion
|
||||
- [x] 3.2 Return warning response with blocker count
|
||||
- [x] 3.3 Add force_delete parameter to bypass check
|
||||
- [x] 3.4 Auto-resolve blockers when force deleting
|
||||
|
||||
## 4. NAS Storage Validation
|
||||
- [x] 4.1 Validate UPLOAD_DIR path exists on startup
|
||||
- [x] 4.2 Check write permissions on storage directory
|
||||
- [x] 4.3 Log warning if using local storage instead of NAS
|
||||
- [x] 4.4 Add health check endpoint for storage status
|
||||
|
||||
## 5. Testing
|
||||
- [x] 5.1 Test under connection pool exhaustion
|
||||
- [x] 5.2 Test Redis disconnect and recovery
|
||||
- [x] 5.3 Test blocker deletion scenarios
|
||||
@@ -1,17 +0,0 @@
|
||||
# Change: Add Concurrency Handling and Reliability Improvements
|
||||
|
||||
## Why
|
||||
Multiple users editing the same task simultaneously can lead to lost updates (last-write-wins). Trigger execution failures are not retried, causing permanent failures on transient errors. Soft-delete restore operation does not cascade to child tasks, leading to data inconsistency.
|
||||
|
||||
## What Changes
|
||||
- Implement optimistic locking using version field for task updates
|
||||
- Add retry mechanism with exponential backoff for trigger execution
|
||||
- Implement cascade restore for soft-deleted tasks and their children
|
||||
|
||||
## Impact
|
||||
- Affected specs: task-management, automation
|
||||
- Affected code:
|
||||
- `backend/app/models/task.py` - Add version field
|
||||
- `backend/app/api/tasks/router.py` - Optimistic locking logic
|
||||
- `backend/app/services/trigger_scheduler.py` - Retry mechanism
|
||||
- `backend/app/api/tasks/router.py` - Cascade restore
|
||||
@@ -1,21 +0,0 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Trigger Execution Retry
|
||||
The system SHALL retry failed trigger executions with exponential backoff to handle transient failures.
|
||||
|
||||
#### Scenario: Trigger succeeds after retry
|
||||
- **WHEN** trigger execution fails due to transient error
|
||||
- **THEN** system retries after 1 second delay
|
||||
- **WHEN** retry succeeds
|
||||
- **THEN** trigger is marked as successful in execution log
|
||||
|
||||
#### Scenario: Trigger exhausts retries
|
||||
- **WHEN** trigger execution fails 3 consecutive times
|
||||
- **THEN** system marks trigger execution as permanently failed
|
||||
- **THEN** system sends alert notification to system administrators
|
||||
- **THEN** execution log contains all retry attempts with error details
|
||||
|
||||
#### Scenario: Non-retryable error
|
||||
- **WHEN** trigger fails with validation or permission error (4xx)
|
||||
- **THEN** system does not retry and marks as failed immediately
|
||||
- **THEN** error is logged with appropriate categorization
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user