@robsun/create-keystone-app 0.2.15 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +46 -44
- package/dist/create-keystone-app.js +347 -10
- package/dist/create-module.js +1217 -1187
- package/package.json +1 -1
- package/template/.claude/skills/keystone-implement/SKILL.md +113 -0
- package/template/.claude/skills/keystone-implement/references/CHECKLIST.md +91 -0
- package/template/.claude/skills/keystone-implement/references/PATTERNS.md +1088 -0
- package/template/.claude/skills/keystone-implement/references/SCHEMA.md +135 -0
- package/template/.claude/skills/keystone-implement/references/TESTING.md +231 -0
- package/template/.claude/skills/keystone-requirements/SKILL.md +296 -0
- package/template/.claude/skills/keystone-requirements/references/CONFIRM_TEMPLATE.md +170 -0
- package/template/.claude/skills/keystone-requirements/references/SCHEMA.md +135 -0
- package/template/.eslintrc.js +3 -0
- package/template/.github/workflows/ci.yml +30 -0
- package/template/.github/workflows/release.yml +32 -0
- package/template/.golangci.yml +11 -0
- package/template/README.md +82 -81
- package/template/apps/server/README.md +8 -0
- package/template/apps/server/cmd/server/main.go +27 -185
- package/template/apps/server/config.example.yaml +31 -1
- package/template/apps/server/config.yaml +31 -1
- package/template/apps/server/go.mod +60 -18
- package/template/apps/server/go.sum +183 -31
- package/template/apps/server/internal/frontend/embed.go +3 -8
- package/template/apps/server/internal/modules/example/README.md +18 -0
- package/template/apps/server/internal/modules/example/api/handler/handler_test.go +9 -0
- package/template/apps/server/internal/modules/example/api/handler/item_handler.go +468 -165
- package/template/apps/server/internal/modules/example/bootstrap/seeds/item.go +217 -8
- package/template/apps/server/internal/modules/example/domain/models/item.go +40 -7
- package/template/apps/server/internal/modules/example/domain/service/approval_callback.go +68 -0
- package/template/apps/server/internal/modules/example/domain/service/approval_schema.go +41 -0
- package/template/apps/server/internal/modules/example/domain/service/errors.go +20 -22
- package/template/apps/server/internal/modules/example/domain/service/item_service.go +267 -7
- package/template/apps/server/internal/modules/example/domain/service/item_service_test.go +281 -0
- package/template/apps/server/internal/modules/example/i18n/keys.go +32 -20
- package/template/apps/server/internal/modules/example/i18n/locales/en-US.json +30 -18
- package/template/apps/server/internal/modules/example/i18n/locales/zh-CN.json +30 -18
- package/template/apps/server/internal/modules/example/infra/exporter/item_exporter.go +119 -0
- package/template/apps/server/internal/modules/example/infra/importer/item_importer.go +77 -0
- package/template/apps/server/internal/modules/example/infra/repository/item_repository.go +99 -49
- package/template/apps/server/internal/modules/example/module.go +171 -97
- package/template/apps/server/internal/modules/example/tests/integration_test.go +7 -0
- package/template/apps/server/internal/modules/manifest.go +7 -7
- package/template/apps/web/README.md +4 -2
- package/template/apps/web/package.json +1 -1
- package/template/apps/web/src/app.config.ts +8 -6
- package/template/apps/web/src/index.css +7 -3
- package/template/apps/web/src/main.tsx +2 -5
- package/template/apps/web/src/modules/example/help/en-US/faq.md +27 -0
- package/template/apps/web/src/modules/example/help/en-US/items.md +30 -0
- package/template/apps/web/src/modules/example/help/en-US/overview.md +31 -0
- package/template/apps/web/src/modules/example/help/zh-CN/faq.md +27 -0
- package/template/apps/web/src/modules/example/help/zh-CN/items.md +31 -0
- package/template/apps/web/src/modules/example/help/zh-CN/overview.md +32 -0
- package/template/apps/web/src/modules/example/locales/en-US/example.json +99 -32
- package/template/apps/web/src/modules/example/locales/zh-CN/example.json +85 -18
- package/template/apps/web/src/modules/example/pages/ExampleItemsPage.tsx +840 -237
- package/template/apps/web/src/modules/example/services/exampleItems.ts +79 -8
- package/template/apps/web/src/modules/example/types.ts +14 -1
- package/template/apps/web/src/modules/index.ts +1 -0
- package/template/apps/web/vite.config.ts +9 -3
- package/template/docs/CONVENTIONS.md +17 -17
- package/template/package.json +4 -5
- package/template/pnpm-lock.yaml +76 -5
- package/template/scripts/build.bat +15 -3
- package/template/scripts/build.sh +9 -3
- package/template/scripts/check-help.js +249 -0
- package/template/scripts/compress-assets.js +89 -0
- package/template/scripts/test.bat +23 -0
- package/template/scripts/test.sh +16 -0
- package/template/.claude/skills/keystone-dev/SKILL.md +0 -90
- package/template/.claude/skills/keystone-dev/references/ADVANCED_PATTERNS.md +0 -716
- package/template/.claude/skills/keystone-dev/references/APPROVAL.md +0 -121
- package/template/.claude/skills/keystone-dev/references/CAPABILITIES.md +0 -261
- package/template/.claude/skills/keystone-dev/references/CHECKLIST.md +0 -285
- package/template/.claude/skills/keystone-dev/references/GOTCHAS.md +0 -390
- package/template/.claude/skills/keystone-dev/references/PATTERNS.md +0 -605
- package/template/.claude/skills/keystone-dev/references/TEMPLATES.md +0 -2710
- package/template/.claude/skills/keystone-dev/references/TESTING.md +0 -44
- package/template/.codex/skills/keystone-dev/SKILL.md +0 -90
- package/template/.codex/skills/keystone-dev/references/ADVANCED_PATTERNS.md +0 -716
- package/template/.codex/skills/keystone-dev/references/APPROVAL.md +0 -121
- package/template/.codex/skills/keystone-dev/references/CAPABILITIES.md +0 -261
- package/template/.codex/skills/keystone-dev/references/CHECKLIST.md +0 -285
- package/template/.codex/skills/keystone-dev/references/GOTCHAS.md +0 -390
- package/template/.codex/skills/keystone-dev/references/PATTERNS.md +0 -605
- package/template/.codex/skills/keystone-dev/references/TEMPLATES.md +0 -2710
- package/template/.codex/skills/keystone-dev/references/TESTING.md +0 -44
- package/template/apps/server/internal/app/routes/module_routes.go +0 -16
- package/template/apps/server/internal/app/routes/routes.go +0 -226
- package/template/apps/server/internal/app/startup/startup.go +0 -74
- package/template/apps/server/internal/frontend/handler.go +0 -122
- package/template/apps/server/internal/modules/registry.go +0 -145
- package/template/apps/web/src/modules/example/help/faq.md +0 -23
- package/template/apps/web/src/modules/example/help/items.md +0 -26
- package/template/apps/web/src/modules/example/help/overview.md +0 -25
|
@@ -1,121 +0,0 @@
|
|
|
1
|
-
# Keystone 审批接入流程
|
|
2
|
-
|
|
3
|
-
## 目标
|
|
4
|
-
本文只描述审批接入所需的表单 schema 与 context 映射等开发工作,
|
|
5
|
-
流程节点与审批路径由人工在审批配置页面完成。
|
|
6
|
-
|
|
7
|
-
## 适用场景
|
|
8
|
-
- 新业务需要审批门禁(新业务类型)。
|
|
9
|
-
- 既有业务接入审批门禁(在业务表单提交或关键业务动作前加审批)。
|
|
10
|
-
|
|
11
|
-
## 不在范围
|
|
12
|
-
- 配置审批流程与节点(由人工在流程配置页面完成)。
|
|
13
|
-
- 修改 approval 基础模块或审批页面本身。
|
|
14
|
-
- 改造审批权限体系或审批路由结构。
|
|
15
|
-
|
|
16
|
-
## 开发路径选择
|
|
17
|
-
- Greenfield:新业务从零引入审批。
|
|
18
|
-
- Retrofit:已有业务接入审批。
|
|
19
|
-
|
|
20
|
-
## 开发步骤 A:新业务(Greenfield)
|
|
21
|
-
1) 前端配置 `approval.businessTypes` 标签映射(用于流程配置时选择表单),
|
|
22
|
-
流程配置页面的业务类型选择会作为 schema code 加载表单字段,需保持一致。
|
|
23
|
-
2) 注册表单 schema(条件节点字段来源)。
|
|
24
|
-
3) 在业务提交处创建审批实例,写入 context JSON 用于条件路由。
|
|
25
|
-
4) 绑定 OnApproved/OnRejected 回调更新业务状态。
|
|
26
|
-
5) 按测试清单执行(见 testing.md)。
|
|
27
|
-
|
|
28
|
-
## 开发步骤 B:已有业务接入(Retrofit)
|
|
29
|
-
1) 确认触发点与审批边界(业务表单提交或关键业务动作前)。
|
|
30
|
-
2) 将现有字段映射为 context JSON(供条件节点判断)。
|
|
31
|
-
3) 做幂等:按业务类型 + 业务 ID 查已有实例再创建。
|
|
32
|
-
4) pending 期间阻断业务动作(状态拦截或硬拦截)。
|
|
33
|
-
5) 决定是否回填存量数据(跳过或补建实例)。
|
|
34
|
-
|
|
35
|
-
## 业务侧改动清单
|
|
36
|
-
|
|
37
|
-
后端(业务模块内)
|
|
38
|
-
- 在业务 service/handler 中调用审批服务创建实例(必要时通过 FlowMatcher 选 flow)。
|
|
39
|
-
- 在业务模块注册审批回调,审批通过/驳回时更新业务状态。
|
|
40
|
-
- 需要数据范围过滤时,实现 ApprovalContextResolver 并在应用装配时注入。
|
|
41
|
-
- 如需默认 flow 或 schema,放在业务模块的迁移/seed 中,不改动审批模块。
|
|
42
|
-
|
|
43
|
-
前端(业务模块内)
|
|
44
|
-
- 配置 `approval.businessTypes` 标签映射,保证业务类型显示正确。
|
|
45
|
-
- 流程配置使用已有审批管理页面,不做二次开发。
|
|
46
|
-
|
|
47
|
-
## 权限说明
|
|
48
|
-
使用系统既有审批权限码:
|
|
49
|
-
approval:flow:view|create|edit|delete
|
|
50
|
-
approval:instance:view|approve|cancel
|
|
51
|
-
approval:record:view|export
|
|
52
|
-
|
|
53
|
-
## 流程版本控制
|
|
54
|
-
|
|
55
|
-
审批流程支持版本管理,确保活跃实例不受流程修改影响:
|
|
56
|
-
|
|
57
|
-
**状态生命周期**:`draft` → `published` → `deprecated`
|
|
58
|
-
|
|
59
|
-
- **draft**: 草稿状态,可编辑,不能创建实例
|
|
60
|
-
- **published**: 已发布,不可编辑,可创建实例
|
|
61
|
-
- **deprecated**: 已废弃,不可编辑,不可创建新实例
|
|
62
|
-
|
|
63
|
-
**操作方式**:
|
|
64
|
-
```go
|
|
65
|
-
// 发布流程(draft → published)
|
|
66
|
-
approvalService.PublishFlow(ctx, tenantID, flowID)
|
|
67
|
-
|
|
68
|
-
// 创建新版本(基于已发布流程)
|
|
69
|
-
newFlow, err := approvalService.CreateNewVersion(ctx, tenantID, flowID)
|
|
70
|
-
// 新版本为 draft 状态,ParentFlowID 指向原流程
|
|
71
|
-
|
|
72
|
-
// 废弃旧版本(自动在发布新版本时执行)
|
|
73
|
-
approvalService.DeprecateFlow(ctx, tenantID, flowID)
|
|
74
|
-
```
|
|
75
|
-
|
|
76
|
-
## 回调重试机制
|
|
77
|
-
|
|
78
|
-
审批回调(OnApproved/OnRejected)支持自动重试:
|
|
79
|
-
|
|
80
|
-
```go
|
|
81
|
-
import approval "github.com/robsuncn/keystone/domain/approval/service"
|
|
82
|
-
|
|
83
|
-
// 配置重试策略
|
|
84
|
-
config := approval.RetryConfig{
|
|
85
|
-
MaxRetries: 3, // 最大重试次数
|
|
86
|
-
InitialBackoff: time.Second, // 初始退避时间
|
|
87
|
-
MaxBackoff: 30 * time.Second, // 最大退避时间
|
|
88
|
-
BackoffFactor: 2.0, // 退避倍数(指数退避)
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
// 包装回调
|
|
92
|
-
retryable := approval.NewRetryableCallback(myCallback, config)
|
|
93
|
-
|
|
94
|
-
// 或使用异步执行器
|
|
95
|
-
executor := approval.NewAsyncCallbackExecutor(registry, config, 100)
|
|
96
|
-
```
|
|
97
|
-
|
|
98
|
-
## 集成自检
|
|
99
|
-
- Flow code <= 50 且使用大写;business type 用短字符串。
|
|
100
|
-
- approve/reject/cancel 使用 version 做乐观锁。
|
|
101
|
-
- 变更接口时更新 OpenAPI 合同。
|
|
102
|
-
- 新增默认流程时补迁移或 seed。
|
|
103
|
-
- **新流程从 draft 开始,发布后才能创建实例**。
|
|
104
|
-
- **使用 RetryableCallback 包装业务回调以增强可靠性**。
|
|
105
|
-
|
|
106
|
-
测试清单见 [testing.md](testing.md)。
|
|
107
|
-
|
|
108
|
-
## 参考入口
|
|
109
|
-
|
|
110
|
-
后端
|
|
111
|
-
- domain/approval/service/service.go
|
|
112
|
-
- domain/approval/service/flow_matcher.go
|
|
113
|
-
- domain/approval/service/callback_registry.go
|
|
114
|
-
- api/routes/approval_routes.go
|
|
115
|
-
- contracts/011-approvals/approvals-api.yaml
|
|
116
|
-
|
|
117
|
-
前端
|
|
118
|
-
- packages/keystone-web-core/src/platform/config/appConfig.ts
|
|
119
|
-
- packages/keystone-web-core/src/platform/core/approval/services/approvalService.ts
|
|
120
|
-
- packages/keystone-web-core/src/platform/core/approval/stores/approvalStore.ts
|
|
121
|
-
- packages/keystone-web-core/src/modules/keystone/approval/pages/*
|
|
@@ -1,261 +0,0 @@
|
|
|
1
|
-
# Keystone 包完整能力清单
|
|
2
|
-
|
|
3
|
-
## 一、前端组件 (@robsun/keystone-web-core)
|
|
4
|
-
|
|
5
|
-
### 1. 应用与路由
|
|
6
|
-
```typescript
|
|
7
|
-
import { KeystoneApp, createKeystoneRouter } from '@robsun/keystone-web-core'
|
|
8
|
-
import { registerModule, registerRoutes, registerMenus, registerPermissions } from '@robsun/keystone-web-core'
|
|
9
|
-
```
|
|
10
|
-
|
|
11
|
-
### 2. 高级 UI 组件
|
|
12
|
-
```typescript
|
|
13
|
-
// 高级表格 - 过滤、分页、行选择、导出
|
|
14
|
-
import { ProTable } from '@robsun/keystone-web-core'
|
|
15
|
-
|
|
16
|
-
// 智能表单 - 自动验证、加载状态
|
|
17
|
-
import { ProForm } from '@robsun/keystone-web-core'
|
|
18
|
-
|
|
19
|
-
// 文件上传 - 拖拽、预览、多文件
|
|
20
|
-
import { FileUpload } from '@robsun/keystone-web-core'
|
|
21
|
-
|
|
22
|
-
// 数据导入向导
|
|
23
|
-
import { DataImporter, ImportWizard, ColumnMapper, ValidationResult } from '@robsun/keystone-web-core'
|
|
24
|
-
|
|
25
|
-
// 数据导出向导
|
|
26
|
-
import { DataExporter, ExportWizard } from '@robsun/keystone-web-core'
|
|
27
|
-
|
|
28
|
-
// 权限守卫
|
|
29
|
-
import { PermissionGuard } from '@robsun/keystone-web-core'
|
|
30
|
-
```
|
|
31
|
-
|
|
32
|
-
### 3. 审批流系统
|
|
33
|
-
```typescript
|
|
34
|
-
import {
|
|
35
|
-
ApprovalFlowEditor, // 流程编辑器
|
|
36
|
-
ApprovalFlowCanvas, // 可视化画布
|
|
37
|
-
ApprovalNodeEditor, // 节点编辑
|
|
38
|
-
ConditionEditor, // 条件编辑
|
|
39
|
-
ApprovalStatus, // 状态展示
|
|
40
|
-
ApprovalAction, // 审批按钮
|
|
41
|
-
ApprovalHistory // 审批历史
|
|
42
|
-
} from '@robsun/keystone-web-core'
|
|
43
|
-
```
|
|
44
|
-
审批流程与接入说明见 [approval.md](approval.md)。
|
|
45
|
-
|
|
46
|
-
### 4. 帮助系统
|
|
47
|
-
```typescript
|
|
48
|
-
import {
|
|
49
|
-
HelpButton, HelpDrawer, HelpSearch, HelpContent,
|
|
50
|
-
useCurrentHelpKey, useHelpSearch
|
|
51
|
-
} from '@robsun/keystone-web-core'
|
|
52
|
-
```
|
|
53
|
-
|
|
54
|
-
### 5. 布局与主题
|
|
55
|
-
```typescript
|
|
56
|
-
import { MainLayout, antdTheme, useLayoutStore } from '@robsun/keystone-web-core'
|
|
57
|
-
```
|
|
58
|
-
|
|
59
|
-
### 6. 认证授权
|
|
60
|
-
```typescript
|
|
61
|
-
import { useAuthStore, useAuth, matchesPermission } from '@robsun/keystone-web-core'
|
|
62
|
-
```
|
|
63
|
-
|
|
64
|
-
### 7. 运行时服务
|
|
65
|
-
```typescript
|
|
66
|
-
import { api, setApiBaseUrl } from '@robsun/keystone-web-core'
|
|
67
|
-
import { createExportJob, createImportJob } from '@robsun/keystone-web-core'
|
|
68
|
-
import { listNotifications, markNotificationRead, fetchUnreadCount } from '@robsun/keystone-web-core'
|
|
69
|
-
import { getJob, listJobs } from '@robsun/keystone-web-core'
|
|
70
|
-
```
|
|
71
|
-
|
|
72
|
-
### 8. 多语言 (i18n)
|
|
73
|
-
```typescript
|
|
74
|
-
// 翻译 Hook
|
|
75
|
-
import { useTranslation } from 'react-i18next'
|
|
76
|
-
|
|
77
|
-
// 使用示例
|
|
78
|
-
const { t } = useTranslation()
|
|
79
|
-
const title = t('module:key') // 命名空间:键
|
|
80
|
-
const description = t('common:actions.save')
|
|
81
|
-
|
|
82
|
-
// 平台多语言配置
|
|
83
|
-
import { getKeystoneConfig } from '@robsun/keystone-web-core'
|
|
84
|
-
const locale = getKeystoneConfig().ui?.i18n?.defaultLocale // 'zh-CN' | 'en-US'
|
|
85
|
-
|
|
86
|
-
// dayjs 本地化同步(自动完成)
|
|
87
|
-
import dayjs from 'dayjs'
|
|
88
|
-
dayjs.locale(getKeystoneConfig().ui?.locale?.dayjs ?? 'zh-cn')
|
|
89
|
-
```
|
|
90
|
-
|
|
91
|
-
**翻译文件组织**:
|
|
92
|
-
```
|
|
93
|
-
src/modules/{module}/locales/
|
|
94
|
-
├── zh-CN/
|
|
95
|
-
│ └── {namespace}.json
|
|
96
|
-
└── en-US/
|
|
97
|
-
└── {namespace}.json
|
|
98
|
-
```
|
|
99
|
-
|
|
100
|
-
**命名空间约定**:
|
|
101
|
-
- `common` - 通用文案(按钮、操作、状态等)
|
|
102
|
-
- `auth` - 认证相关
|
|
103
|
-
- `system` - 系统管理
|
|
104
|
-
- `{module}` - 业务模块专属翻译
|
|
105
|
-
|
|
106
|
-
### 9. Hooks
|
|
107
|
-
```typescript
|
|
108
|
-
import { useListState } from '@robsun/keystone-web-core'
|
|
109
|
-
```
|
|
110
|
-
|
|
111
|
-
### 10. 类型
|
|
112
|
-
```typescript
|
|
113
|
-
import type { ApiResponse, PaginatedData, PaginatedResponse } from '@robsun/keystone-web-core'
|
|
114
|
-
import type { Permission, ModuleName, Action } from '@robsun/keystone-web-core'
|
|
115
|
-
```
|
|
116
|
-
|
|
117
|
-
## 二、后端能力 (Go)
|
|
118
|
-
|
|
119
|
-
### 1. 模块系统
|
|
120
|
-
```go
|
|
121
|
-
type Module interface {
|
|
122
|
-
Name() string
|
|
123
|
-
RegisterRoutes(r *gin.RouterGroup)
|
|
124
|
-
RegisterPermissions() []Permission
|
|
125
|
-
Migrate(db *gorm.DB) error
|
|
126
|
-
Seed(db *gorm.DB) error
|
|
127
|
-
}
|
|
128
|
-
```
|
|
129
|
-
|
|
130
|
-
### 2. 审批引擎
|
|
131
|
-
```go
|
|
132
|
-
// 基础操作
|
|
133
|
-
approvalService.CreateInstance(...)
|
|
134
|
-
approvalService.Approve(...)
|
|
135
|
-
approvalService.Reject(...)
|
|
136
|
-
approvalService.Cancel(...)
|
|
137
|
-
|
|
138
|
-
// 流程版本控制
|
|
139
|
-
approvalService.PublishFlow(...) // draft → published
|
|
140
|
-
approvalService.DeprecateFlow(...) // published → deprecated
|
|
141
|
-
approvalService.CreateNewVersion(...) // 基于现有流程创建新版本
|
|
142
|
-
```
|
|
143
|
-
|
|
144
|
-
**流程状态生命周期**:`draft` → `published` → `deprecated`
|
|
145
|
-
|
|
146
|
-
### 3. 审批回调重试
|
|
147
|
-
```go
|
|
148
|
-
import approval "github.com/robsuncn/keystone/domain/approval/service"
|
|
149
|
-
|
|
150
|
-
// 带重试的回调包装
|
|
151
|
-
config := approval.RetryConfig{
|
|
152
|
-
MaxRetries: 3,
|
|
153
|
-
InitialBackoff: time.Second,
|
|
154
|
-
MaxBackoff: 30 * time.Second,
|
|
155
|
-
BackoffFactor: 2.0,
|
|
156
|
-
}
|
|
157
|
-
retryable := approval.NewRetryableCallback(callback, config)
|
|
158
|
-
|
|
159
|
-
// 异步回调执行器
|
|
160
|
-
executor := approval.NewAsyncCallbackExecutor(registry, config, 100)
|
|
161
|
-
executor.ExecuteApproved(ctx, businessType, tenantID, businessID, approverID)
|
|
162
|
-
|
|
163
|
-
// 监听结果
|
|
164
|
-
for result := range executor.Results() {
|
|
165
|
-
if !result.Success {
|
|
166
|
-
log.Error("callback failed", "error", result.Error)
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
```
|
|
170
|
-
|
|
171
|
-
### 4. Job 队列
|
|
172
|
-
```go
|
|
173
|
-
jobService.CreateJob(...)
|
|
174
|
-
jobService.GetJob(...)
|
|
175
|
-
jobService.ListJobs(...)
|
|
176
|
-
```
|
|
177
|
-
|
|
178
|
-
### 5. 通知服务
|
|
179
|
-
```go
|
|
180
|
-
notificationService.SendNotification(...)
|
|
181
|
-
notificationService.ListNotifications(...)
|
|
182
|
-
notificationService.MarkRead(...)
|
|
183
|
-
```
|
|
184
|
-
|
|
185
|
-
### 6. 权限中间件
|
|
186
|
-
```go
|
|
187
|
-
middleware.RequirePermission("module:resource:action")
|
|
188
|
-
```
|
|
189
|
-
|
|
190
|
-
### 7. 存储服务
|
|
191
|
-
```go
|
|
192
|
-
storageService.Upload(...)
|
|
193
|
-
storageService.Download(...)
|
|
194
|
-
storageService.Delete(...)
|
|
195
|
-
```
|
|
196
|
-
|
|
197
|
-
### 8. 统一分页
|
|
198
|
-
```go
|
|
199
|
-
import "github.com/robsuncn/keystone/infra/pagination"
|
|
200
|
-
|
|
201
|
-
// 解析请求参数
|
|
202
|
-
req := pagination.ParseRequest(c) // 从 Gin context 获取 page, page_size
|
|
203
|
-
|
|
204
|
-
// 方式一:使用 GORM Scope
|
|
205
|
-
db.Scopes(pagination.Scope(req)).Find(&items)
|
|
206
|
-
|
|
207
|
-
// 方式二:一步完成分页查询
|
|
208
|
-
var items []Item
|
|
209
|
-
total, err := pagination.Paginate(db.Where("tenant_id = ?", tenantID), req, &items)
|
|
210
|
-
|
|
211
|
-
// 方式三:带预加载的分页
|
|
212
|
-
total, err := pagination.PaginateWithPreload(db, req, &items, "Category", "Tags")
|
|
213
|
-
|
|
214
|
-
// 构建响应
|
|
215
|
-
resp := pagination.NewResponse(items, total, req)
|
|
216
|
-
// resp.TotalPages(), resp.HasNextPage(), resp.HasPrevPage()
|
|
217
|
-
```
|
|
218
|
-
|
|
219
|
-
### 9. 多语言 (i18n)
|
|
220
|
-
```go
|
|
221
|
-
// 获取当前请求语言环境
|
|
222
|
-
import "github.com/robsuncn/keystone/infra/i18n"
|
|
223
|
-
|
|
224
|
-
locale := i18n.GetLocale(c) // 从请求头或参数获取
|
|
225
|
-
|
|
226
|
-
// 翻译函数
|
|
227
|
-
i18n.T(c, "module.key") // 基础翻译
|
|
228
|
-
i18n.T(c, "module.greeting", "name", "张三") // 带变量
|
|
229
|
-
i18n.Tf(c, "module.count", count) // 复数形式
|
|
230
|
-
|
|
231
|
-
// 错误消息翻译
|
|
232
|
-
return errors.New(i18n.T(c, "module.errors.notFound"))
|
|
233
|
-
```
|
|
234
|
-
|
|
235
|
-
**翻译文件组织**:
|
|
236
|
-
```
|
|
237
|
-
internal/modules/{module}/i18n/
|
|
238
|
-
├── i18n.go # 初始化
|
|
239
|
-
├── keys.go # 翻译键常量
|
|
240
|
-
└── locales/
|
|
241
|
-
├── zh-CN.json
|
|
242
|
-
└── en-US.json
|
|
243
|
-
```
|
|
244
|
-
|
|
245
|
-
**翻译键常量定义**:
|
|
246
|
-
```go
|
|
247
|
-
package i18n
|
|
248
|
-
|
|
249
|
-
const (
|
|
250
|
-
KeyItemCreated = "example.item.created"
|
|
251
|
-
KeyItemNotFound = "example.item.notFound"
|
|
252
|
-
KeyItemInvalid = "example.item.invalid"
|
|
253
|
-
)
|
|
254
|
-
```
|
|
255
|
-
|
|
256
|
-
**模块初始化**:
|
|
257
|
-
```go
|
|
258
|
-
func init() {
|
|
259
|
-
i18n.MustLoadModuleTranslations("example", Translations)
|
|
260
|
-
}
|
|
261
|
-
```
|
|
@@ -1,285 +0,0 @@
|
|
|
1
|
-
# Keystone 模块开发自检清单
|
|
2
|
-
|
|
3
|
-
> 完成开发后逐项检查,确保模块完整且符合规范。
|
|
4
|
-
|
|
5
|
-
## 1. 后端检查
|
|
6
|
-
|
|
7
|
-
### 1.1 文件结构 ✓
|
|
8
|
-
|
|
9
|
-
```
|
|
10
|
-
[ ] module.go - 实现所有 Module 接口方法
|
|
11
|
-
[ ] api/handler/ - Handler 结构体 + CRUD 方法
|
|
12
|
-
[ ] domain/models/ - 模型定义 + TableName()
|
|
13
|
-
[ ] domain/service/ - Service + Input/UpdateInput 类型
|
|
14
|
-
[ ] domain/service/errors.go - I18n 错误定义
|
|
15
|
-
[ ] infra/repository/ - Repository 实现 Service 定义的接口
|
|
16
|
-
[ ] i18n/keys.go - 翻译键常量
|
|
17
|
-
[ ] i18n/i18n.go - RegisterLocales() 函数
|
|
18
|
-
[ ] i18n/locales/ - zh-CN.json + en-US.json
|
|
19
|
-
[ ] bootstrap/migrations/ - Migrate() 函数
|
|
20
|
-
[ ] bootstrap/seeds/ - Seed() 函数(可选)
|
|
21
|
-
```
|
|
22
|
-
|
|
23
|
-
### 1.2 Module 接口 ✓
|
|
24
|
-
|
|
25
|
-
```go
|
|
26
|
-
[ ] Name() - 返回模块名(小写)
|
|
27
|
-
[ ] RegisterRoutes() - 注册 API 路由
|
|
28
|
-
[ ] RegisterModels() - 返回模型列表
|
|
29
|
-
[ ] RegisterPermissions() - 注册菜单 + 操作权限
|
|
30
|
-
[ ] RegisterI18n() - 调用 modulei18n.RegisterLocales()
|
|
31
|
-
[ ] RegisterJobs() - 注册后台任务(可为空)
|
|
32
|
-
[ ] Migrate() - 调用 migrations.Migrate()
|
|
33
|
-
[ ] Seed() - 调用 seeds.Seed()
|
|
34
|
-
[ ] ensureServices() - 延迟初始化 service
|
|
35
|
-
```
|
|
36
|
-
|
|
37
|
-
### 1.3 Handler 检查 ✓
|
|
38
|
-
|
|
39
|
-
```go
|
|
40
|
-
[ ] nil 检查 - if h == nil || h.svc == nil
|
|
41
|
-
[ ] 租户隔离 - tenantID := resolveTenantID(c)
|
|
42
|
-
[ ] 参数绑定 - c.ShouldBindJSON(&input)
|
|
43
|
-
[ ] ID 解析 - hcommon.ParseUintParam(c, "id")
|
|
44
|
-
[ ] I18n 错误处理 - errors.As(err, &i18nErr)
|
|
45
|
-
[ ] 正确 HTTP 状态码 - BadRequest/NotFound/InternalError
|
|
46
|
-
[ ] I18n 响应消息 - response.SuccessI18n/CreatedI18n
|
|
47
|
-
```
|
|
48
|
-
|
|
49
|
-
### 1.4 Service 检查 ✓
|
|
50
|
-
|
|
51
|
-
```go
|
|
52
|
-
[ ] 输入验证 - strings.TrimSpace(), 空值检查
|
|
53
|
-
[ ] 状态验证 - status.IsValid()
|
|
54
|
-
[ ] 租户 ID 设置 - entity.TenantID = tenantID
|
|
55
|
-
[ ] 返回 I18n 错误 - return nil, ErrNameRequired
|
|
56
|
-
```
|
|
57
|
-
|
|
58
|
-
### 1.5 Repository 检查 ✓
|
|
59
|
-
|
|
60
|
-
```go
|
|
61
|
-
[ ] 租户过滤 - WHERE tenant_id = ?
|
|
62
|
-
[ ] 上下文传递 - db.WithContext(ctx)
|
|
63
|
-
[ ] 404 转换 - gorm.ErrRecordNotFound → service.ErrNotFound
|
|
64
|
-
[ ] 排序 - ORDER BY created_at desc
|
|
65
|
-
```
|
|
66
|
-
|
|
67
|
-
### 1.6 模型检查 ✓
|
|
68
|
-
|
|
69
|
-
```go
|
|
70
|
-
[ ] 继承 BaseModel - models.BaseModel
|
|
71
|
-
[ ] GORM 标签 - gorm:"size:200;not null"
|
|
72
|
-
[ ] JSON 标签 - json:"name"
|
|
73
|
-
[ ] TableName() - 返回表名
|
|
74
|
-
[ ] 状态枚举 - type Status string + IsValid()
|
|
75
|
-
```
|
|
76
|
-
|
|
77
|
-
### 1.7 权限注册 ✓
|
|
78
|
-
|
|
79
|
-
```go
|
|
80
|
-
[ ] 菜单权限 - reg.CreateMenuI18n("module:entity", ...)
|
|
81
|
-
[ ] 查看权限 - module:entity:view
|
|
82
|
-
[ ] 管理权限 - module:entity:manage
|
|
83
|
-
[ ] 或细分权限 - :create, :update, :delete, :export, :import
|
|
84
|
-
```
|
|
85
|
-
|
|
86
|
-
### 1.8 翻译文件 ✓
|
|
87
|
-
|
|
88
|
-
```
|
|
89
|
-
[ ] keys.go 常量完整 - 所有消息键都有定义
|
|
90
|
-
[ ] zh-CN.json - 所有键都有中文翻译
|
|
91
|
-
[ ] en-US.json - 所有键都有英文翻译
|
|
92
|
-
[ ] 翻译键格式 - module.entity.action
|
|
93
|
-
```
|
|
94
|
-
|
|
95
|
-
---
|
|
96
|
-
|
|
97
|
-
## 2. 前端检查
|
|
98
|
-
|
|
99
|
-
### 2.1 文件结构 ✓
|
|
100
|
-
|
|
101
|
-
```
|
|
102
|
-
[ ] index.ts - registerRoutes() 调用
|
|
103
|
-
[ ] routes.tsx - 路由 + menu handle + permission
|
|
104
|
-
[ ] types.ts - 实体类型定义
|
|
105
|
-
[ ] services/api.ts - API 调用函数
|
|
106
|
-
[ ] pages/ - 页面组件
|
|
107
|
-
[ ] locales/zh-CN/ - 中文翻译
|
|
108
|
-
[ ] locales/en-US/ - 英文翻译
|
|
109
|
-
```
|
|
110
|
-
|
|
111
|
-
### 2.2 路由检查 ✓
|
|
112
|
-
|
|
113
|
-
```tsx
|
|
114
|
-
[ ] lazyNamed - 按需加载组件
|
|
115
|
-
[ ] withSuspense - Suspense 包装
|
|
116
|
-
[ ] menu.labelKey - 使用翻译键
|
|
117
|
-
[ ] menu.icon - Ant Design 图标
|
|
118
|
-
[ ] menu.permission - 权限码
|
|
119
|
-
[ ] breadcrumbKey - 面包屑翻译键
|
|
120
|
-
[ ] permission - 路由级权限
|
|
121
|
-
[ ] helpKey - 帮助文档键
|
|
122
|
-
```
|
|
123
|
-
|
|
124
|
-
### 2.3 API 服务检查 ✓
|
|
125
|
-
|
|
126
|
-
```typescript
|
|
127
|
-
[ ] 正确导入 api - import { api } from '@robsun/keystone-web-core'
|
|
128
|
-
[ ] 正确类型 - ApiResponse<T>
|
|
129
|
-
[ ] 解构 data.data - const { data } = await api.get<...>(...); return data.data
|
|
130
|
-
[ ] 完整 CRUD - list, get, create, update, delete
|
|
131
|
-
```
|
|
132
|
-
|
|
133
|
-
### 2.4 页面组件检查 ✓
|
|
134
|
-
|
|
135
|
-
```tsx
|
|
136
|
-
[ ] useTranslation - const { t } = useTranslation('module')
|
|
137
|
-
[ ] App.useApp - const { message } = App.useApp()
|
|
138
|
-
[ ] 加载状态 - loading, setLoading
|
|
139
|
-
[ ] 错误处理 - try/catch + message.error
|
|
140
|
-
[ ] Form.useForm - const [form] = Form.useForm<FormValues>()
|
|
141
|
-
[ ] 表单验证 - rules={[{ required: true, ... }]}
|
|
142
|
-
[ ] Modal destroyOnHidden - 不要用 destroyOnClose
|
|
143
|
-
[ ] 确认删除 - Popconfirm
|
|
144
|
-
```
|
|
145
|
-
|
|
146
|
-
### 2.5 翻译文件检查 ✓
|
|
147
|
-
|
|
148
|
-
```json
|
|
149
|
-
[ ] menu - 菜单标签
|
|
150
|
-
[ ] page.title - 页面标题
|
|
151
|
-
[ ] page.createButton - 创建按钮
|
|
152
|
-
[ ] table.* - 表格列标题
|
|
153
|
-
[ ] form.* - 表单标签和占位符
|
|
154
|
-
[ ] status.* - 状态枚举翻译
|
|
155
|
-
[ ] messages.* - 操作反馈消息
|
|
156
|
-
```
|
|
157
|
-
|
|
158
|
-
### 2.6 类型检查 ✓
|
|
159
|
-
|
|
160
|
-
```typescript
|
|
161
|
-
[ ] 实体类型完整 - 所有字段都有定义
|
|
162
|
-
[ ] 状态联合类型 - type Status = 'active' | 'inactive'
|
|
163
|
-
[ ] id 使用 number - 后端是 uint
|
|
164
|
-
[ ] 时间使用 string - ISO 8601 格式
|
|
165
|
-
```
|
|
166
|
-
|
|
167
|
-
---
|
|
168
|
-
|
|
169
|
-
## 3. 注册检查
|
|
170
|
-
|
|
171
|
-
### 3.1 后端注册 ✓
|
|
172
|
-
|
|
173
|
-
```
|
|
174
|
-
[ ] manifest.go 导入 - import xxxmodule "app/internal/modules/xxx"
|
|
175
|
-
[ ] manifest.go 注册 - Register(xxxmodule.NewModule())
|
|
176
|
-
[ ] config.yaml 启用 - modules.enabled 包含模块名
|
|
177
|
-
```
|
|
178
|
-
|
|
179
|
-
### 3.2 前端注册 ✓
|
|
180
|
-
|
|
181
|
-
```
|
|
182
|
-
[ ] main.tsx 导入 - import './modules/xxx'
|
|
183
|
-
[ ] app.config.ts 启用 - modules.enabled 包含模块名
|
|
184
|
-
```
|
|
185
|
-
|
|
186
|
-
---
|
|
187
|
-
|
|
188
|
-
## 4. 质量检查
|
|
189
|
-
|
|
190
|
-
### 4.1 代码检查 ✓
|
|
191
|
-
|
|
192
|
-
```bash
|
|
193
|
-
[ ] go build ./... - 编译通过
|
|
194
|
-
[ ] go test ./... - 测试通过
|
|
195
|
-
[ ] go vet ./... - 无警告
|
|
196
|
-
[ ] pnpm typecheck - TypeScript 类型检查通过
|
|
197
|
-
[ ] pnpm lint - ESLint 检查通过
|
|
198
|
-
```
|
|
199
|
-
|
|
200
|
-
### 4.2 功能验证 ✓
|
|
201
|
-
|
|
202
|
-
```
|
|
203
|
-
[ ] 列表页加载 - 数据正确显示
|
|
204
|
-
[ ] 创建功能 - 表单提交成功
|
|
205
|
-
[ ] 编辑功能 - 数据回填 + 更新成功
|
|
206
|
-
[ ] 删除功能 - 确认后删除成功
|
|
207
|
-
[ ] 分页功能 - 翻页正常(如适用)
|
|
208
|
-
[ ] 权限控制 - 无权限时按钮/菜单隐藏
|
|
209
|
-
[ ] 多语言切换 - 中英文显示正确
|
|
210
|
-
[ ] 错误提示 - 验证失败显示正确消息
|
|
211
|
-
```
|
|
212
|
-
|
|
213
|
-
---
|
|
214
|
-
|
|
215
|
-
## 5. 快速验证命令
|
|
216
|
-
|
|
217
|
-
```bash
|
|
218
|
-
# 后端
|
|
219
|
-
go build ./...
|
|
220
|
-
go test ./... -v
|
|
221
|
-
go vet ./...
|
|
222
|
-
|
|
223
|
-
# 前端
|
|
224
|
-
pnpm -C apps/web typecheck
|
|
225
|
-
pnpm -C apps/web lint
|
|
226
|
-
pnpm -C apps/web build
|
|
227
|
-
|
|
228
|
-
# 运行验证
|
|
229
|
-
make dev # 启动后端
|
|
230
|
-
pnpm -C apps/web dev # 启动前端
|
|
231
|
-
```
|
|
232
|
-
|
|
233
|
-
---
|
|
234
|
-
|
|
235
|
-
## 6. 审批流检查(--with-approval)
|
|
236
|
-
|
|
237
|
-
### 6.1 后端审批 ✓
|
|
238
|
-
|
|
239
|
-
```go
|
|
240
|
-
[ ] 状态枚举完整 - draft/pending/approved/rejected/active/inactive
|
|
241
|
-
[ ] ApprovalInstanceID - 模型包含审批实例 ID 字段
|
|
242
|
-
[ ] RejectReason - 模型包含拒绝原因字段
|
|
243
|
-
[ ] service/approval.go - Submit() 和 Cancel() 方法
|
|
244
|
-
[ ] service/callback.go - OnApproved() 和 OnRejected() 实现
|
|
245
|
-
[ ] handler/approval.go - HTTP 处理器
|
|
246
|
-
[ ] 审批路由 - POST /:id/submit, POST /:id/cancel
|
|
247
|
-
[ ] 回调注册 - RegisterApprovalCallback() 在 module.go
|
|
248
|
-
[ ] 审批类型常量 - ApprovalBusinessType = "module_approval"
|
|
249
|
-
```
|
|
250
|
-
|
|
251
|
-
### 6.2 前端审批 ✓
|
|
252
|
-
|
|
253
|
-
```tsx
|
|
254
|
-
[ ] ApprovalActions - 状态标签 + 提交/撤回按钮组件
|
|
255
|
-
[ ] 状态类型完整 - 'draft' | 'pending' | 'approved' | 'rejected'
|
|
256
|
-
[ ] API 函数 - submit{Pascal}(), cancel{Pascal}Approval()
|
|
257
|
-
[ ] 状态翻译 - status.draft/pending/approved/rejected
|
|
258
|
-
[ ] 操作翻译 - actions.submit/cancelApproval
|
|
259
|
-
[ ] 确认提示 - confirm.submit/cancel
|
|
260
|
-
[ ] 消息翻译 - messages.submitSuccess/cancelSuccess
|
|
261
|
-
```
|
|
262
|
-
|
|
263
|
-
### 6.3 审批流验证 ✓
|
|
264
|
-
|
|
265
|
-
```
|
|
266
|
-
[ ] 草稿→提交 - 只有 draft 状态可提交
|
|
267
|
-
[ ] 审批中→撤回 - 只有 pending 状态可撤回
|
|
268
|
-
[ ] 回调幂等 - OnApproved/OnRejected 检查当前状态
|
|
269
|
-
[ ] 上下文传递 - CreateInstance 包含业务信息
|
|
270
|
-
[ ] 错误处理 - 状态不匹配返回 I18n 错误
|
|
271
|
-
```
|
|
272
|
-
|
|
273
|
-
---
|
|
274
|
-
|
|
275
|
-
## 7. 常见遗漏
|
|
276
|
-
|
|
277
|
-
| 遗漏项 | 后果 | 检查方法 |
|
|
278
|
-
|--------|------|----------|
|
|
279
|
-
| `app.config.ts` 未启用 | 菜单不显示 | 检查 modules.enabled |
|
|
280
|
-
| `config.yaml` 未启用 | 模块不加载 | 检查 modules.enabled |
|
|
281
|
-
| 缺少 TableName() | 表名错误 | 检查数据库表 |
|
|
282
|
-
| 缺少 RegisterI18n() | 翻译不生效 | 检查错误消息 |
|
|
283
|
-
| Handler nil 检查缺失 | 服务 panic | 启动时测试 API |
|
|
284
|
-
| 缺少租户过滤 | 数据泄露 | 检查 SQL 日志 |
|
|
285
|
-
| 翻译键不匹配 | 显示键而非文本 | 切换语言测试 |
|