Initial commit
This commit is contained in:
@@ -0,0 +1,777 @@
|
||||
# Gitea 工单管理系统架构设计
|
||||
|
||||
## 1. 架构摘要
|
||||
|
||||
本项目采用 Flutter 单客户端架构,客户端直连 Gitea REST API,不新增自研后端服务。业务代码按 Clean Architecture / Hexagonal Architecture 的思想分层:UI 依赖应用用例,应用用例依赖领域模型和仓储接口,数据层负责 Gitea API、认证头、分页、缓存和错误转换。
|
||||
|
||||
目标平台:
|
||||
|
||||
- Android:主验证平台。
|
||||
- iOS:主验证平台,依赖 macOS、Xcode、证书和签名配置。
|
||||
- 鸿蒙 / OpenHarmony:高风险适配平台,通过 OpenHarmony-SIG Flutter 兼容扩展做 Spike 和后续适配。
|
||||
|
||||
关键结论:
|
||||
|
||||
- 不做微服务,不做移动 BFF,MVP 直连 Gitea。
|
||||
- 不在本地保存完整业务数据库,仅做 token、用户偏好、最近仓库、轻量响应缓存。
|
||||
- 用户使用账号密码登录;客户端优先换取 Personal Access Token,无法换取时退回 Basic Auth 会话。
|
||||
- 当前 Gitea 服务是 HTTP,生产环境必须切换 HTTPS;内测阶段需要明确移动端明文访问配置。
|
||||
- 所有第三方 Flutter 插件必须记录 Android、iOS、鸿蒙兼容结论。
|
||||
|
||||
## 2. 设计目标
|
||||
|
||||
功能目标:
|
||||
|
||||
- 支持登录、仓库选择、工单列表、工单详情、创建工单、评论、关闭和重开。
|
||||
- 支持标签、里程碑、状态、关键词等筛选。
|
||||
- 支持后续通知、最近访问、收藏仓库等增强功能。
|
||||
|
||||
质量目标:
|
||||
|
||||
- UI 与业务逻辑解耦,便于测试。
|
||||
- Gitea API 与领域模型解耦,便于处理 API 版本差异。
|
||||
- 网络错误、权限错误、token 失效有统一处理。
|
||||
- 尽量使用纯 Dart 依赖,降低鸿蒙适配成本。
|
||||
- 关键业务逻辑可单测,核心用户路径可集成测试。
|
||||
|
||||
非目标:
|
||||
|
||||
- 不替代完整 Gitea Web。
|
||||
- 不实现服务端推送、Webhook 中间服务或移动推送平台。
|
||||
- 不在 MVP 中做复杂离线编辑和冲突合并。
|
||||
- 不为 Gitea 建立独立同步数据库。
|
||||
|
||||
## 3. 系统上下文
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
User["移动端用户"]
|
||||
App["Flutter Gitea 工单 App"]
|
||||
SecureStore["本地安全存储"]
|
||||
LocalCache["本地轻量缓存"]
|
||||
Gitea["Gitea 1.26.2 REST API\nhttp://111.228.39.103:3000/api/v1"]
|
||||
|
||||
User --> App
|
||||
App --> SecureStore
|
||||
App --> LocalCache
|
||||
App -- "HTTPS/HTTP REST\nAuthorization: token <token>" --> Gitea
|
||||
```
|
||||
|
||||
说明:
|
||||
|
||||
- 客户端是唯一新增系统。
|
||||
- Gitea 是既有外部系统,作为认证、权限、仓库、Issue、评论、标签、里程碑和通知的数据源。
|
||||
- 本地存储只服务于会话、偏好和体验优化,不作为事实数据源。
|
||||
|
||||
## 4. 架构模式选择
|
||||
|
||||
推荐模式:Flutter 客户端内的 Clean Architecture + Ports and Adapters。
|
||||
|
||||
选择理由:
|
||||
|
||||
- 移动端业务长期会演进,Issue、Repository、Notification 等领域需要清晰边界。
|
||||
- Gitea API 是外部系统,未来可能出现版本差异、鉴权变化或代理层,适合用 Repository 接口隔离。
|
||||
- Flutter UI 变化快,业务用例需要保持可测试。
|
||||
- 项目团队规模预期较小,不需要服务端微服务复杂度。
|
||||
|
||||
不选择微服务 / BFF 的理由:
|
||||
|
||||
- 当前核心能力都由 Gitea API 提供。
|
||||
- 引入后端会增加部署、安全、日志、运维和权限转发成本。
|
||||
- MVP 没有跨系统聚合、服务端推送或复杂数据加工需求。
|
||||
|
||||
未来可引入 BFF 的触发条件:
|
||||
|
||||
- 需要移动推送,且必须通过 Gitea Webhook 汇聚事件。
|
||||
- 需要统一企业登录、Token 换发、审计或风控。
|
||||
- 需要跨多个 Gitea 实例聚合搜索。
|
||||
- Gitea HTTP 无法短期改 HTTPS,但又需要安全外网访问。
|
||||
- 需要服务端缓存或报表计算,移动端直连无法满足性能目标。
|
||||
|
||||
## 5. 客户端分层
|
||||
|
||||
```mermaid
|
||||
flowchart TB
|
||||
Presentation["Presentation\nPages / Widgets / ViewModels"]
|
||||
Application["Application\nUse Cases / State Notifiers"]
|
||||
Domain["Domain\nEntities / Value Objects / Repository Ports"]
|
||||
Data["Data\nRepository Impl / DTO Mapper / API Client"]
|
||||
Platform["Platform\nSecure Storage / Local Cache / Device Config"]
|
||||
External["External\nGitea REST API"]
|
||||
|
||||
Presentation --> Application
|
||||
Application --> Domain
|
||||
Application --> Data
|
||||
Data --> Domain
|
||||
Data --> Platform
|
||||
Data --> External
|
||||
```
|
||||
|
||||
依赖规则:
|
||||
|
||||
- UI 不直接调用 HTTP Client。
|
||||
- Application 不直接解析 Gitea JSON。
|
||||
- Domain 不依赖 Flutter、HTTP、存储插件。
|
||||
- Data 可以依赖外部 SDK、HTTP、平台存储,并负责转换成领域模型。
|
||||
- Platform 能力通过接口包装,方便替换和 mock。
|
||||
|
||||
## 6. 模块划分
|
||||
|
||||
建议按 Feature-first + Shared Core 组织:
|
||||
|
||||
```text
|
||||
lib/
|
||||
main.dart
|
||||
app/
|
||||
app.dart
|
||||
router.dart
|
||||
theme/
|
||||
localization/
|
||||
core/
|
||||
config/
|
||||
app_config.dart
|
||||
gitea_endpoints.dart
|
||||
error/
|
||||
app_exception.dart
|
||||
failure.dart
|
||||
error_mapper.dart
|
||||
network/
|
||||
api_client.dart
|
||||
auth_interceptor.dart
|
||||
pagination.dart
|
||||
storage/
|
||||
secure_token_store.dart
|
||||
preferences_store.dart
|
||||
cache_store.dart
|
||||
platform/
|
||||
platform_capabilities.dart
|
||||
widgets/
|
||||
features/
|
||||
auth/
|
||||
domain/
|
||||
application/
|
||||
data/
|
||||
presentation/
|
||||
repositories/
|
||||
domain/
|
||||
application/
|
||||
data/
|
||||
presentation/
|
||||
issues/
|
||||
domain/
|
||||
application/
|
||||
data/
|
||||
presentation/
|
||||
comments/
|
||||
domain/
|
||||
application/
|
||||
data/
|
||||
presentation/
|
||||
metadata/
|
||||
domain/
|
||||
application/
|
||||
data/
|
||||
presentation/
|
||||
notifications/
|
||||
domain/
|
||||
application/
|
||||
data/
|
||||
presentation/
|
||||
```
|
||||
|
||||
模块职责:
|
||||
|
||||
| 模块 | 职责 |
|
||||
| --- | --- |
|
||||
| `auth` | Gitea 地址、token 登录、会话恢复、退出登录 |
|
||||
| `repositories` | 仓库列表、搜索、最近访问、收藏 |
|
||||
| `issues` | 工单列表、筛选、详情、创建、编辑、关闭、重开 |
|
||||
| `comments` | 评论列表、发表评论 |
|
||||
| `metadata` | 标签、里程碑、指派人候选 |
|
||||
| `notifications` | Gitea 通知列表、标记已读、跳转到工单 |
|
||||
| `core/network` | HTTP 请求、鉴权头、超时、重试、分页 |
|
||||
| `core/storage` | token、偏好、轻缓存 |
|
||||
| `core/error` | 统一异常和用户可见错误 |
|
||||
|
||||
边界规则:
|
||||
|
||||
- Feature 之间不能直接访问对方的 `data` 层。
|
||||
- 跨 Feature 共享的领域对象放在 `core` 或明确的 shared model 中,避免重复 JSON DTO。
|
||||
- 对 Gitea API 的所有调用必须经过 `core/network`。
|
||||
|
||||
## 7. 领域模型
|
||||
|
||||
核心领域对象:
|
||||
|
||||
```mermaid
|
||||
classDiagram
|
||||
class GiteaAccount {
|
||||
+String username
|
||||
+String displayName
|
||||
+String avatarUrl
|
||||
+bool isAdmin
|
||||
}
|
||||
|
||||
class Repository {
|
||||
+int id
|
||||
+String owner
|
||||
+String name
|
||||
+String fullName
|
||||
+bool private
|
||||
+bool archived
|
||||
+RepositoryPermissions permissions
|
||||
}
|
||||
|
||||
class Issue {
|
||||
+int id
|
||||
+int number
|
||||
+String title
|
||||
+String body
|
||||
+IssueState state
|
||||
+User author
|
||||
+List~User~ assignees
|
||||
+List~IssueLabel~ labels
|
||||
+Milestone? milestone
|
||||
+DateTime createdAt
|
||||
+DateTime updatedAt
|
||||
+DateTime? closedAt
|
||||
}
|
||||
|
||||
class IssueComment {
|
||||
+int id
|
||||
+String body
|
||||
+User author
|
||||
+DateTime createdAt
|
||||
+DateTime updatedAt
|
||||
}
|
||||
|
||||
class IssueFilter {
|
||||
+IssueState state
|
||||
+String? keyword
|
||||
+List~String~ labels
|
||||
+String? milestone
|
||||
+String? assignee
|
||||
+SortOrder sort
|
||||
}
|
||||
|
||||
Repository "1" --> "*" Issue
|
||||
Issue "1" --> "*" IssueComment
|
||||
Issue "*" --> "*" IssueLabel
|
||||
Issue "*" --> "0..1" Milestone
|
||||
```
|
||||
|
||||
模型设计原则:
|
||||
|
||||
- Domain Model 使用客户端稳定命名,如 `number` 表示 Gitea issue `index`。
|
||||
- DTO 保留 Gitea 原始字段,如 `index`、`html_url`、`pull_request` 等。
|
||||
- Mapper 负责 DTO 到 Domain 的转换,避免 Gitea API 细节污染 UI。
|
||||
- Issue 与 Pull Request 需要区分。Gitea API 的 issue 搜索可能返回 PR,应在列表中默认过滤或明确标识。
|
||||
|
||||
## 8. Repository 接口
|
||||
|
||||
Domain 层定义端口:
|
||||
|
||||
```dart
|
||||
abstract interface class AuthRepository {
|
||||
Future<GiteaAccount> validateToken(GiteaServer server, String token);
|
||||
Future<Session?> restoreSession();
|
||||
Future<void> saveSession(Session session);
|
||||
Future<void> clearSession();
|
||||
}
|
||||
|
||||
abstract interface class GiteaRepositoryRepository {
|
||||
Future<PagedResult<Repository>> listMyRepositories(PageRequest page);
|
||||
Future<PagedResult<Repository>> searchRepositories(String keyword, PageRequest page);
|
||||
Future<void> rememberRepository(Repository repository);
|
||||
Future<List<Repository>> listRecentRepositories();
|
||||
}
|
||||
|
||||
abstract interface class IssueRepository {
|
||||
Future<PagedResult<Issue>> listIssues(RepositoryRef repo, IssueFilter filter, PageRequest page);
|
||||
Future<PagedResult<Issue>> searchIssues(IssueSearchQuery query, PageRequest page);
|
||||
Future<IssueDetail> getIssue(RepositoryRef repo, int number);
|
||||
Future<Issue> createIssue(RepositoryRef repo, CreateIssueInput input);
|
||||
Future<Issue> updateIssue(RepositoryRef repo, int number, UpdateIssueInput input);
|
||||
Future<Issue> changeIssueState(RepositoryRef repo, int number, IssueState state);
|
||||
}
|
||||
|
||||
abstract interface class CommentRepository {
|
||||
Future<List<IssueComment>> listComments(RepositoryRef repo, int issueNumber);
|
||||
Future<IssueComment> addComment(RepositoryRef repo, int issueNumber, String body);
|
||||
}
|
||||
```
|
||||
|
||||
实现位于 Data 层:
|
||||
|
||||
- `GiteaAuthRepository`
|
||||
- `GiteaRepositoryRepository`
|
||||
- `GiteaIssueRepository`
|
||||
- `GiteaCommentRepository`
|
||||
- `GiteaNotificationRepository`
|
||||
|
||||
## 9. API 集成设计
|
||||
|
||||
Base URL:
|
||||
|
||||
- 默认:`http://111.228.39.103:3000/api/v1`
|
||||
- 配置:支持在登录页或构建参数中覆盖。
|
||||
|
||||
认证:
|
||||
|
||||
- 登录入口:Gitea 地址、账号、密码。
|
||||
- 首次校验:使用 Basic Auth 调用 `GET /user`。
|
||||
- Token 换取:登录成功后调用 `POST /users/{username}/tokens`,成功则后续请求使用 `Authorization: token <token>`。
|
||||
- 退回策略:如果 token 创建被拒绝但 Basic Auth 校验成功,后续请求使用 `Authorization: Basic <base64(username:password)>`。
|
||||
- 认证信息本地安全存储。
|
||||
- 401:清理会话并引导重新登录。
|
||||
- 403:显示无权限,不自动退出。
|
||||
|
||||
分页:
|
||||
|
||||
- 使用 Gitea 的 `page` 和 `limit` 查询参数。
|
||||
- 默认 `limit = 20`,列表页可调到 `30`。
|
||||
- `PagedResult` 包含 `items`、`page`、`limit`、`hasMore`。
|
||||
- 若响应头提供总数或 Link,优先使用;否则用返回数量是否等于 limit 推断。
|
||||
|
||||
错误映射:
|
||||
|
||||
| HTTP 状态 | 客户端错误 |
|
||||
| --- | --- |
|
||||
| 400 | 请求参数错误 |
|
||||
| 401 | 登录已失效 |
|
||||
| 403 | 没有操作权限 |
|
||||
| 404 | 资源不存在或无访问权限 |
|
||||
| 409 | 状态冲突 |
|
||||
| 422 | 表单校验失败 |
|
||||
| 429 | 请求过于频繁 |
|
||||
| 5xx | Gitea 服务异常 |
|
||||
| timeout | 网络超时 |
|
||||
| socket | 网络不可达 |
|
||||
|
||||
重试策略:
|
||||
|
||||
- GET 请求可在网络抖动或 5xx 下重试 1 次。
|
||||
- POST/PATCH 写操作默认不自动重试,避免重复创建或重复评论。
|
||||
- 用户手动重试时保留表单内容。
|
||||
|
||||
## 10. 关键用户流程
|
||||
|
||||
### 登录和会话恢复
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant U as User
|
||||
participant UI as LoginPage
|
||||
participant Auth as AuthUseCase
|
||||
participant API as GiteaApiClient
|
||||
participant Store as SecureTokenStore
|
||||
participant Gitea as Gitea API
|
||||
|
||||
U->>UI: 输入 Gitea 地址、账号和密码
|
||||
UI->>Auth: login(server, username, password)
|
||||
Auth->>API: GET /user
|
||||
API->>Gitea: Authorization: Basic <base64>
|
||||
Gitea-->>API: 200 User
|
||||
API-->>Auth: GiteaAccount
|
||||
Auth->>API: POST /users/{username}/tokens
|
||||
API->>Gitea: Authorization: Basic <base64>
|
||||
Gitea-->>API: AccessToken 或权限错误
|
||||
Auth->>Store: 优先保存 token,否则保存 Basic Auth 凭据
|
||||
Auth-->>UI: 登录成功
|
||||
UI->>UI: 跳转主页
|
||||
```
|
||||
|
||||
### 工单列表
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant UI as IssueListPage
|
||||
participant VM as IssueListController
|
||||
participant Repo as IssueRepository
|
||||
participant API as GiteaApiClient
|
||||
participant Gitea as Gitea API
|
||||
|
||||
UI->>VM: 打开仓库工单页
|
||||
VM->>Repo: listIssues(repo, filter, page)
|
||||
Repo->>API: GET /repos/{owner}/{repo}/issues
|
||||
API->>Gitea: page, limit, state, labels, milestone
|
||||
Gitea-->>API: Issue DTO[]
|
||||
API-->>Repo: DTO[]
|
||||
Repo-->>VM: PagedResult<Issue>
|
||||
VM-->>UI: 渲染列表
|
||||
```
|
||||
|
||||
### 创建工单
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant UI as CreateIssuePage
|
||||
participant UseCase as CreateIssueUseCase
|
||||
participant Repo as IssueRepository
|
||||
participant API as GiteaApiClient
|
||||
participant Gitea as Gitea API
|
||||
|
||||
UI->>UseCase: submit(input)
|
||||
UseCase->>UseCase: 校验标题和正文
|
||||
UseCase->>Repo: createIssue(repo, input)
|
||||
Repo->>API: POST /repos/{owner}/{repo}/issues
|
||||
API->>Gitea: title, body, labels, milestone, assignees
|
||||
Gitea-->>API: 201 Issue DTO
|
||||
API-->>Repo: Issue DTO
|
||||
Repo-->>UseCase: Issue
|
||||
UseCase-->>UI: 跳转详情
|
||||
```
|
||||
|
||||
## 11. 状态管理
|
||||
|
||||
推荐 Riverpod。
|
||||
|
||||
理由:
|
||||
|
||||
- 对 Flutter 项目足够轻量。
|
||||
- Provider 可组合,适合按 Feature 组织。
|
||||
- 易于注入 mock repository 做单元测试。
|
||||
- 不强依赖 BuildContext,业务状态更清晰。
|
||||
|
||||
状态边界:
|
||||
|
||||
| 状态 | 生命周期 | 建议实现 |
|
||||
| --- | --- | --- |
|
||||
| 会话状态 | App 全局 | `SessionController` |
|
||||
| 当前仓库 | 页面 / App 局部 | `SelectedRepositoryController` |
|
||||
| 工单列表 | 页面级,可缓存 | `IssueListController` |
|
||||
| 工单详情 | 页面级 | `IssueDetailController` |
|
||||
| 创建 / 评论表单 | 页面级短生命周期 | `StateNotifier` 或 `AsyncNotifier` |
|
||||
| 主题和偏好 | App 全局 | `PreferencesController` |
|
||||
|
||||
状态模型:
|
||||
|
||||
- 使用 `AsyncValue` 表示 loading/data/error。
|
||||
- 列表页保留上一次成功数据,错误以 banner/snackbar 呈现。
|
||||
- 写操作单独维护 submitting 状态,避免影响详情主数据。
|
||||
|
||||
## 12. 本地存储策略
|
||||
|
||||
| 数据 | 存储位置 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| Gitea token 或 Basic Auth 凭据 | 安全存储 | Android Keystore / iOS Keychain / 鸿蒙待验证 |
|
||||
| Gitea server URL | 安全存储或偏好存储 | 与 token 配套保存 |
|
||||
| 最近访问仓库 | 偏好存储 | 非敏感,可清理 |
|
||||
| 收藏仓库 | 偏好存储 | 非敏感 |
|
||||
| 主题、语言 | 偏好存储 | 非敏感 |
|
||||
| 工单列表缓存 | 轻量缓存 | 可选,仅用于弱网体验 |
|
||||
| 工单表单草稿 | 本地偏好或轻量缓存 | 不含 token,可按仓库和 issue key 存储 |
|
||||
|
||||
安全要求:
|
||||
|
||||
- Token、密码和 Basic Auth 凭据不进入日志、URL、崩溃报告、错误弹窗。
|
||||
- 退出登录必须清除认证信息。
|
||||
- 切换 Gitea 地址时必须重新校验认证信息。
|
||||
- 当前 HTTP 环境只适合内测,生产必须使用 HTTPS。
|
||||
|
||||
鸿蒙风险:
|
||||
|
||||
- `flutter_secure_storage` 等插件在 OpenHarmony 适配链上可能不可用。
|
||||
- 需要抽象 `TokenStore` 接口,为鸿蒙提供独立实现或降级策略。
|
||||
- 不允许为了兼容鸿蒙而把 token 或密码明文落盘作为生产方案。
|
||||
|
||||
## 13. 跨平台架构
|
||||
|
||||
```mermaid
|
||||
flowchart TB
|
||||
Shared["lib/ 共享 Dart 业务代码\nUI / Domain / Application / Data"]
|
||||
Android["android/\nManifest / Network Security Config"]
|
||||
IOS["ios/\nInfo.plist / ATS / Signing"]
|
||||
OHOS["ohos/\nOpenHarmony Flutter SDK / DevEco 配置"]
|
||||
Plugins["平台插件适配层\nSecure Storage / Preferences / URL Launcher"]
|
||||
|
||||
Shared --> Android
|
||||
Shared --> IOS
|
||||
Shared --> OHOS
|
||||
Shared --> Plugins
|
||||
Plugins --> Android
|
||||
Plugins --> IOS
|
||||
Plugins --> OHOS
|
||||
```
|
||||
|
||||
平台策略:
|
||||
|
||||
- 业务代码只写一次,尽量不使用 `Platform.isXxx` 分支散落在 Feature 中。
|
||||
- 平台差异集中到 `core/platform` 和 `core/storage`。
|
||||
- Android/iOS 使用官方 Flutter stable 构建链。
|
||||
- 鸿蒙使用 OpenHarmony-SIG Flutter SDK,优先在 Sprint 0 验证最小可运行路径。
|
||||
|
||||
HTTP 明文访问:
|
||||
|
||||
- Android:需要 Network Security Config 放行目标 host。
|
||||
- iOS:需要 ATS 例外配置;生产环境应移除例外。
|
||||
- 鸿蒙:需要在 OpenHarmony 工程中验证网络权限和明文策略。
|
||||
|
||||
## 14. 依赖选择
|
||||
|
||||
建议依赖:
|
||||
|
||||
| 能力 | 首选 | 备选 | 备注 |
|
||||
| --- | --- | --- | --- |
|
||||
| 状态管理 | `flutter_riverpod` | `bloc` | 以测试和注入便利为优先 |
|
||||
| HTTP | `dio` | `http` | `dio` 拦截器和错误处理更成熟;鸿蒙需验证 |
|
||||
| JSON | `json_serializable` | 手写 mapper | 类型安全和可维护性优先 |
|
||||
| 路由 | `go_router` | Flutter Navigator | 深链和页面守卫更清晰 |
|
||||
| 安全存储 | `flutter_secure_storage` | 平台 TokenStore 实现 | 鸿蒙必须验证 |
|
||||
| 偏好存储 | `shared_preferences` | 本地文件封装 | 鸿蒙必须验证 |
|
||||
| Markdown | `flutter_markdown` | 自定义简化渲染 | 鸿蒙必须验证 |
|
||||
| 日志 | `logging` | 自定义 logger | 必须脱敏 |
|
||||
|
||||
依赖准入规则:
|
||||
|
||||
- 引入前检查 Android、iOS、鸿蒙兼容性。
|
||||
- 若插件含原生代码,必须记录替代方案。
|
||||
- 业务核心不能直接依赖插件 API,应通过接口封装。
|
||||
|
||||
## 15. 网络与安全架构
|
||||
|
||||
请求链路:
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
UseCase["Use Case"] --> Repo["Repository Impl"]
|
||||
Repo --> Client["ApiClient"]
|
||||
Client --> Auth["AuthInterceptor"]
|
||||
Auth --> Error["ErrorMapper"]
|
||||
Error --> Gitea["Gitea API"]
|
||||
```
|
||||
|
||||
安全控制:
|
||||
|
||||
- `AuthInterceptor` 从 `CredentialStore` 获取 token 或 Basic Auth 凭据并注入请求头。
|
||||
- `LogInterceptor` 必须脱敏 `Authorization`、token、密码、cookie。
|
||||
- 所有请求设置超时。
|
||||
- 写操作使用明确的提交状态,防止重复点击。
|
||||
- 统一处理 401 并触发会话失效事件。
|
||||
|
||||
生产安全门槛:
|
||||
|
||||
- Gitea 必须启用 HTTPS。
|
||||
- 如果 token 创建成功,应设置最小权限,只授予仓库和工单所需范围。
|
||||
- 应提供 token / 凭据清理和退出登录路径。
|
||||
- 构建包中不硬编码真实 token。
|
||||
|
||||
## 16. 缓存与一致性
|
||||
|
||||
缓存策略:
|
||||
|
||||
- Gitea API 是事实数据源。
|
||||
- 列表页可以缓存最近一次成功响应,缓存时间建议 1-5 分钟。
|
||||
- 详情页进入时优先显示列表传入的摘要,再拉取详情和评论。
|
||||
- 写操作成功后主动失效对应列表和详情缓存。
|
||||
|
||||
一致性规则:
|
||||
|
||||
- 创建工单成功后跳转详情,并刷新对应仓库的 open 列表。
|
||||
- 评论成功后追加本地评论,再用后台刷新校准。
|
||||
- 关闭 / 重开成功后更新详情状态,并让列表下一次出现时刷新。
|
||||
- 写操作失败不得修改本地事实状态。
|
||||
|
||||
## 17. 可观测性
|
||||
|
||||
客户端日志:
|
||||
|
||||
- 开发环境记录请求方法、路径、状态码、耗时。
|
||||
- 生产环境默认不记录请求体和敏感头。
|
||||
- 记录错误分类:network、auth、permission、validation、server。
|
||||
|
||||
建议指标:
|
||||
|
||||
- 登录成功率。
|
||||
- 工单列表加载耗时 p50/p95。
|
||||
- 工单创建成功率。
|
||||
- 评论发送成功率。
|
||||
- 401/403/5xx 出现次数。
|
||||
- Android/iOS/鸿蒙构建结果。
|
||||
|
||||
说明:
|
||||
|
||||
- MVP 可先使用本地日志和调试面板。
|
||||
- 后续如果接入崩溃采集或埋点,需要先确认隐私和内网合规要求。
|
||||
|
||||
## 18. 测试架构
|
||||
|
||||
测试金字塔:
|
||||
|
||||
```mermaid
|
||||
flowchart TB
|
||||
E2E["少量端到端测试\n登录、列表、创建、评论、关闭"]
|
||||
Widget["Widget 测试\n页面状态、表单校验、错误展示"]
|
||||
Unit["大量单元测试\nUse Case、Mapper、Filter、ErrorMapper"]
|
||||
|
||||
E2E --> Widget
|
||||
Widget --> Unit
|
||||
```
|
||||
|
||||
测试重点:
|
||||
|
||||
- DTO 到 Domain Mapper。
|
||||
- API 错误到用户错误映射。
|
||||
- Issue 筛选参数构造。
|
||||
- 登录 token 校验和会话恢复。
|
||||
- 创建、评论、关闭、重开用例。
|
||||
- 写操作失败后表单内容保留。
|
||||
|
||||
Mock 策略:
|
||||
|
||||
- Domain/Application 测试 mock Repository。
|
||||
- Data 层测试 mock HTTP Client。
|
||||
- Widget 测试注入 fake Controller 或 fake Repository。
|
||||
- 集成测试使用测试 Gitea 实例或可控 mock server。
|
||||
|
||||
## 19. 构建与交付
|
||||
|
||||
建议环境矩阵:
|
||||
|
||||
| 阶段 | Android | iOS | 鸿蒙 |
|
||||
| --- | --- | --- | --- |
|
||||
| Sprint 0 | Debug 可运行 | 工程可生成 | 最小 demo Spike |
|
||||
| Sprint 1 | Debug 主路径 | Simulator/真机准备 | 插件兼容清单 |
|
||||
| Sprint 2 | APK 内测包 | iOS Debug 包 | 共享代码编译验证 |
|
||||
| Sprint 3 | Release 包 | 可签名包 | 输出可运行包或阻塞报告 |
|
||||
|
||||
构建配置:
|
||||
|
||||
- 使用 `--dart-define=GITEA_BASE_URL=...` 支持环境切换。
|
||||
- Debug 可默认指向 `http://111.228.39.103:3000`。
|
||||
- Release 不应强依赖 HTTP 默认值,需确认 HTTPS 地址。
|
||||
- Android/iOS/HarmonyOS 平台配置文件分开维护。
|
||||
|
||||
## 20. 风险与缓解
|
||||
|
||||
| 风险 | 等级 | 架构缓解 |
|
||||
| --- | --- | --- |
|
||||
| HTTP 明文传输密码或 token | Critical | 生产强制 HTTPS;内测配置清晰标记;日志脱敏 |
|
||||
| 鸿蒙 Flutter 生态不确定 | High | 平台能力抽象;减少原生插件;Sprint 0 Spike |
|
||||
| Gitea API 字段和权限差异 | Medium | DTO/Domain 分离;统一错误映射;写操作处理 401/403/422 |
|
||||
| 插件不兼容鸿蒙 | Medium | 依赖准入清单;对安全存储和偏好存储做接口封装 |
|
||||
| 工单列表数据量大 | Medium | 分页、筛选、轻缓存、骨架屏 |
|
||||
| 重复提交创建或评论 | Medium | 写操作禁用重复点击;POST 不自动重试 |
|
||||
| PR 与 Issue 混合返回 | Medium | Mapper 标记 pull request;列表默认过滤或明确展示 |
|
||||
|
||||
## 21. ADR
|
||||
|
||||
### ADR-001:采用客户端直连 Gitea API
|
||||
|
||||
状态:Accepted
|
||||
|
||||
背景:
|
||||
|
||||
- MVP 的核心数据和操作都由 Gitea REST API 提供。
|
||||
- 当前没有跨系统聚合、推送、审计中台或复杂数据加工要求。
|
||||
|
||||
决策:
|
||||
|
||||
- Flutter 客户端直接调用 Gitea `/api/v1`。
|
||||
- 不新增后端服务。
|
||||
|
||||
后果:
|
||||
|
||||
- 交付更快,部署更少。
|
||||
- 移动端必须直接处理 token 安全、网络错误和权限错误。
|
||||
- 如果未来需要推送、企业统一认证或多实例聚合,可以新增 BFF。
|
||||
|
||||
### ADR-002:采用 Clean Architecture / Ports and Adapters 分层
|
||||
|
||||
状态:Accepted
|
||||
|
||||
背景:
|
||||
|
||||
- Gitea API 是外部系统,字段、错误、权限都不应泄漏到 UI。
|
||||
- 移动端页面和状态管理会持续变化。
|
||||
|
||||
决策:
|
||||
|
||||
- 按 Presentation、Application、Domain、Data、Platform 分层。
|
||||
- Domain 定义 Repository 接口,Data 层实现 Gitea API 适配。
|
||||
|
||||
后果:
|
||||
|
||||
- 初始文件数量比简单 CRUD 多。
|
||||
- 测试和后续平台适配更稳。
|
||||
|
||||
### ADR-003:账号密码登录,优先换取 Personal Access Token
|
||||
|
||||
状态:Accepted
|
||||
|
||||
背景:
|
||||
|
||||
- Gitea API 支持 `Authorization: token <token>`。
|
||||
- 用户明确要求使用账号密码登录。
|
||||
- Gitea API 同时支持 Basic Auth 和 `POST /users/{username}/tokens` 创建访问令牌。
|
||||
|
||||
决策:
|
||||
|
||||
- MVP 使用账号密码作为登录入口。
|
||||
- 登录时先用 Basic Auth 调用 `GET /user` 校验身份。
|
||||
- 校验成功后尝试创建访问令牌;成功则保存 token,失败但 Basic Auth 可用则保存 Basic Auth 凭据。
|
||||
- 认证信息安全存储,本地会话恢复。
|
||||
|
||||
后果:
|
||||
|
||||
- 用户无需提前生成 token。
|
||||
- 当前 HTTP 环境下密码和 token 都可能明文传输,生产必须启用 HTTPS。
|
||||
- 如果 Gitea 开启 2FA 或禁用 token API,可能需要补充 OTP 输入或仅使用 Basic Auth。
|
||||
|
||||
### ADR-004:本地只做轻缓存,不建立同步数据库
|
||||
|
||||
状态:Accepted
|
||||
|
||||
背景:
|
||||
|
||||
- MVP 不要求离线编辑。
|
||||
- Gitea 是事实数据源。
|
||||
|
||||
决策:
|
||||
|
||||
- 本地保存 token、偏好、最近仓库、可过期列表缓存和草稿。
|
||||
- 不保存完整 Issue 数据库。
|
||||
|
||||
后果:
|
||||
|
||||
- 实现简单,数据一致性风险低。
|
||||
- 弱网体验有限,后续如需离线能力需引入同步机制。
|
||||
|
||||
### ADR-005:鸿蒙作为独立平台 Spike 管理
|
||||
|
||||
状态:Accepted
|
||||
|
||||
背景:
|
||||
|
||||
- Flutter 官方稳定平台不包含 HarmonyOS/OpenHarmony。
|
||||
- 鸿蒙依赖 OpenHarmony-SIG Flutter 兼容扩展和 DevEco Studio。
|
||||
|
||||
决策:
|
||||
|
||||
- Sprint 0 做最小 demo 和插件兼容性验证。
|
||||
- 平台相关能力统一接口封装,不散落到业务代码。
|
||||
|
||||
后果:
|
||||
|
||||
- Android/iOS 主路径不被鸿蒙不确定性拖住。
|
||||
- 鸿蒙交付需要单独记录环境、插件和构建阻塞。
|
||||
|
||||
## 22. 推荐落地顺序
|
||||
|
||||
1. 初始化 Flutter 工程,建立 `core` 与 `features` 目录。
|
||||
2. 实现 `ApiClient`、`CredentialStore`、`ErrorMapper`。
|
||||
3. 实现 `AuthRepository` 和登录页。
|
||||
4. 实现仓库列表和最近仓库。
|
||||
5. 实现工单列表、筛选和分页。
|
||||
6. 实现工单详情、评论列表和 Markdown 展示。
|
||||
7. 实现创建、评论、关闭、重开。
|
||||
8. 补充通知和体验增强。
|
||||
9. 完成 Android/iOS 构建配置。
|
||||
10. 执行鸿蒙 OpenHarmony Spike 并输出兼容性报告。
|
||||
|
||||
## 23. 参考
|
||||
|
||||
- 产品需求:[docs/product-requirements.md](product-requirements.md)
|
||||
- Flutter 官方支持平台:https://docs.flutter.dev/reference/supported-platforms
|
||||
- OpenHarmony-SIG Flutter 兼容扩展:https://gitee.com/openharmony-sig/flutter_flutter/blob/master/README.en.md
|
||||
- Gitea API Swagger:`http://111.228.39.103:3000/swagger.v1.json`
|
||||
@@ -0,0 +1,90 @@
|
||||
# iOS 打包说明
|
||||
|
||||
## 当前限制
|
||||
|
||||
iOS / IPA 打包必须在 macOS 上完成,并且需要:
|
||||
|
||||
- Xcode
|
||||
- CocoaPods
|
||||
- Flutter macOS 环境
|
||||
- Apple Developer 账号和证书,用于真机安装、TestFlight 或 App Store
|
||||
|
||||
当前电脑是 Windows,无法直接生成可安装的 iOS 包。
|
||||
|
||||
## 项目配置
|
||||
|
||||
当前 iOS 工程已经生成:
|
||||
|
||||
- Xcode workspace:`ios/Runner.xcworkspace`
|
||||
- Bundle ID:`com.example.gitea.giteaIssueManager`
|
||||
- 显示名:`Gitea Issue Manager`
|
||||
- 当前 HTTP 内测地址 `111.228.39.103` 已加入 ATS 例外。
|
||||
|
||||
正式发布前建议:
|
||||
|
||||
- 将 Gitea 切换为 HTTPS。
|
||||
- 移除 `ios/Runner/Info.plist` 中的 HTTP ATS 例外。
|
||||
- 将 Bundle ID 改成你的公司域名,例如 `com.yourcompany.giteaIssues`。
|
||||
|
||||
## 在 Mac 上生成未签名 IPA
|
||||
|
||||
未签名 IPA 不能直接安装到普通 iPhone,只适合交给后续签名流程。
|
||||
|
||||
```bash
|
||||
chmod +x scripts/build_ios_package.sh
|
||||
./scripts/build_ios_package.sh
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
```text
|
||||
build/ios/ipa/Runner-unsigned.ipa
|
||||
```
|
||||
|
||||
## 在 Mac 上生成可签名 IPA
|
||||
|
||||
先在 Xcode 中打开:
|
||||
|
||||
```bash
|
||||
open ios/Runner.xcworkspace
|
||||
```
|
||||
|
||||
然后配置:
|
||||
|
||||
- Signing & Capabilities
|
||||
- Team
|
||||
- Bundle Identifier
|
||||
- Provisioning Profile
|
||||
|
||||
也可以通过环境变量执行脚本:
|
||||
|
||||
```bash
|
||||
export APPLE_TEAM_ID=你的TeamID
|
||||
export EXPORT_METHOD=development
|
||||
export GITEA_BASE_URL=https://你的gitea域名
|
||||
./scripts/build_ios_package.sh
|
||||
```
|
||||
|
||||
常见 `EXPORT_METHOD`:
|
||||
|
||||
- `development`:开发真机测试
|
||||
- `ad-hoc`:指定设备分发
|
||||
- `app-store`:TestFlight / App Store
|
||||
|
||||
签名 IPA 默认输出到:
|
||||
|
||||
```text
|
||||
build/ios/ipa/
|
||||
```
|
||||
|
||||
## 常见问题
|
||||
|
||||
如果 CocoaPods 依赖未安装:
|
||||
|
||||
```bash
|
||||
cd ios
|
||||
pod install
|
||||
cd ..
|
||||
```
|
||||
|
||||
如果真机无法访问当前 HTTP Gitea 地址,请确认 `Info.plist` 中仍包含 `111.228.39.103` 的 ATS 例外。正式环境请改用 HTTPS。
|
||||
@@ -0,0 +1,513 @@
|
||||
# Gitea 工单管理系统需求分析
|
||||
|
||||
## 1. 背景与目标
|
||||
|
||||
本项目计划使用 Flutter 开发一个面向移动端的 Gitea 工单管理系统,默认连接到:
|
||||
|
||||
- Gitea 地址:`http://111.228.39.103:3000/`
|
||||
- API Base URL:`http://111.228.39.103:3000/api/v1`
|
||||
- 已验证 Gitea 版本:`1.26.2`
|
||||
|
||||
产品目标是让研发、测试、项目负责人能够在 Android、iOS 和鸿蒙设备上快速查看、创建、跟进和关闭 Gitea Issue,减少移动场景下只能打开网页处理工单的不便。
|
||||
|
||||
## 2. 产品定位
|
||||
|
||||
这是一个“移动优先的 Gitea Issue 工作台”,不是完整的 Gitea 客户端。
|
||||
|
||||
核心价值:
|
||||
|
||||
- 快速进入与自己相关的仓库和工单。
|
||||
- 支持常见工单动作:查看、筛选、创建、评论、关闭、重新打开。
|
||||
- 在手机上保持清晰、低干扰、高效率的处理体验。
|
||||
- 业务代码尽量保持纯 Flutter/Dart,降低 Android、iOS、鸿蒙多端适配成本。
|
||||
|
||||
## 3. 目标用户
|
||||
|
||||
| 用户角色 | 主要诉求 |
|
||||
| --- | --- |
|
||||
| 研发成员 | 查看分配给自己的问题、评论进展、关闭已完成工单 |
|
||||
| 测试人员 | 创建缺陷、补充复现信息、跟踪状态变化 |
|
||||
| 项目负责人 | 按仓库、标签、里程碑查看工作进度 |
|
||||
| 新成员 | 快速找到参与项目的仓库和待处理事项 |
|
||||
| 仓库管理员 | 需要在移动端处理标签、里程碑、权限相关问题,但这部分不是 MVP 核心 |
|
||||
|
||||
## 4. 已确认接口能力
|
||||
|
||||
从目标 Gitea 实例的 Swagger/OpenAPI 中确认,当前版本提供以下工单相关接口:
|
||||
|
||||
| 能力 | Gitea API |
|
||||
| --- | --- |
|
||||
| 版本探测 | `GET /version` |
|
||||
| 仓库搜索 | `GET /repos/search` |
|
||||
| 当前用户仓库 | `GET /user/repos` |
|
||||
| 跨仓库工单搜索 | `GET /repos/issues/search` |
|
||||
| 仓库工单列表 | `GET /repos/{owner}/{repo}/issues` |
|
||||
| 创建工单 | `POST /repos/{owner}/{repo}/issues` |
|
||||
| 工单详情 | `GET /repos/{owner}/{repo}/issues/{index}` |
|
||||
| 编辑工单 | `PATCH /repos/{owner}/{repo}/issues/{index}` |
|
||||
| 工单评论 | `GET/POST /repos/{owner}/{repo}/issues/{index}/comments` |
|
||||
| 标签 | `GET /repos/{owner}/{repo}/labels` |
|
||||
| 里程碑 | `GET /repos/{owner}/{repo}/milestones` |
|
||||
| 通知 | `GET/PUT /notifications` |
|
||||
|
||||
认证建议:
|
||||
|
||||
- 用户入口使用 Gitea 账号密码登录。
|
||||
- 客户端登录时先用 Basic Auth 校验账号密码,再尝试调用 `POST /users/{username}/tokens` 创建访问令牌。
|
||||
- 如果 Gitea 禁止 API 创建 token 或受 2FA 策略限制,客户端可退回 Basic Auth 会话。
|
||||
- API 请求优先使用 `Authorization: token <token>` 请求头;退回 Basic Auth 时使用 `Authorization: Basic <base64(username:password)>`。
|
||||
- 不建议把 token 或密码放到 URL 查询参数中。
|
||||
- 当前服务是 HTTP 地址,正式使用前强烈建议启用 HTTPS,否则 token 会明文传输。
|
||||
|
||||
## 5. 平台范围
|
||||
|
||||
| 平台 | MVP 支持策略 |
|
||||
| --- | --- |
|
||||
| Android | Flutter 官方稳定支持,MVP 必须构建并验证 |
|
||||
| iOS | Flutter 官方稳定支持,MVP 必须构建配置;真机签名与上架证书需单独准备 |
|
||||
| 鸿蒙 / OpenHarmony | 通过 OpenHarmony-SIG Flutter 适配 SDK 验证,作为高风险适配项提前做 Spike |
|
||||
|
||||
重要约束:
|
||||
|
||||
- Flutter 官方支持的平台列表包含 Android、iOS、Web、Windows、macOS、Linux 等,未把 HarmonyOS/OpenHarmony 作为官方稳定目标平台。
|
||||
- 鸿蒙支持依赖 OpenHarmony-SIG 的 Flutter 兼容扩展及 DevEco Studio 环境,插件兼容性需要逐项验证。
|
||||
- 为降低鸿蒙适配风险,MVP 阶段应尽量减少原生插件依赖,优先使用纯 Dart 能力。
|
||||
|
||||
## 6. MVP 范围
|
||||
|
||||
MVP 必做:
|
||||
|
||||
1. 登录与会话
|
||||
- 输入或预置 Gitea 地址。
|
||||
- 输入 Gitea 账号和密码。
|
||||
- 校验账号密码是否可用。
|
||||
- 优先换取并安全保存访问令牌;无法换取时安全保存 Basic Auth 凭据。
|
||||
- 支持退出登录。
|
||||
|
||||
2. 仓库选择
|
||||
- 显示当前用户可访问仓库。
|
||||
- 支持搜索仓库。
|
||||
- 支持最近访问或收藏仓库。
|
||||
|
||||
3. 工单列表
|
||||
- 支持按仓库查看工单。
|
||||
- 支持 open / closed 状态筛选。
|
||||
- 支持关键词搜索。
|
||||
- 支持标签、里程碑、指派人基础筛选。
|
||||
- 支持下拉刷新和分页加载。
|
||||
|
||||
4. 工单详情
|
||||
- 显示标题、编号、状态、作者、指派人、标签、里程碑、创建时间、更新时间。
|
||||
- 显示正文 Markdown。
|
||||
- 显示评论列表。
|
||||
- 支持刷新详情。
|
||||
|
||||
5. 工单操作
|
||||
- 创建工单。
|
||||
- 创建工单时选择已有标签。
|
||||
- 在工单详情中添加或移除已有标签。
|
||||
- 编辑标题和正文。
|
||||
- 添加评论。
|
||||
- 关闭工单。
|
||||
- 重新打开工单。
|
||||
|
||||
6. 基础质量
|
||||
- 统一错误提示。
|
||||
- 网络超时和重试。
|
||||
- Token 失效时引导重新登录。
|
||||
- Android 和 iOS 可构建。
|
||||
- 鸿蒙完成环境 Spike 和兼容性报告。
|
||||
|
||||
MVP 暂不做:
|
||||
|
||||
- Pull Request 审查。
|
||||
- 代码浏览、提交记录、分支管理。
|
||||
- Release、Wiki、Packages。
|
||||
- 管理员后台能力。
|
||||
- Webhook 服务端推送。
|
||||
- 离线编辑和冲突合并。
|
||||
- 附件上传、图片预览、语音、扫码等依赖较多原生插件的能力。
|
||||
- 完整项目看板或甘特图。
|
||||
|
||||
## 7. Epic 拆分
|
||||
|
||||
| Epic | 名称 | 目标 | 估算 |
|
||||
| --- | --- | --- | --- |
|
||||
| E1 | 认证与安全会话 | 用户能安全连接 Gitea 并保持登录状态 | 13 pts |
|
||||
| E2 | 仓库上下文 | 用户能找到并进入可访问仓库 | 8 pts |
|
||||
| E3 | 工单发现 | 用户能按条件找到要处理的工单 | 13 pts |
|
||||
| E4 | 工单详情与协作 | 用户能查看完整上下文并参与讨论 | 13 pts |
|
||||
| E5 | 工单变更 | 用户能创建、编辑、关闭和重开工单 | 13 pts |
|
||||
| E6 | 通知与个人工作流 | 用户能关注未读事项和最近工作 | 8 pts |
|
||||
| E7 | 跨端交付 | Android、iOS、鸿蒙构建路径清晰可验证 | 13 pts |
|
||||
| E8 | 体验打磨 | 提升性能、错误恢复、可访问性和稳定性 | 8 pts |
|
||||
|
||||
## 8. 用户故事与验收标准
|
||||
|
||||
### US-001 登录 Gitea
|
||||
|
||||
优先级:High
|
||||
估算:5 pts
|
||||
|
||||
作为研发成员,我想用 Gitea 账号密码登录移动端客户端,以便安全访问我有权限的仓库和工单。
|
||||
|
||||
验收标准:
|
||||
|
||||
- Given 用户输入默认 Gitea 地址和有效账号密码,When 点击登录,Then 客户端调用用户校验接口并进入主页。
|
||||
- Given Gitea 支持创建访问令牌,When 登录成功,Then 客户端创建并保存 API token 用于后续请求。
|
||||
- Given Gitea 不允许创建访问令牌但 Basic Auth 可用,When 登录成功,Then 客户端保存 Basic Auth 凭据并进入主页。
|
||||
- Given 账号或密码无效,When 用户点击登录,Then 显示明确错误,并停留在登录页。
|
||||
- Given 网络不可达,When 用户点击登录,Then 显示网络失败提示和重试入口。
|
||||
- Given 登录成功,When 用户重新打开应用,Then 应保持登录状态。
|
||||
- Given 用户选择退出登录,When 操作确认,Then 本地认证信息被清除并返回登录页。
|
||||
|
||||
INVEST:独立、可协商、有用户价值、可估算、小于 8 点、可测试。
|
||||
|
||||
### US-002 查看可访问仓库
|
||||
|
||||
优先级:High
|
||||
估算:3 pts
|
||||
|
||||
作为研发成员,我想查看自己可访问的仓库列表,以便选择要处理工单的项目。
|
||||
|
||||
验收标准:
|
||||
|
||||
- Given 用户已登录,When 进入主页,Then 显示当前用户可访问仓库。
|
||||
- Given 仓库较多,When 用户滚动到底部,Then 支持分页加载更多。
|
||||
- Given 用户输入关键词,When 搜索仓库,Then 只显示匹配仓库。
|
||||
- Given API 返回空列表,When 页面加载完成,Then 显示空状态而不是错误页。
|
||||
|
||||
INVEST:通过。
|
||||
|
||||
### US-003 按仓库查看工单列表
|
||||
|
||||
优先级:High
|
||||
估算:5 pts
|
||||
|
||||
作为测试人员,我想查看某个仓库的工单列表,以便快速找到需要跟进的问题。
|
||||
|
||||
验收标准:
|
||||
|
||||
- Given 用户选择仓库,When 进入工单页,Then 默认显示 open 工单。
|
||||
- Given 用户切换 open / closed,When 状态变化,Then 列表按所选状态刷新。
|
||||
- Given 工单较多,When 用户滚动到底部,Then 分页加载下一页。
|
||||
- Given 用户下拉刷新,When API 请求成功,Then 列表显示最新数据。
|
||||
- Given API 请求失败,When 加载失败,Then 保留可用旧数据并显示错误提示。
|
||||
|
||||
INVEST:通过。
|
||||
|
||||
### US-004 搜索和筛选工单
|
||||
|
||||
优先级:High
|
||||
估算:5 pts
|
||||
|
||||
作为项目负责人,我想按关键词、标签、里程碑、指派人筛选工单,以便快速定位当前关注范围。
|
||||
|
||||
验收标准:
|
||||
|
||||
- Given 用户输入关键词,When 提交搜索,Then 工单列表按关键词刷新。
|
||||
- Given 用户选择标签,When 应用筛选,Then 只显示包含该标签的工单。
|
||||
- Given 用户选择里程碑,When 应用筛选,Then 只显示该里程碑下的工单。
|
||||
- Given 用户清空筛选,When 返回列表,Then 恢复默认 open 工单列表。
|
||||
- Given 筛选组合无结果,When 请求完成,Then 显示清晰空状态。
|
||||
|
||||
INVEST:通过。
|
||||
|
||||
### US-005 查看工单详情
|
||||
|
||||
优先级:High
|
||||
估算:5 pts
|
||||
|
||||
作为研发成员,我想查看工单正文、状态和评论,以便理解问题背景并决定下一步动作。
|
||||
|
||||
验收标准:
|
||||
|
||||
- Given 用户点击工单,When 详情接口返回成功,Then 显示工单标题、编号、状态、标签、作者、指派人、里程碑和时间信息。
|
||||
- Given 工单正文包含 Markdown,When 页面渲染,Then 应以可读格式展示。
|
||||
- Given 工单有评论,When 页面加载,Then 按时间顺序显示评论。
|
||||
- Given 用户下拉刷新,When 请求成功,Then 详情和评论更新。
|
||||
- Given 用户无权限或工单不存在,When 打开详情,Then 显示无权限或不存在提示。
|
||||
|
||||
INVEST:通过。
|
||||
|
||||
### US-006 创建工单
|
||||
|
||||
优先级:High
|
||||
估算:5 pts
|
||||
|
||||
作为测试人员,我想在移动端创建 Gitea 工单,以便现场记录缺陷或任务。
|
||||
|
||||
验收标准:
|
||||
|
||||
- Given 用户在仓库中点击创建,When 输入标题和正文并提交,Then 创建成功并进入新工单详情。
|
||||
- Given 标题为空,When 用户提交,Then 阻止提交并提示标题必填。
|
||||
- Given 用户选择标签或里程碑,When 创建成功,Then 新工单包含对应元数据。
|
||||
- Given API 返回权限错误,When 提交失败,Then 提示用户无创建权限。
|
||||
- Given 网络失败,When 提交失败,Then 保留表单内容以便重试。
|
||||
|
||||
INVEST:通过。
|
||||
|
||||
### US-007 评论工单
|
||||
|
||||
优先级:High
|
||||
估算:3 pts
|
||||
|
||||
作为研发成员,我想给工单添加评论,以便同步处理进展和沟通问题。
|
||||
|
||||
验收标准:
|
||||
|
||||
- Given 用户在详情页输入评论,When 点击发送,Then 评论成功后出现在评论列表底部。
|
||||
- Given 评论为空,When 用户点击发送,Then 阻止提交并提示内容必填。
|
||||
- Given 评论发送失败,When API 返回错误,Then 保留输入内容并显示失败原因。
|
||||
- Given 发送中,When 请求未完成,Then 发送按钮进入不可重复点击状态。
|
||||
|
||||
INVEST:通过。
|
||||
|
||||
### US-007A 编辑工单标签
|
||||
|
||||
优先级:High
|
||||
估算:3 pts
|
||||
|
||||
作为研发成员,我想在工单详情里添加或移除已有标签,以便准确标记问题类型和处理阶段。
|
||||
|
||||
验收标准:
|
||||
|
||||
- Given 仓库存在标签,When 用户打开工单详情并点击编辑标签,Then 显示当前仓库可用标签列表。
|
||||
- Given 用户勾选或取消标签,When 点击保存,Then 工单标签被更新。
|
||||
- Given 用户无权限修改标签,When 保存失败,Then 显示权限不足提示。
|
||||
- Given 标签更新成功,When 返回工单列表,Then 工单标签状态保持一致。
|
||||
|
||||
INVEST:通过。
|
||||
|
||||
### US-008 关闭和重新打开工单
|
||||
|
||||
优先级:High
|
||||
估算:3 pts
|
||||
|
||||
作为研发成员,我想关闭已解决工单或重新打开未解决工单,以便维护准确的工作状态。
|
||||
|
||||
验收标准:
|
||||
|
||||
- Given 工单为 open,When 用户点击关闭并确认,Then 工单状态变为 closed。
|
||||
- Given 工单为 closed,When 用户点击重新打开并确认,Then 工单状态变为 open。
|
||||
- Given 用户无权限修改状态,When 操作失败,Then 显示权限不足提示。
|
||||
- Given 状态修改成功,When 返回列表,Then 列表状态与详情一致。
|
||||
|
||||
INVEST:通过。
|
||||
|
||||
### US-009 查看通知
|
||||
|
||||
优先级:Medium
|
||||
估算:3 pts
|
||||
|
||||
作为项目成员,我想查看 Gitea 未读通知,以便及时处理与我相关的工单变化。
|
||||
|
||||
验收标准:
|
||||
|
||||
- Given 用户已登录,When 进入通知页,Then 显示通知线程列表。
|
||||
- Given 用户点击通知,When 目标是工单,Then 跳转到对应工单详情。
|
||||
- Given 用户点击标记已读,When 请求成功,Then 通知从未读列表中移除或状态更新。
|
||||
- Given 通知接口不可用,When 加载失败,Then 显示错误提示而不影响工单列表使用。
|
||||
|
||||
INVEST:通过。
|
||||
|
||||
### US-010 跨平台构建验证
|
||||
|
||||
优先级:High
|
||||
估算:8 pts
|
||||
|
||||
作为项目负责人,我想确认 Android、iOS、鸿蒙三端都有明确构建路径,以便项目不会在交付阶段才暴露平台阻塞。
|
||||
|
||||
验收标准:
|
||||
|
||||
- Given Flutter 项目代码完成,When 执行 Android 构建,Then 能生成 APK 或 AAB。
|
||||
- Given iOS 构建环境和证书具备,When 执行 iOS 构建,Then 能生成可签名包。
|
||||
- Given OpenHarmony Flutter SDK 和 DevEco Studio 环境具备,When 执行鸿蒙构建 Spike,Then 输出可运行结果或明确阻塞清单。
|
||||
- Given 引入第三方插件,When 做平台兼容检查,Then 每个插件都有 Android、iOS、鸿蒙兼容结论。
|
||||
- Given 当前 Gitea 是 HTTP 地址,When 移动端请求 API,Then Android/iOS/鸿蒙网络安全配置风险被记录并处理。
|
||||
|
||||
INVEST:通过,但鸿蒙部分风险最高,应尽早执行。
|
||||
|
||||
## 9. Backlog 优先级
|
||||
|
||||
| 优先级 | 条目 | 原因 |
|
||||
| --- | --- | --- |
|
||||
| Critical | HTTPS 或 HTTP 明文访问策略 | 直接影响密码、token 安全和移动端网络配置 |
|
||||
| High | 登录、仓库、工单列表、详情、创建、评论、状态变更 | 构成最小可用闭环 |
|
||||
| High | Android/iOS 构建与鸿蒙 Spike | 用户明确要求三端支持 |
|
||||
| Medium | 通知、最近访问、收藏仓库、筛选增强 | 提升日常效率 |
|
||||
| Medium | 编辑标签、里程碑、指派人 | 常用但可在基本闭环后补齐 |
|
||||
| Low | 附件上传、图片预览、扫码登录、推送通知 | 依赖更多原生插件和服务端能力 |
|
||||
| Low | 看板、报表、时间追踪、依赖关系 | 更偏高级项目管理 |
|
||||
|
||||
## 10. 建议迭代计划
|
||||
|
||||
以下计划假设 2 周一个 Sprint,移动端 1-2 名开发,初始速度按 20-24 点估算。真实承诺应在确认团队人数、可用时间和设备环境后调整。
|
||||
|
||||
### Sprint 0:项目启动与风险 Spike
|
||||
|
||||
目标:建立工程基础,打通 Gitea API,尽早验证三端风险。
|
||||
|
||||
建议范围:
|
||||
|
||||
- Flutter 工程初始化。
|
||||
- API 客户端基础封装。
|
||||
- Gitea 账号密码校验与 token 换取 Spike。
|
||||
- HTTP 明文请求在 Android/iOS 上的配置验证。
|
||||
- 鸿蒙 OpenHarmony Flutter SDK 环境调研和最小 demo Spike。
|
||||
- 插件候选清单:HTTP、状态管理、安全存储、Markdown 渲染。
|
||||
|
||||
完成标准:
|
||||
|
||||
- 有可运行 demo 能访问 `GET /version` 和 `GET /user`。
|
||||
- 明确账号密码、token 换取和本地认证信息存储方案。
|
||||
- 明确鸿蒙构建环境和阻塞项。
|
||||
|
||||
### Sprint 1:MVP 纵向闭环
|
||||
|
||||
目标:用户能登录、选择仓库、查看工单列表和详情。
|
||||
|
||||
建议范围:
|
||||
|
||||
- US-001 登录 Gitea。
|
||||
- US-002 查看可访问仓库。
|
||||
- US-003 按仓库查看工单列表。
|
||||
- US-005 查看工单详情。
|
||||
|
||||
完成标准:
|
||||
|
||||
- Android 本地可运行。
|
||||
- iOS 工程配置可生成。
|
||||
- 工单详情能显示正文和评论。
|
||||
|
||||
### Sprint 2:工单处理能力
|
||||
|
||||
目标:用户能完成移动端核心工单操作。
|
||||
|
||||
建议范围:
|
||||
|
||||
- US-004 搜索和筛选工单。
|
||||
- US-006 创建工单。
|
||||
- US-007 评论工单。
|
||||
- US-008 关闭和重新打开工单。
|
||||
|
||||
完成标准:
|
||||
|
||||
- 创建、评论、关闭、重开形成完整闭环。
|
||||
- 失败场景保留用户输入。
|
||||
- 核心接口有单元测试或集成测试覆盖。
|
||||
|
||||
### Sprint 3:通知、跨端与稳定性
|
||||
|
||||
目标:提升可用性并进入多端验证。
|
||||
|
||||
建议范围:
|
||||
|
||||
- US-009 查看通知。
|
||||
- US-010 跨平台构建验证。
|
||||
- 最近访问仓库。
|
||||
- 错误提示统一。
|
||||
- 页面性能和加载状态优化。
|
||||
- Android/iOS 打包脚本。
|
||||
- 鸿蒙 Spike 结论转为构建说明或适配任务。
|
||||
|
||||
完成标准:
|
||||
|
||||
- Android 可打包。
|
||||
- iOS 构建路径清晰。
|
||||
- 鸿蒙有可执行构建说明或明确技术阻塞。
|
||||
|
||||
## 11. Definition of Ready
|
||||
|
||||
故事进入 Sprint 前应满足:
|
||||
|
||||
- 用户故事按 “作为...我想...以便...” 描述。
|
||||
- 至少 3 条可测试验收标准。
|
||||
- 接口、权限和异常路径已确认。
|
||||
- 估算不超过 8 点;超过则拆分。
|
||||
- 依赖项已标注,尤其是鸿蒙环境、证书、HTTPS、Gitea 权限。
|
||||
|
||||
## 12. Definition of Done
|
||||
|
||||
故事完成需满足:
|
||||
|
||||
- 功能代码完成并通过 Review。
|
||||
- 验收标准逐条验证。
|
||||
- 关键逻辑有测试覆盖。
|
||||
- Android 至少完成本地运行验证。
|
||||
- 涉及平台能力时记录 Android、iOS、鸿蒙兼容性。
|
||||
- 不在日志、URL、错误信息中泄露 token。
|
||||
- 用户可见错误有中文提示。
|
||||
- 文档或 README 更新。
|
||||
- 无 Critical 或 High 级未处理缺陷。
|
||||
|
||||
## 13. 非功能需求
|
||||
|
||||
安全:
|
||||
|
||||
- 认证信息必须安全存储。
|
||||
- 禁止把 token 或密码放进 URL。
|
||||
- 默认不打印请求头和敏感响应。
|
||||
- 当前 HTTP 服务用于内测可以临时放行;生产必须切换 HTTPS。
|
||||
|
||||
性能:
|
||||
|
||||
- 首屏已有会话时 1 秒内进入主页骨架屏。
|
||||
- 常规网络下,50 条工单列表加载 p95 小于 2 秒。
|
||||
- 列表滚动保持流畅,分页加载不阻塞当前内容浏览。
|
||||
|
||||
可靠性:
|
||||
|
||||
- 网络失败时保留当前页面数据。
|
||||
- 表单提交失败时保留用户输入。
|
||||
- Token 或 Basic Auth 凭据失效时引导重新登录。
|
||||
|
||||
可用性:
|
||||
|
||||
- 中文界面。
|
||||
- 支持浅色和深色模式。
|
||||
- 工单状态、标签、里程碑要能快速识别。
|
||||
- 手机单手操作优先,常用动作放在详情页底部或明确菜单中。
|
||||
|
||||
可测试性:
|
||||
|
||||
- API 客户端可注入 mock。
|
||||
- 工单列表筛选逻辑可单测。
|
||||
- 登录、创建工单、评论、关闭工单作为集成测试主路径。
|
||||
|
||||
## 14. 主要风险与应对
|
||||
|
||||
| 风险 | 等级 | 影响 | 应对 |
|
||||
| --- | --- | --- | --- |
|
||||
| Gitea 当前使用 HTTP | Critical | 密码和 token 明文传输,iOS/Android 也需额外放行明文请求 | 内测可临时配置,正式环境必须上 HTTPS |
|
||||
| 鸿蒙不是 Flutter 官方稳定平台 | High | 构建链、插件、渲染和调试存在不确定性 | Sprint 0 做 Spike,减少插件依赖,保留独立适配任务 |
|
||||
| Gitea 权限差异 | Medium | 不同用户创建、编辑、关闭权限不同 | 所有写操作处理 401/403/404/422 |
|
||||
| Token 获取流程 | Medium | 用户不知道如何生成 token | 登录页提供简短指引,后续可补网页登录跳转 |
|
||||
| 附件和推送需求扩张 | Medium | 引入原生插件和服务端组件,影响鸿蒙适配 | 放到 MVP 后,逐项评估 |
|
||||
| 私有仓库与组织仓库较多 | Medium | 列表和搜索性能下降 | 分页、缓存、最近访问 |
|
||||
|
||||
## 15. 开放问题
|
||||
|
||||
1. 是否可以为 Gitea 配置 HTTPS 域名或反向代理?
|
||||
2. Gitea 是否允许通过 API 创建访问令牌?如果不允许,是否接受客户端保存 Basic Auth 凭据?
|
||||
3. 是否需要管理员能力,例如创建标签、创建里程碑、修改指派人?
|
||||
4. 是否要求工单附件上传和图片预览?
|
||||
5. 鸿蒙目标是 HarmonyOS NEXT / OpenHarmony 哪个版本?是否已有 DevEco Studio、签名证书和测试机?
|
||||
6. 是否需要推送通知?如果需要,是否有可部署的中间服务用于对接 Gitea Webhook 和移动推送通道?
|
||||
7. 是否需要多 Gitea 实例切换,还是只固定当前地址?
|
||||
|
||||
## 16. 推荐技术取向
|
||||
|
||||
- 架构:Flutter 分层架构,UI、状态、Repository、API Client 分离。
|
||||
- 状态管理:优先 Riverpod 或 Bloc,选择团队更熟悉的一种。
|
||||
- 网络:统一 API Client,集中处理 token、错误、分页和超时。
|
||||
- 存储:token 使用安全存储;非敏感缓存可用本地轻量存储。
|
||||
- Markdown:选择 Android、iOS、鸿蒙兼容性更好的 Flutter/Dart 方案。
|
||||
- 适配策略:先完成 Android/iOS 主路径,再用同一业务代码执行鸿蒙构建 Spike。
|
||||
- 插件策略:每引入一个插件,都在 backlog 标注三端兼容结论。
|
||||
|
||||
## 17. 参考资料
|
||||
|
||||
- Flutter 官方支持平台文档:https://docs.flutter.dev/reference/supported-platforms
|
||||
- OpenHarmony-SIG Flutter 兼容扩展:https://gitee.com/openharmony-sig/flutter_flutter/blob/master/README.en.md
|
||||
- Gitea API Swagger:`http://111.228.39.103:3000/swagger.v1.json`
|
||||
Reference in New Issue
Block a user