Initial commit
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
This commit is contained in:
@@ -0,0 +1,91 @@
|
||||
---
|
||||
name: code-reviewer
|
||||
description: "Diff-driven code reviewer for LJWX platform. Reviews ONLY files listed in the prompt against Phase scope and hard rules. Read-only access. Outputs structured findings. Use proactively after implementing a phase."
|
||||
model: claude-sonnet-4-6
|
||||
tools:
|
||||
- Read
|
||||
- Grep
|
||||
- Glob
|
||||
- Bash
|
||||
---
|
||||
|
||||
## Role
|
||||
|
||||
You are the Code Reviewer for the LJWX Platform. You have **read-only** access. Never edit or create files.
|
||||
|
||||
## Workflow
|
||||
|
||||
You receive:
|
||||
- A list of changed files (from `git diff --name-only HEAD`)
|
||||
- The Phase number and its allowed scope glob patterns
|
||||
|
||||
Execute in order:
|
||||
|
||||
### Step 1: Read context
|
||||
Read `CLAUDE.md` (hard rules) and `spec/01-constraints.md` (constraint detail).
|
||||
|
||||
### Step 2: Scope check
|
||||
Every changed file MUST match at least one scope glob pattern from the Phase.
|
||||
Files outside scope → **CRITICAL** violation (PATCHES 最小化 rule).
|
||||
|
||||
### Step 3: Rule checks per file type
|
||||
|
||||
**Java files — Controllers** (`*Controller.java`):
|
||||
- Every `@(Get|Post|Put|Delete|Patch)Mapping` method must have `@PreAuthorize` within 3 lines above
|
||||
- Exception: login and refresh endpoints (path contains `/auth/login` or `/auth/refresh`)
|
||||
- Authority format must be `hasAuthority('resource:action')`, not `hasRole(...)`
|
||||
|
||||
**Java files — DTOs / Requests / Responses** (`*DTO.java`, `*Dto.java`, `*Request.java`, `*Response.java`):
|
||||
- Must NOT contain `tenantId` or `tenant_id` field
|
||||
|
||||
**Java files — Module DAG**:
|
||||
- `ljwx-platform-data/` imports must NOT reference `com.ljwx.platform.security`
|
||||
- `ljwx-platform-security/` imports must NOT reference `com.ljwx.platform.data`
|
||||
- `ljwx-platform-core/` imports must NOT reference any other ljwx module
|
||||
- `ljwx-platform-web/` must depend on security (direct or transitive), not data directly
|
||||
|
||||
**POM files**:
|
||||
- No `${latest.version}` placeholder
|
||||
- Version numbers must be hard-coded digits
|
||||
|
||||
**SQL migration files** (`*/migration/*.sql`):
|
||||
- Business tables (not `QRTZ_*`): must contain all 7 audit columns:
|
||||
`tenant_id`, `created_by`, `created_time`, `updated_by`, `updated_time`, `deleted`, `version`
|
||||
- `QRTZ_*` tables: must NOT contain audit columns (they are Quartz system tables)
|
||||
- No `IF NOT EXISTS` anywhere in migration files
|
||||
|
||||
**TypeScript / Vue files** (`.ts`, `.vue`):
|
||||
- No `: any` or `as any` type usage
|
||||
- `tsconfig.json` must have `"strict": true`
|
||||
|
||||
**package.json files**:
|
||||
- All dependency versions must use `~` (tilde), never `^` (caret)
|
||||
|
||||
**Environment config files** (`.env*`, `*.ts`, `*.vue`):
|
||||
- Must use `VITE_APP_BASE_API`, never `VITE_API_BASE_URL` or other variants
|
||||
|
||||
**Vue Router**:
|
||||
- Must use vue-router v5 API patterns
|
||||
- No deprecated v4-only patterns
|
||||
|
||||
### Step 4: Output format
|
||||
|
||||
Each finding on its own line:
|
||||
```
|
||||
[CRITICAL|WARNING|INFO] <file>:<line> — <rule-id> — <description>
|
||||
evidence: <the offending line or pattern>
|
||||
fix: <specific remediation>
|
||||
```
|
||||
|
||||
Rule IDs: `scope-violation`, `no-preauthorize`, `dto-tenant-id`, `dag-violation`,
|
||||
`no-latest-version`, `audit-columns`, `if-not-exists`, `no-any`, `no-caret`,
|
||||
`wrong-env-var`, `router-v4-api`
|
||||
|
||||
End with exactly one of:
|
||||
```
|
||||
REVIEW PASSED
|
||||
```
|
||||
or:
|
||||
```
|
||||
REVIEW FAILED: <N> critical, <M> warnings
|
||||
```
|
||||
@@ -0,0 +1,89 @@
|
||||
{
|
||||
"permissions": {
|
||||
"defaultMode": "dontAsk",
|
||||
"allow": [
|
||||
"Read",
|
||||
"Edit",
|
||||
"Write",
|
||||
"Bash(mvn clean compile -f *)",
|
||||
"Bash(mvn test -f *)",
|
||||
"Bash(mvn verify -f *)",
|
||||
"Bash(mvn dependency:tree -f *)",
|
||||
"Bash(mvn versions:display-dependency-updates -f *)",
|
||||
"Bash(pnpm install)",
|
||||
"Bash(pnpm install --frozen-lockfile)",
|
||||
"Bash(pnpm run *)",
|
||||
"Bash(pnpm list *)",
|
||||
"Bash(pnpm exec *)",
|
||||
"Bash(node --version)",
|
||||
"Bash(java -version)",
|
||||
"Bash(java --version)",
|
||||
"Bash(bash scripts/gates/gate-all.sh)",
|
||||
"Bash(bash scripts/gates/gate-compile.sh)",
|
||||
"Bash(bash scripts/gates/gate-integration.sh)",
|
||||
"Bash(bash scripts/gates/gate-manifest.sh)",
|
||||
"Bash(bash scripts/gates/gate-contract.sh)",
|
||||
"Bash(bash scripts/gates/gate-nfr.sh)",
|
||||
"Bash(bash scripts/preflight/preflight-check.sh)",
|
||||
"Bash(bash scripts/phase-execute.sh *)",
|
||||
"Bash(bash scripts/phase-bundle.sh *)",
|
||||
"Bash(bash scripts/phase-parallel.sh *)",
|
||||
"Bash(cat *)",
|
||||
"Bash(grep *)",
|
||||
"Bash(grep -r *)",
|
||||
"Bash(grep -rn *)",
|
||||
"Bash(find *)",
|
||||
"Bash(mkdir -p *)",
|
||||
"Bash(chmod +x scripts/*)",
|
||||
"Bash(wc -l *)",
|
||||
"Bash(head *)",
|
||||
"Bash(tail *)",
|
||||
"Bash(diff *)",
|
||||
"Bash(git add .)",
|
||||
"Bash(git add -A)",
|
||||
"Bash(git commit -m *)",
|
||||
"Bash(git status)",
|
||||
"Bash(git status --porcelain)",
|
||||
"Bash(git diff)",
|
||||
"Bash(git diff --name-only *)",
|
||||
"Bash(git diff --cached *)",
|
||||
"Bash(git diff HEAD *)",
|
||||
"Bash(git log --oneline *)",
|
||||
"Bash(git branch *)",
|
||||
"Bash(git checkout -b *)",
|
||||
"Bash(git checkout *)",
|
||||
"Bash(git merge --no-ff *)",
|
||||
"Bash(git worktree add *)",
|
||||
"Bash(git worktree remove *)",
|
||||
"Bash(git worktree list)",
|
||||
"Bash(docker compose -f docker-compose.yml up -d)",
|
||||
"Bash(docker compose -f docker-compose.yml down)",
|
||||
"Bash(docker compose -f docker-compose.yml ps)",
|
||||
"Bash(docker compose -f docker-compose.yml logs *)",
|
||||
"Task(backend-builder)",
|
||||
"Task(frontend-builder)",
|
||||
"Task(code-reviewer)"
|
||||
],
|
||||
"deny": [
|
||||
"Bash(git push *)",
|
||||
"Bash(rm -rf *)",
|
||||
"Bash(rm -r *)",
|
||||
"Bash(curl *)",
|
||||
"Bash(wget *)",
|
||||
"Bash(sudo *)",
|
||||
"Bash(docker run *)",
|
||||
"Bash(docker exec *)",
|
||||
"Bash(npx *)",
|
||||
"Bash(eval *)",
|
||||
"Bash(sh -c *)",
|
||||
"WebSearch"
|
||||
]
|
||||
},
|
||||
"sandbox": {
|
||||
"enabled": true,
|
||||
"mode": "auto-allow"
|
||||
},
|
||||
"env": {
|
||||
"CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS": "1"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
---
|
||||
name: gate-check
|
||||
description: Run constraint checks on current phase output to verify LJWX platform rules
|
||||
disable-model-invocation: true
|
||||
argument-hint: [optional: phase-number]
|
||||
---
|
||||
|
||||
# Gate 检查
|
||||
|
||||
对当前 Phase 的产出执行约束检查:
|
||||
|
||||
## 检查项
|
||||
|
||||
1. **Caret 检查**: 所有 `package.json` 中是否有 `^`(应全部为 `~`)
|
||||
2. **Env 变量检查**: 是否存在 `VITE_API_BASE_URL`(应为 `VITE_APP_BASE_API`)
|
||||
3. **审计字段检查**: Flyway SQL 中的 CREATE TABLE 是否包含 7 列审计字段(Quartz 除外)
|
||||
4. **DAG 检查**: data 模块是否 import 了 security 包,反之亦然
|
||||
5. **DTO 检查**: DTO 类中是否出现 tenantId 字段
|
||||
6. **权限注解检查**: Controller 方法是否都有 @PreAuthorize(login/refresh 除外)
|
||||
7. **TypeScript 检查**: .ts / .vue 文件中是否有 `: any` 或 `as any`
|
||||
8. **Flyway 检查**: SQL 中是否有 `IF NOT EXISTS`
|
||||
9. **Vue Router 检查**: 是否使用了 v4 已废弃的 API
|
||||
|
||||
## 执行
|
||||
|
||||
```bash
|
||||
bash scripts/gates/gate-manifest.sh PHASE_MANIFEST.txt
|
||||
```
|
||||
|
||||
对每个不通过的检查项,给出具体文件路径和修复建议。
|
||||
@@ -0,0 +1,21 @@
|
||||
---
|
||||
name: phase-exec
|
||||
description: Execute a specific phase of LJWX platform development. Use when asked to implement a phase or when the user says phase followed by a number.
|
||||
argument-hint: [phase-number or range like 0-1]
|
||||
---
|
||||
|
||||
# Phase 执行流程
|
||||
|
||||
执行 Phase $ARGUMENTS:
|
||||
|
||||
1. 读取 `spec/phase/phase-$ARGUMENTS.md`
|
||||
2. 按其中的"读取清单"加载所需 spec 文件(仅加载列出的文件和章节)
|
||||
3. 按 `spec/08-output-rules.md` 的格式生成输出
|
||||
4. 如果是 Bundle(如 $ARGUMENTS 是 "0-1" 或 "2+3"),分别读取对应的 phase brief,每个 Phase 输出独立的块
|
||||
5. 输出前自检:
|
||||
- 所有 NEW FILES 是否完整(无省略标记)
|
||||
- CLAUDE.md 中的硬规则是否全部满足
|
||||
- Phase-Local Manifest 中的每个文件是否都已生成
|
||||
- import 路径是否符合 DAG 依赖规则
|
||||
- package.json 是否全部使用 ~ 而非 ^
|
||||
6. 完成后提示用户更新 CLAUDE.md 中的 Current Phase
|
||||
@@ -0,0 +1,70 @@
|
||||
---
|
||||
name: preflight
|
||||
description: Run preflight self-check to verify all spec files, phase briefs, skills, and agents are properly set up before starting code generation. Use before Phase 0.
|
||||
disable-model-invocation: false
|
||||
allowed-tools: Read, Bash, Grep, Glob
|
||||
---
|
||||
|
||||
# Preflight 自检
|
||||
|
||||
在开始任何 Phase 之前执行完整的环境和文档自检。
|
||||
|
||||
## Step 1: 运行自动化脚本
|
||||
|
||||
```bash
|
||||
bash scripts/preflight/preflight-check.sh
|
||||
```
|
||||
|
||||
如果脚本返回非零退出码,逐项修复 FAIL 项后重新运行。
|
||||
|
||||
## Step 2: 语义一致性检查(脚本无法覆盖,需人工智能验证)
|
||||
|
||||
读取以下文件并检查:
|
||||
|
||||
### 2a. CLAUDE.md 硬规则 vs spec/01-constraints.md
|
||||
|
||||
读取两个文件,确认:
|
||||
- CLAUDE.md 硬规则中提到的每条约束,在 spec/01-constraints.md 中有对应的详细定义
|
||||
- 没有矛盾(例如 CLAUDE.md 说 "不使用 ROLE_ 前缀" 但 constraints 中写了 ROLE_)
|
||||
|
||||
### 2b. CLAUDE.md 版本表 vs spec/06-frontend-config.md 中的 package.json
|
||||
|
||||
读取两个文件,确认:
|
||||
- spec/06-frontend-config.md 中所有 package.json 中的版本号与 CLAUDE.md 版本锁定表一致
|
||||
- 所有依赖使用 `~` 而非 `^`
|
||||
|
||||
### 2c. Phase Brief 的读取清单 vs spec/ 文件实际章节
|
||||
|
||||
抽查 phase-00、phase-02、phase-05、phase-12 的"读取清单":
|
||||
- 列出的 spec 文件是否存在
|
||||
- 列出的 §章节名是否能在对应文件中找到(模糊匹配即可)
|
||||
|
||||
### 2d. spec/03-api.md 路由表 vs spec/01-constraints.md 权限字符串
|
||||
|
||||
确认路由表中使用的所有权限字符串(如 user:read, job:execute)都在 RBAC 权限列表中。
|
||||
|
||||
## Step 3: 输出报告
|
||||
|
||||
输出格式:
|
||||
|
||||
```
|
||||
============================
|
||||
PREFLIGHT REPORT
|
||||
============================
|
||||
|
||||
## 自动化检查
|
||||
[粘贴 preflight-check.sh 的输出]
|
||||
|
||||
## 语义一致性检查
|
||||
| 检查项 | 结果 | 备注 |
|
||||
|--------|------|------|
|
||||
| 硬规则 vs constraints | ✅/❌ | ... |
|
||||
| 版本号一致性 | ✅/❌ | ... |
|
||||
| Phase Brief 引用有效性 | ✅/❌ | ... |
|
||||
| 路由权限覆盖率 | ✅/❌ | ... |
|
||||
|
||||
## 结论
|
||||
✅ READY — 可以开始 Phase 0
|
||||
或
|
||||
❌ NOT READY — 需要修复以下问题:[列表]
|
||||
```
|
||||
@@ -0,0 +1,183 @@
|
||||
# LJWX Platform
|
||||
|
||||
企业级全栈脚手架。Java 21 / Spring Boot 3.5 后端 + Vue 3 三端前端(Admin 管理后台 / Mobile uni-app 移动端 / Screen 数据大屏)。
|
||||
|
||||
## 工作流程(IMPORTANT — YOU MUST FOLLOW)
|
||||
|
||||
### 首次启动(Preflight)
|
||||
如果 Current Phase 显示"尚未开始",必须先执行 preflight 自检:
|
||||
1. 运行 `/preflight`
|
||||
2. 等待全部检查通过
|
||||
3. 通过后将 Current Phase 更新为 `Phase: 0 (Skeleton) — READY`
|
||||
4. 然后才能执行 `/phase-exec 0`
|
||||
|
||||
### 常规 Phase 执行
|
||||
1. 确认 Current Phase 编号
|
||||
2. 读取 `spec/phase/phase-{NN}.md` 获取本阶段任务、读取清单、验收条件
|
||||
3. **仅**按"读取清单"中列出的文件和章节读取 `spec/` 内容
|
||||
4. 禁止扫描整个 `spec/` 目录(Phase 19 除外)
|
||||
5. 按 `spec/08-output-rules.md` 格式输出
|
||||
6. 完成后告知用户更新 Current Phase
|
||||
|
||||
## Current Phase
|
||||
|
||||
Phase: 0 (Skeleton) — 尚未开始
|
||||
|
||||
## 硬规则(违反任何一篇为 FAIL)
|
||||
|
||||
1. **DAG 依赖**: core ← {security, data} ← web ← app。security 和 data 互不依赖。禁止 data import security 的任何类,反之亦然
|
||||
2. **前端 semver**: 所有 dependencies / devDependencies 仅用 `~`(tilde),禁止 `^`(caret)
|
||||
3. **审计字段**: 所有业务表(Quartz 除外)必须含 tenant_id, created_by, created_time, updated_by, updated_time, deleted, version 共 7 列,均 NOT NULL + 有 DEFAULT
|
||||
4. **TypeScript**: 禁止 `any`,tsconfig 开启 `strict: true`
|
||||
5. **权限注解**: 每个 Controller 方法必须 `@PreAuthorize`(login / refresh 除外),格式 `hasAuthority('resource:action')`,不使用 ROLE_ 前缀
|
||||
6. **tenant_id**: DTO 中禁止出现,前端禁止传递,后端由 Interceptor 自动注入
|
||||
7. **环境变量**: 前端统一 `VITE_APP_BASE_API`,禁止 `VITE_API_BASE_URL`
|
||||
8. **Vue Router**: 必须按 vue-router @5 API 写,禁止按 v4 经验写。参考 https://router.vuejs.org/guide/migration/v4-to-v5
|
||||
9. **Flyway**: 禁止 `IF NOT EXISTS`
|
||||
10. **POM 版本**: 禁止 `${latest.version}`,所有版本必须硬编码数字
|
||||
11. **输出完整性**: NEW FILES 必须输出完整文件内容,禁止 `// ...省略...` 或 `/* same as before */`
|
||||
12. **PATCHES 最小化**: 仅修改与当前 Phase 直接相关的文件,禁止顺手重构、重新格式化、批量重写无关文件
|
||||
13. **日志脱敏**: password → `***`,phone → 中间四位 `*`,idCard → 中间段 `*`
|
||||
14. **BCrypt**: admin 密码 `Admin@12345` 使用 cost=10 的 BCrypt hash,写在 V006 种子 SQL 中
|
||||
|
||||
## 版本锁定(SINGLE SOURCE OF TRUTH — 其他文件禁止重复写版本号,引用本段即可)
|
||||
|
||||
### 后端
|
||||
|
||||
| 依赖 | 版本 |
|
||||
|------|------|
|
||||
| Java JDK | 21 (21.0.10) |
|
||||
| Spring Boot | 3.5.11 |
|
||||
| MyBatis Spring Boot Starter | 3.0.5 |
|
||||
| springdoc-openapi (BOM) | 2.8.15 |
|
||||
| Testcontainers (BOM) | 1.21.4 |
|
||||
| PostgreSQL (Docker) | 16.12 (postgres:16.12-alpine) |
|
||||
| Flyway | 由 Spring Boot BOM 管理 |
|
||||
| Quartz | 由 spring-boot-starter-quartz 管理 |
|
||||
| Maven Wrapper | 3.9.9 |
|
||||
|
||||
### 前端
|
||||
|
||||
| 依赖 | 版本 | 策略 |
|
||||
|------|------|------|
|
||||
| Node.js | 22.22.0 (.nvmrc) | ≥20.19 \| ≥22.12 |
|
||||
| pnpm | 10.30.1 | packageManager 字段锁定 |
|
||||
| Vue | ~3.5.28 | ~ patch only |
|
||||
| Vite | ~7.3.1 | ~ patch only |
|
||||
| TypeScript | ~5.9.3 | ~ patch only |
|
||||
| Vue Router | ~5.0.2 | ~ patch only |
|
||||
| Pinia | ~3.0.4 | ~ patch only |
|
||||
| Element Plus | ~2.13.2 | ~ patch only |
|
||||
| @element-plus/icons-vue | ~2.3.2 | ~ patch only |
|
||||
| ECharts | ~6.0.0 | ~ patch only |
|
||||
| Axios | ~1.13.5 | ~ patch only |
|
||||
| @vueuse/core | ~14.2.1 | ~ patch only |
|
||||
| unplugin-auto-import | ~21.0.0 | ~ devDep |
|
||||
| unplugin-vue-components | ~31.0.0 | ~ devDep |
|
||||
| vue-tsc | ~3.2.4 | ~ devDep |
|
||||
| sass | ~1.97.3 | ~ devDep |
|
||||
| nprogress | ~0.2.0 | ~ |
|
||||
| dayjs | ~1.11.19 | ~ |
|
||||
| @kjgl77/datav-vue3 | ~1.7.4 | ~ Screen 专用 |
|
||||
| tsup | ~8.5.0 | ~ shared 包构建 |
|
||||
|
||||
### Maven 坐标
|
||||
|
||||
```xml
|
||||
<groupId>com.ljwx.platform</groupId>
|
||||
<artifactId>ljwx-platform-{module}</artifactId>
|
||||
<version>1.0.0-SNAPSHOT</version>
|
||||
```
|
||||
|
||||
## 代码风格参考(少样本)
|
||||
|
||||
### Controller(后端)
|
||||
|
||||
```java
|
||||
@RestController
|
||||
@RequestMapping("/api/users")
|
||||
@RequiredArgsConstructor
|
||||
public class UserController {
|
||||
private final UserAppService userAppService;
|
||||
|
||||
@GetMapping
|
||||
@PreAuthorize("hasAuthority('user:read')")
|
||||
public Result<PageResult<UserVO>> list(UserQueryDTO query) {
|
||||
return Result.ok(userAppService.listUsers(query));
|
||||
}
|
||||
|
||||
@PostMapping
|
||||
@PreAuthorize("hasAuthority('user:write')")
|
||||
public Result<Long> create(@RequestBody @Valid UserCreateDTO dto) {
|
||||
return Result.ok(userAppService.createUser(dto));
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Service(后端)
|
||||
|
||||
```java
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class UserAppService {
|
||||
private final UserMapper userMapper;
|
||||
|
||||
public PageResult<UserVO> listUsers(UserQueryDTO query) {
|
||||
// 不传 tenantId,TenantLineInterceptor 自动注入
|
||||
List<UserVO> list = userMapper.selectUserList(query);
|
||||
long total = userMapper.countUsers(query);
|
||||
return new PageResult<>(list, total);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Vue 组件(前端)
|
||||
|
||||
```vue
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { getUsers } from '@/api/user'
|
||||
import type { UserVO } from '@ljwx/shared'
|
||||
|
||||
const loading = ref(false)
|
||||
const users = ref<UserVO[]>([])
|
||||
|
||||
onMounted(async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
users.value = await getUsers()
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
})
|
||||
</script>
|
||||
```
|
||||
|
||||
### API 调用(前端)
|
||||
|
||||
```typescript
|
||||
import request from '@/api/request'
|
||||
import type { Result, PageResult, UserVO, UserQueryDTO } from '@ljwx/shared'
|
||||
|
||||
export function getUsers(params?: UserQueryDTO): Promise<PageResult<UserVO>> {
|
||||
return request.get('/users', { params })
|
||||
}
|
||||
```
|
||||
|
||||
## 反模式(IMPORTANT — 禁止)
|
||||
|
||||
- ❌ data 模块 import com.ljwx.platform.security.* → 违反 DAG
|
||||
- ❌ security 模块 import com.ljwx.platform.data.* → 违反 DAG
|
||||
- ❌ DTO 中 private Long tenantId → 违反多租户规则
|
||||
- ❌ @PreAuthorize("hasRole('ADMIN')") → 不使用 ROLE_ 前缀
|
||||
- ❌ "axios": "^1.13.5" → 禁止 caret
|
||||
- ❌ const data: any = response → 禁止 any
|
||||
- ❌ CREATE TABLE IF NOT EXISTS 在 Flyway SQL 中 → 禁止
|
||||
- ❌ VITE_API_BASE_URL → 已废弃变量名
|
||||
- ❌ 按 vue-router v4 经验写 createRouter → 必须用 v5 API
|
||||
- ❌ 修改非本 Phase 职责范围内的文件 → PATCHES 最小化
|
||||
- ❌ // ... 省略 ... 或 /* rest same as before */ → 必须输出完整内容
|
||||
|
||||
## Compact 指令
|
||||
|
||||
When compacting, always preserve: 当前 Phase 编号及进度、已完成的文件清单、硬规则全文、版本锁定表全文、反模式清单。
|
||||
Executable
+48
@@ -0,0 +1,48 @@
|
||||
#!/usr/bin/env bash
|
||||
# Parse YAML front-matter from a Phase Brief file.
|
||||
# Usage: source scripts/lib/parse-phase-brief.sh <phase-brief-path>
|
||||
# Exports: PHASE_NUM, PHASE_TITLE, TARGET_BACKEND, TARGET_FRONTEND, PHASE_SCOPE (array)
|
||||
|
||||
_BRIEF_PATH="${1:?Usage: source parse-phase-brief.sh <path-to-phase-XX.md>}"
|
||||
|
||||
if [[ ! -f "$_BRIEF_PATH" ]]; then
|
||||
echo "ERROR: Phase brief not found: $_BRIEF_PATH" >&2
|
||||
return 1 2>/dev/null || exit 1
|
||||
fi
|
||||
|
||||
# Extract YAML block between first pair of --- markers (skip first line, stop before second ---)
|
||||
_YAML=$(awk '/^---$/{n++;if(n==1){next};if(n==2){exit}} n==1{print}' "$_BRIEF_PATH")
|
||||
|
||||
if [[ -z "$_YAML" ]]; then
|
||||
echo "ERROR: No YAML front-matter found in $_BRIEF_PATH" >&2
|
||||
echo " Every phase brief must begin with a --- YAML block." >&2
|
||||
return 1 2>/dev/null || exit 1
|
||||
fi
|
||||
|
||||
PHASE_NUM=$(echo "$_YAML" | grep '^phase:' | awk '{print $2}')
|
||||
PHASE_TITLE=$(echo "$_YAML" | grep '^title:' | sed 's/^title:[[:space:]]*//' | sed 's/^"//' | sed 's/"$//')
|
||||
TARGET_BACKEND=$(echo "$_YAML" | grep 'backend:' | awk '{print $2}' | head -1)
|
||||
TARGET_FRONTEND=$(echo "$_YAML" | grep 'frontend:' | awk '{print $2}' | head -1)
|
||||
|
||||
# Parse scope list into array — use sed instead of BASH_REMATCH for portability
|
||||
PHASE_SCOPE=()
|
||||
_in_scope=false
|
||||
while IFS= read -r _line; do
|
||||
if echo "$_line" | grep -q '^scope:'; then
|
||||
_in_scope=true
|
||||
continue
|
||||
fi
|
||||
if $_in_scope; then
|
||||
if echo "$_line" | grep -qE '^[[:space:]]*-[[:space:]]+'; then
|
||||
_entry=$(echo "$_line" | sed 's/^[[:space:]]*-[[:space:]]*//')
|
||||
# Strip surrounding quotes if present
|
||||
_entry="${_entry%\"}"
|
||||
_entry="${_entry#\"}"
|
||||
PHASE_SCOPE+=("$_entry")
|
||||
else
|
||||
_in_scope=false
|
||||
fi
|
||||
fi
|
||||
done <<< "$_YAML"
|
||||
|
||||
export PHASE_NUM PHASE_TITLE TARGET_BACKEND TARGET_FRONTEND PHASE_SCOPE
|
||||
Executable
+108
@@ -0,0 +1,108 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
PHASE_NUM="${1:?Usage: phase-execute.sh <phase-number> [--skip-preflight]}"
|
||||
SKIP_PREFLIGHT="${2:-}"
|
||||
PROJECT_ROOT="$(cd "$(dirname "$0")/.." && pwd)"
|
||||
cd "$PROJECT_ROOT"
|
||||
|
||||
PHASE_BRIEF="spec/phase/phase-$(printf '%02d' "$PHASE_NUM").md"
|
||||
if [[ ! -f "$PHASE_BRIEF" ]]; then
|
||||
echo "ERROR: $PHASE_BRIEF not found"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# ── Parse Phase Brief ─────────────────────────────────────
|
||||
source scripts/lib/parse-phase-brief.sh "$PHASE_BRIEF"
|
||||
echo "Phase $PHASE_NUM: $PHASE_TITLE"
|
||||
echo " Backend: $TARGET_BACKEND | Frontend: $TARGET_FRONTEND"
|
||||
echo " Scope: ${PHASE_SCOPE[*]}"
|
||||
echo ""
|
||||
|
||||
# ── Step 0: Preflight ─────────────────────────────────────
|
||||
if [[ "$SKIP_PREFLIGHT" != "--skip-preflight" ]]; then
|
||||
echo "═══ STEP 0: Preflight Check ═══"
|
||||
bash scripts/preflight/preflight-check.sh
|
||||
echo ""
|
||||
fi
|
||||
|
||||
# ── Step 1a: Backend generation ───────────────────────────
|
||||
if [[ "$TARGET_BACKEND" == "true" ]]; then
|
||||
echo "═══ STEP 1a: Backend Builder — Phase $PHASE_NUM ═══"
|
||||
claude -p "You are backend-builder. Execute Phase $PHASE_NUM.
|
||||
Read CLAUDE.md, spec/01-constraints.md, and $PHASE_BRIEF.
|
||||
Generate all backend files listed in the Phase Brief scope.
|
||||
Follow spec/08-output-rules.md for output format.
|
||||
After generating, run: bash scripts/gates/gate-compile.sh" \
|
||||
--agent backend-builder \
|
||||
--output-format json > "/tmp/ljwx-phase-${PHASE_NUM}-backend.json"
|
||||
echo "Backend generation complete."
|
||||
echo ""
|
||||
fi
|
||||
|
||||
# ── Step 1b: Frontend generation ──────────────────────────
|
||||
if [[ "$TARGET_FRONTEND" == "true" ]]; then
|
||||
echo "═══ STEP 1b: Frontend Builder — Phase $PHASE_NUM ═══"
|
||||
claude -p "You are frontend-builder. Execute Phase $PHASE_NUM.
|
||||
Read CLAUDE.md, spec/06-frontend-config.md, and $PHASE_BRIEF.
|
||||
Generate all frontend files listed in the Phase Brief scope.
|
||||
Follow spec/08-output-rules.md for output format.
|
||||
After generating, run: pnpm install && pnpm run type-check" \
|
||||
--agent frontend-builder \
|
||||
--output-format json > "/tmp/ljwx-phase-${PHASE_NUM}-frontend.json"
|
||||
echo "Frontend generation complete."
|
||||
echo ""
|
||||
fi
|
||||
|
||||
# ── Step 2: Gate check ────────────────────────────────────
|
||||
echo "═══ STEP 2: Gate Check ═══"
|
||||
bash scripts/gates/gate-all.sh
|
||||
GATE_EXIT=$?
|
||||
if [[ "$GATE_EXIT" -ne 0 ]]; then
|
||||
echo "GATE FAILED — aborting."
|
||||
exit 1
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# ── Step 3: Diff-driven hard rule scan ────────────────────
|
||||
echo "═══ STEP 3: Diff Review (zero-token) ═══"
|
||||
bash scripts/review/diff-review.sh "$PHASE_NUM"
|
||||
REVIEW_EXIT=$?
|
||||
if [[ "$REVIEW_EXIT" -ne 0 ]]; then
|
||||
echo "DIFF REVIEW FAILED — aborting."
|
||||
exit 1
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# ── Step 4: Semantic review via Agent ─────────────────────
|
||||
echo "═══ STEP 4: Semantic Review ═══"
|
||||
CHANGED_FILES=$(git diff --name-only HEAD)
|
||||
REVIEW_RESULT=$(claude -p "You are code-reviewer. Review ONLY these changed files:
|
||||
$CHANGED_FILES
|
||||
|
||||
Phase $PHASE_NUM scope allows: ${PHASE_SCOPE[*]}
|
||||
|
||||
Read CLAUDE.md and spec/01-constraints.md.
|
||||
For each file, check the review checklist.
|
||||
Flag any file NOT in the allowed scope as CRITICAL.
|
||||
Output each finding as:
|
||||
[CRITICAL|WARNING|INFO] <file>:<line> — <rule-id> — <description> — <evidence>
|
||||
End with: REVIEW PASSED or REVIEW FAILED: N critical, M warnings." \
|
||||
--agent code-reviewer \
|
||||
--output-format text)
|
||||
|
||||
echo "$REVIEW_RESULT"
|
||||
echo ""
|
||||
|
||||
if echo "$REVIEW_RESULT" | grep -q "REVIEW FAILED"; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# ── Step 5: Commit ────────────────────────────────────────
|
||||
echo "═══ STEP 5: Git Commit ═══"
|
||||
git add -A
|
||||
git commit -m "feat(phase-${PHASE_NUM}): ${PHASE_TITLE} — auto-verified"
|
||||
echo ""
|
||||
echo "════════════════════════════════════════"
|
||||
echo "Phase $PHASE_NUM COMPLETE: $PHASE_TITLE"
|
||||
echo "════════════════════════════════════════"
|
||||
Executable
+161
@@ -0,0 +1,161 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
PHASE_NUM="${1:?Usage: phase-parallel.sh <phase-number>}"
|
||||
PROJECT_ROOT="$(cd "$(dirname "$0")/.." && pwd)"
|
||||
cd "$PROJECT_ROOT"
|
||||
|
||||
PHASE_BRIEF="spec/phase/phase-$(printf '%02d' "$PHASE_NUM").md"
|
||||
source scripts/lib/parse-phase-brief.sh "$PHASE_BRIEF"
|
||||
|
||||
if [[ "$TARGET_BACKEND" != "true" || "$TARGET_FRONTEND" != "true" ]]; then
|
||||
echo "Phase $PHASE_NUM does not require both backend and frontend."
|
||||
echo " Backend: $TARGET_BACKEND | Frontend: $TARGET_FRONTEND"
|
||||
echo "Use scripts/phase-execute.sh instead."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
MAIN_BRANCH=$(git branch --show-current)
|
||||
BE_BRANCH="phase-${PHASE_NUM}-backend"
|
||||
FE_BRANCH="phase-${PHASE_NUM}-frontend"
|
||||
BE_WORKTREE="/tmp/ljwx-worktree-be-${PHASE_NUM}"
|
||||
FE_WORKTREE="/tmp/ljwx-worktree-fe-${PHASE_NUM}"
|
||||
|
||||
# ── Cleanup on exit ───────────────────────────────────────
|
||||
cleanup() {
|
||||
echo ""
|
||||
echo "Cleaning up worktrees..."
|
||||
git worktree remove "$BE_WORKTREE" --force 2>/dev/null || true
|
||||
git worktree remove "$FE_WORKTREE" --force 2>/dev/null || true
|
||||
git branch -D "$BE_BRANCH" 2>/dev/null || true
|
||||
git branch -D "$FE_BRANCH" 2>/dev/null || true
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
# ── Step 0: Preflight ─────────────────────────────────────
|
||||
echo "═══ Preflight ═══"
|
||||
bash scripts/preflight/preflight-check.sh
|
||||
echo ""
|
||||
|
||||
# ── Step 1: Create isolated worktrees ─────────────────────
|
||||
echo "═══ Creating worktrees ═══"
|
||||
git branch "$BE_BRANCH" HEAD
|
||||
git branch "$FE_BRANCH" HEAD
|
||||
git worktree add "$BE_WORKTREE" "$BE_BRANCH"
|
||||
git worktree add "$FE_WORKTREE" "$FE_BRANCH"
|
||||
echo " Backend worktree: $BE_WORKTREE ($BE_BRANCH)"
|
||||
echo " Frontend worktree: $FE_WORKTREE ($FE_BRANCH)"
|
||||
echo ""
|
||||
|
||||
# ── Step 2: Parallel generation ───────────────────────────
|
||||
echo "═══ Parallel Generation — Phase $PHASE_NUM ═══"
|
||||
|
||||
(
|
||||
cd "$BE_WORKTREE"
|
||||
echo "[BE] Starting backend generation..."
|
||||
claude -p "You are backend-builder. Execute Phase $PHASE_NUM backend tasks.
|
||||
Read CLAUDE.md, spec/01-constraints.md, spec/08-output-rules.md, and $PHASE_BRIEF.
|
||||
Generate ONLY backend files (.java, pom.xml, .xml, .yml, .sql, .properties).
|
||||
Do NOT generate any frontend files (.vue, .ts, .json for node, .css, .html).
|
||||
Follow spec/08-output-rules.md for output format." \
|
||||
--agent backend-builder \
|
||||
--output-format text
|
||||
git add -A
|
||||
git commit -m "wip: phase-${PHASE_NUM} backend" --allow-empty
|
||||
echo "[BE] Done."
|
||||
) &
|
||||
PID_BE=$!
|
||||
|
||||
(
|
||||
cd "$FE_WORKTREE"
|
||||
echo "[FE] Starting frontend generation..."
|
||||
claude -p "You are frontend-builder. Execute Phase $PHASE_NUM frontend tasks.
|
||||
Read CLAUDE.md, spec/06-frontend-config.md, spec/08-output-rules.md, and $PHASE_BRIEF.
|
||||
Generate ONLY frontend files (.vue, .ts, package.json, .scss, .html, tsconfig*.json).
|
||||
Do NOT generate any backend files (.java, pom.xml, .sql, .yml backend configs).
|
||||
Follow spec/08-output-rules.md for output format." \
|
||||
--agent frontend-builder \
|
||||
--output-format text
|
||||
git add -A
|
||||
git commit -m "wip: phase-${PHASE_NUM} frontend" --allow-empty
|
||||
echo "[FE] Done."
|
||||
) &
|
||||
PID_FE=$!
|
||||
|
||||
echo "Backend PID: $PID_BE | Frontend PID: $PID_FE"
|
||||
echo "Waiting for both agents to complete..."
|
||||
|
||||
BE_EXIT=0
|
||||
FE_EXIT=0
|
||||
wait $PID_BE || BE_EXIT=$?
|
||||
wait $PID_FE || FE_EXIT=$?
|
||||
|
||||
if [[ "$BE_EXIT" -ne 0 ]]; then
|
||||
echo "ERROR: Backend generation failed (exit $BE_EXIT)"
|
||||
exit 1
|
||||
fi
|
||||
if [[ "$FE_EXIT" -ne 0 ]]; then
|
||||
echo "ERROR: Frontend generation failed (exit $FE_EXIT)"
|
||||
exit 1
|
||||
fi
|
||||
echo "Both agents completed successfully."
|
||||
echo ""
|
||||
|
||||
# ── Step 3: Merge both branches back ─────────────────────
|
||||
echo "═══ Merging worktrees → $MAIN_BRANCH ═══"
|
||||
git merge --no-ff "$BE_BRANCH" -m "merge: phase-${PHASE_NUM} backend"
|
||||
git merge --no-ff "$FE_BRANCH" -m "merge: phase-${PHASE_NUM} frontend"
|
||||
echo ""
|
||||
|
||||
# ── Step 4: Gate check on merged result ───────────────────
|
||||
echo "═══ Gate Check (merged) ═══"
|
||||
bash scripts/gates/gate-all.sh
|
||||
GATE_EXIT=$?
|
||||
if [[ "$GATE_EXIT" -ne 0 ]]; then
|
||||
echo "GATE FAILED — aborting."
|
||||
exit 1
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# ── Step 5: Diff review on merged result ──────────────────
|
||||
echo "═══ Diff Review (merged) ═══"
|
||||
bash scripts/review/diff-review.sh "$PHASE_NUM"
|
||||
DREVIEW_EXIT=$?
|
||||
if [[ "$DREVIEW_EXIT" -ne 0 ]]; then
|
||||
echo "DIFF REVIEW FAILED — aborting."
|
||||
exit 1
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# ── Step 6: Semantic review via Agent ─────────────────────
|
||||
echo "═══ Semantic Review ═══"
|
||||
CHANGED_FILES=$(git diff --name-only HEAD~2)
|
||||
REVIEW_RESULT=$(claude -p "You are code-reviewer. Review ONLY these changed files:
|
||||
$CHANGED_FILES
|
||||
|
||||
Phase $PHASE_NUM scope: ${PHASE_SCOPE[*]}
|
||||
|
||||
Read CLAUDE.md and spec/01-constraints.md.
|
||||
Check all hard rules against each changed file.
|
||||
Flag any file outside scope as CRITICAL (scope-violation).
|
||||
Output structured findings. End with REVIEW PASSED or REVIEW FAILED: N critical, M warnings." \
|
||||
--agent code-reviewer \
|
||||
--output-format text)
|
||||
|
||||
echo "$REVIEW_RESULT"
|
||||
echo ""
|
||||
|
||||
if echo "$REVIEW_RESULT" | grep -q "REVIEW FAILED"; then
|
||||
echo "SEMANTIC REVIEW FAILED — aborting."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# ── Step 7: Squash merge commits into one ─────────────────
|
||||
echo "═══ Final Commit ═══"
|
||||
git reset --soft HEAD~2
|
||||
git commit -m "feat(phase-${PHASE_NUM}): ${PHASE_TITLE} — parallel verified"
|
||||
|
||||
echo ""
|
||||
echo "════════════════════════════════════════"
|
||||
echo "Phase $PHASE_NUM COMPLETE (parallel): $PHASE_TITLE"
|
||||
echo "════════════════════════════════════════"
|
||||
Executable
+224
@@ -0,0 +1,224 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
PASS=0
|
||||
FAIL=0
|
||||
WARN=0
|
||||
|
||||
pass() { echo " ✅ $1"; PASS=$((PASS + 1)); }
|
||||
fail() { echo " ❌ $1"; FAIL=$((FAIL + 1)); }
|
||||
warn() { echo " ⚠️ $1"; WARN=$((WARN + 1)); }
|
||||
|
||||
echo "============================================"
|
||||
echo " LJWX Platform Preflight Check"
|
||||
echo " Date: $(date -u '+%Y-%m-%d %H:%M:%S UTC')"
|
||||
echo "============================================"
|
||||
echo ""
|
||||
|
||||
# ============================================
|
||||
# 1. 核心文件存在性检查
|
||||
# ============================================
|
||||
echo "[1/8] Core Files"
|
||||
|
||||
CORE_FILES=(
|
||||
"CLAUDE.md"
|
||||
"spec.md"
|
||||
"spec/INDEX.md"
|
||||
"spec/01-constraints.md"
|
||||
"spec/02-architecture.md"
|
||||
"spec/03-api.md"
|
||||
"spec/04-database.md"
|
||||
"spec/05-backend-config.md"
|
||||
"spec/06-frontend-config.md"
|
||||
"spec/07-devops.md"
|
||||
"spec/08-output-rules.md"
|
||||
)
|
||||
|
||||
for f in "${CORE_FILES[@]}"; do
|
||||
if [[ -f "$f" ]]; then
|
||||
# 非空检查
|
||||
if [[ -s "$f" ]]; then
|
||||
pass "$f exists and non-empty"
|
||||
else
|
||||
fail "$f exists but is EMPTY"
|
||||
fi
|
||||
else
|
||||
fail "$f MISSING"
|
||||
fi
|
||||
done
|
||||
|
||||
# ============================================
|
||||
# 2. Phase Brief 文件检查(phase-00 ~ phase-19)
|
||||
# ============================================
|
||||
echo ""
|
||||
echo "[2/8] Phase Brief Files"
|
||||
|
||||
for i in $(seq -w 0 19); do
|
||||
pf="spec/phase/phase-${i}.md"
|
||||
if [[ -f "$pf" && -s "$pf" ]]; then
|
||||
pass "$pf"
|
||||
else
|
||||
fail "$pf missing or empty"
|
||||
fi
|
||||
done
|
||||
|
||||
# ============================================
|
||||
# 3. Claude Code 扩展文件
|
||||
# ============================================
|
||||
echo ""
|
||||
echo "[3/8] Claude Code Extensions"
|
||||
|
||||
EXTENSION_FILES=(
|
||||
".claude/skills/phase-exec/SKILL.md"
|
||||
".claude/skills/gate-check/SKILL.md"
|
||||
)
|
||||
|
||||
for f in "${EXTENSION_FILES[@]}"; do
|
||||
if [[ -f "$f" && -s "$f" ]]; then
|
||||
pass "$f"
|
||||
else
|
||||
fail "$f missing or empty"
|
||||
fi
|
||||
done
|
||||
|
||||
# ============================================
|
||||
# 4. CLAUDE.md 内容完整性
|
||||
# ============================================
|
||||
echo ""
|
||||
echo "[4/8] CLAUDE.md Content Integrity"
|
||||
|
||||
check_claude_md() {
|
||||
local keyword="$1"
|
||||
local label="$2"
|
||||
if grep -qi "$keyword" CLAUDE.md; then
|
||||
pass "CLAUDE.md contains: $label"
|
||||
else
|
||||
fail "CLAUDE.md missing: $label"
|
||||
fi
|
||||
}
|
||||
|
||||
check_claude_md "Current Phase" "Current Phase 标记"
|
||||
check_claude_md "硬规则" "硬规则段"
|
||||
check_claude_md "版本锁定" "版本锁定段"
|
||||
check_claude_md "反模式" "反模式段"
|
||||
check_claude_md "Compact 指令" "Compact 指令"
|
||||
check_claude_md "DAG 依赖" "DAG 依赖规则"
|
||||
check_claude_md "3.5.11" "Spring Boot 版本"
|
||||
check_claude_md "22.22.0" "Node.js 版本"
|
||||
check_claude_md "10.30.1" "pnpm 版本"
|
||||
check_claude_md "~3.5.28" "Vue 版本(tilde)"
|
||||
check_claude_md "~7.3.1" "Vite 版本(tilde)"
|
||||
check_claude_md "Vue Router.*@5" "Vue Router 5 约束"
|
||||
check_claude_md "VITE_APP_BASE_API" "环境变量名"
|
||||
check_claude_md "hasAuthority" "权限注解格式"
|
||||
|
||||
# ============================================
|
||||
# 5. spec/INDEX.md 防漂移规则
|
||||
# ============================================
|
||||
echo ""
|
||||
echo "[5/8] INDEX.md Integrity"
|
||||
|
||||
if grep -qi "单一事实源" spec/INDEX.md; then
|
||||
pass "INDEX.md contains 单一事实源 rule"
|
||||
else
|
||||
fail "INDEX.md missing 单一事实源 rule"
|
||||
fi
|
||||
|
||||
if grep -qi "防漂移" spec/INDEX.md; then
|
||||
pass "INDEX.md contains 防漂移 rules"
|
||||
else
|
||||
fail "INDEX.md missing 防漂移 rules"
|
||||
fi
|
||||
|
||||
# ============================================
|
||||
# 6. 版本号唯一性(不应在 spec/ 中重复出现硬编码版本)
|
||||
# ============================================
|
||||
echo ""
|
||||
echo "[6/8] Version Single Source of Truth"
|
||||
|
||||
# 检查 spec/ 下是否直接硬编码了 Spring Boot 版本号(应引用 CLAUDE.md)
|
||||
SB_IN_SPEC=$(grep -rn "3\.5\.11" spec/ 2>/dev/null | grep -v "见 CLAUDE" | grep -v "INDEX" || true)
|
||||
if [[ -z "$SB_IN_SPEC" ]]; then
|
||||
pass "Spring Boot version not duplicated in spec/"
|
||||
else
|
||||
warn "Spring Boot version (3.5.11) found in spec/ — should reference CLAUDE.md instead"
|
||||
echo " $SB_IN_SPEC"
|
||||
fi
|
||||
|
||||
# ============================================
|
||||
# 7. Phase Brief 结构检查(抽查 phase-00)
|
||||
# ============================================
|
||||
echo ""
|
||||
echo "[7/8] Phase Brief Structure (spot check: phase-00)"
|
||||
|
||||
PHASE00="spec/phase/phase-00.md"
|
||||
if [[ -f "$PHASE00" ]]; then
|
||||
for section in "读取清单" "任务" "Phase-Local Manifest" "验收条件"; do
|
||||
if grep -qi "$section" "$PHASE00"; then
|
||||
pass "phase-00 contains section: $section"
|
||||
else
|
||||
fail "phase-00 missing section: $section"
|
||||
fi
|
||||
done
|
||||
else
|
||||
fail "phase-00.md not found, cannot check structure"
|
||||
fi
|
||||
|
||||
# ============================================
|
||||
# 8. Skill/Agent 格式检查
|
||||
# ============================================
|
||||
echo ""
|
||||
echo "[8/8] Skill/Agent Format"
|
||||
|
||||
# Skills 应包含 frontmatter
|
||||
for skill in .claude/skills/*/SKILL.md; do
|
||||
[[ ! -f "$skill" ]] && continue
|
||||
if head -1 "$skill" | grep -q "^---"; then
|
||||
pass "$skill has YAML frontmatter"
|
||||
else
|
||||
fail "$skill missing YAML frontmatter"
|
||||
fi
|
||||
if grep -q "description:" "$skill"; then
|
||||
pass "$skill has description field"
|
||||
else
|
||||
fail "$skill missing description field"
|
||||
fi
|
||||
done
|
||||
|
||||
# Agents 应包含 frontmatter (如果存在)
|
||||
if compgen -G ".claude/agents/*.md" > /dev/null; then
|
||||
for agent in .claude/agents/*.md; do
|
||||
[[ ! -f "$agent" ]] && continue
|
||||
if head -1 "$agent" | grep -q "^---"; then
|
||||
pass "$agent has YAML frontmatter"
|
||||
else
|
||||
fail "$agent missing YAML frontmatter"
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
# ============================================
|
||||
# Summary
|
||||
# ============================================
|
||||
echo ""
|
||||
echo "============================================"
|
||||
echo " PREFLIGHT SUMMARY"
|
||||
echo "============================================"
|
||||
echo " ✅ PASS: $PASS"
|
||||
echo " ❌ FAIL: $FAIL"
|
||||
echo " ⚠️ WARN: $WARN"
|
||||
echo ""
|
||||
|
||||
if [[ $FAIL -gt 0 ]]; then
|
||||
echo " 🚫 PREFLIGHT FAILED — Fix $FAIL issue(s) before starting Phase 0"
|
||||
echo ""
|
||||
exit 1
|
||||
else
|
||||
if [[ $WARN -gt 0 ]]; then
|
||||
echo " ✅ PREFLIGHT PASSED (with $WARN warning(s))"
|
||||
else
|
||||
echo " ✅ PREFLIGHT PASSED — Ready to start Phase 0"
|
||||
fi
|
||||
echo ""
|
||||
exit 0
|
||||
fi
|
||||
Executable
+235
@@ -0,0 +1,235 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
PHASE_NUM="${1:?Usage: diff-review.sh <phase-number>}"
|
||||
PROJECT_ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
|
||||
cd "$PROJECT_ROOT"
|
||||
|
||||
PHASE_BRIEF="spec/phase/phase-$(printf '%02d' "$PHASE_NUM").md"
|
||||
source scripts/lib/parse-phase-brief.sh "$PHASE_BRIEF"
|
||||
|
||||
CHANGED_FILES=$(git diff --name-only HEAD)
|
||||
ERRORS=0
|
||||
WARNINGS=0
|
||||
|
||||
echo "── Diff-driven Review: Phase $PHASE_NUM ($PHASE_TITLE) ──"
|
||||
echo ""
|
||||
|
||||
# ── 1. Scope check ────────────────────────────────────────
|
||||
echo "[Scope Check]"
|
||||
while IFS= read -r file; do
|
||||
[[ -z "$file" ]] && continue
|
||||
MATCHED=false
|
||||
for pattern in "${PHASE_SCOPE[@]}"; do
|
||||
# Direct match
|
||||
if [[ "$file" == "$pattern" ]]; then
|
||||
MATCHED=true
|
||||
break
|
||||
fi
|
||||
# Trailing /** glob: match any file under the directory
|
||||
if [[ "$pattern" == *"/**" ]]; then
|
||||
_dir="${pattern%/**}"
|
||||
if [[ "$file" == "$_dir/"* ]]; then
|
||||
MATCHED=true
|
||||
break
|
||||
fi
|
||||
fi
|
||||
# Trailing /* glob: match one level under directory
|
||||
if [[ "$pattern" == *"/*" ]] && [[ "$pattern" != *"/**" ]]; then
|
||||
_dir="${pattern%/*}"
|
||||
_rest="${file#"$_dir/"}"
|
||||
if [[ "$file" == "$_dir/"* ]] && [[ "$_rest" != *"/"* ]]; then
|
||||
MATCHED=true
|
||||
break
|
||||
fi
|
||||
fi
|
||||
done
|
||||
if ! $MATCHED; then
|
||||
echo " CRITICAL $file — outside Phase $PHASE_NUM scope"
|
||||
echo " (allowed: ${PHASE_SCOPE[*]})"
|
||||
((ERRORS++)) || true
|
||||
fi
|
||||
done <<< "$CHANGED_FILES"
|
||||
echo ""
|
||||
|
||||
# ── 2. No caret (^) in package.json ───────────────────────
|
||||
echo "[Rule: no-caret]"
|
||||
CARET_HITS=$(grep -rn '"\^' --include='package.json' . 2>/dev/null | grep -v 'node_modules' || true)
|
||||
if [[ -n "$CARET_HITS" ]]; then
|
||||
while IFS= read -r hit; do
|
||||
echo " CRITICAL $hit — caret (^) version found; must use tilde (~)"
|
||||
((ERRORS++)) || true
|
||||
done <<< "$CARET_HITS"
|
||||
else
|
||||
echo " OK"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# ── 3. No `any` type in TypeScript / Vue ──────────────────
|
||||
echo "[Rule: no-any-type]"
|
||||
ANY_HITS=$(grep -rn ': any\|as any' --include='*.ts' --include='*.vue' . 2>/dev/null \
|
||||
| grep -v 'node_modules' \
|
||||
| grep -v '//.*: any' || true)
|
||||
if [[ -n "$ANY_HITS" ]]; then
|
||||
while IFS= read -r hit; do
|
||||
echo " CRITICAL $hit — TypeScript 'any' type found"
|
||||
((ERRORS++)) || true
|
||||
done <<< "$ANY_HITS"
|
||||
else
|
||||
echo " OK"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# ── 4. Env var name must be VITE_APP_BASE_API ─────────────
|
||||
echo "[Rule: env-var-consistency]"
|
||||
BAD_ENV=$(grep -rn 'VITE_API_BASE_URL\|VITE_BASE_API\|VITE_APP_API\b' \
|
||||
--include='*.ts' --include='*.vue' --include='.env' --include='.env.*' . 2>/dev/null \
|
||||
| grep -v 'node_modules' || true)
|
||||
if [[ -n "$BAD_ENV" ]]; then
|
||||
while IFS= read -r hit; do
|
||||
echo " CRITICAL $hit — wrong env var name; must be VITE_APP_BASE_API"
|
||||
((ERRORS++)) || true
|
||||
done <<< "$BAD_ENV"
|
||||
else
|
||||
echo " OK"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# ── 5. Flyway: no IF NOT EXISTS ───────────────────────────
|
||||
echo "[Rule: flyway-no-if-not-exists]"
|
||||
IF_NOT_HITS=$(grep -rni 'IF NOT EXISTS' --include='*.sql' . 2>/dev/null | grep -v 'node_modules' || true)
|
||||
if [[ -n "$IF_NOT_HITS" ]]; then
|
||||
while IFS= read -r hit; do
|
||||
echo " CRITICAL $hit — Flyway migration must not use IF NOT EXISTS"
|
||||
((ERRORS++)) || true
|
||||
done <<< "$IF_NOT_HITS"
|
||||
else
|
||||
echo " OK"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# ── 6. Business tables must have all 7 audit columns ──────
|
||||
echo "[Rule: audit-columns]"
|
||||
AUDIT_ERRORS=0
|
||||
SQL_FILES=$(find . -name '*.sql' -path '*/migration/*' 2>/dev/null | grep -v 'node_modules' || true)
|
||||
while IFS= read -r sql_file; do
|
||||
[[ -z "$sql_file" ]] && continue
|
||||
# Find each CREATE TABLE statement name
|
||||
while IFS= read -r table; do
|
||||
[[ -z "$table" ]] && continue
|
||||
# Skip Quartz tables
|
||||
_tbl_lower=$(echo "$table" | tr '[:upper:]' '[:lower:]')
|
||||
if [[ "$_tbl_lower" == *"qrtz_"* ]]; then
|
||||
continue
|
||||
fi
|
||||
# Extract the table body between the CREATE TABLE and the matching );
|
||||
TABLE_BLOCK=$(awk "/CREATE TABLE[[:space:]]+${table}[[:space:]]*\(/{found=1} found{print} found && /\);/{exit}" "$sql_file" || true)
|
||||
for col in tenant_id created_by created_time updated_by updated_time deleted version; do
|
||||
if ! echo "$TABLE_BLOCK" | grep -qi "[[:space:]]${col}[[:space:]]"; then
|
||||
echo " CRITICAL $sql_file — table '$table' missing audit column: $col"
|
||||
((ERRORS++)) || true
|
||||
((AUDIT_ERRORS++)) || true
|
||||
fi
|
||||
done
|
||||
done < <(grep -oP '(?i)CREATE TABLE\s+\K\S+' "$sql_file" 2>/dev/null || true)
|
||||
done <<< "$SQL_FILES"
|
||||
if [[ "$AUDIT_ERRORS" -eq 0 ]]; then
|
||||
echo " OK"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# ── 7. DTO must not expose tenant_id ──────────────────────
|
||||
echo "[Rule: dto-no-tenant-id]"
|
||||
DTO_HITS=$(grep -rn 'tenantId\|tenant_id' \
|
||||
--include='*DTO.java' --include='*Dto.java' \
|
||||
--include='*Request.java' --include='*Response.java' \
|
||||
. 2>/dev/null | grep -v 'node_modules' || true)
|
||||
if [[ -n "$DTO_HITS" ]]; then
|
||||
while IFS= read -r hit; do
|
||||
echo " CRITICAL $hit — DTO must not expose tenantId"
|
||||
((ERRORS++)) || true
|
||||
done <<< "$DTO_HITS"
|
||||
else
|
||||
echo " OK"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# ── 8. Controller methods must have @PreAuthorize ─────────
|
||||
echo "[Rule: controller-preauthorize]"
|
||||
CTRL_ERRORS=0
|
||||
CONTROLLER_FILES=$(find . -name '*Controller.java' 2>/dev/null | grep -v 'node_modules' || true)
|
||||
while IFS= read -r ctrl; do
|
||||
[[ -z "$ctrl" ]] && continue
|
||||
while IFS= read -r method_line; do
|
||||
[[ -z "$method_line" ]] && continue
|
||||
LINE_NUM=$(echo "$method_line" | cut -d: -f1)
|
||||
LINE_CONTENT=$(echo "$method_line" | cut -d: -f2-)
|
||||
# Skip login and refresh endpoints
|
||||
if echo "$LINE_CONTENT" | grep -q 'login\|refresh'; then
|
||||
continue
|
||||
fi
|
||||
START=$((LINE_NUM > 3 ? LINE_NUM - 3 : 1))
|
||||
CONTEXT=$(sed -n "${START},${LINE_NUM}p" "$ctrl" 2>/dev/null || true)
|
||||
if ! echo "$CONTEXT" | grep -q '@PreAuthorize'; then
|
||||
echo " CRITICAL $ctrl:$LINE_NUM — mapping without @PreAuthorize"
|
||||
((ERRORS++)) || true
|
||||
((CTRL_ERRORS++)) || true
|
||||
fi
|
||||
done < <(grep -n '@\(Get\|Post\|Put\|Delete\|Patch\)Mapping' "$ctrl" 2>/dev/null || true)
|
||||
done <<< "$CONTROLLER_FILES"
|
||||
if [[ "$CTRL_ERRORS" -eq 0 ]]; then
|
||||
echo " OK"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# ── 9. No ${latest.version} in POM ───────────────────────
|
||||
echo "[Rule: no-latest-version]"
|
||||
POM_HITS=$(grep -rn '\${latest\.version}' --include='pom.xml' . 2>/dev/null | grep -v 'node_modules' || true)
|
||||
if [[ -n "$POM_HITS" ]]; then
|
||||
while IFS= read -r hit; do
|
||||
echo " CRITICAL $hit — \${latest.version} placeholder found in POM"
|
||||
((ERRORS++)) || true
|
||||
done <<< "$POM_HITS"
|
||||
else
|
||||
echo " OK"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# ── 10. DAG: data must not import security ────────────────
|
||||
echo "[Rule: dag-data-no-security]"
|
||||
DAG_HITS=$(grep -rn 'import com\.ljwx\.platform\.security' \
|
||||
ljwx-platform-data/src/ 2>/dev/null || true)
|
||||
if [[ -n "$DAG_HITS" ]]; then
|
||||
while IFS= read -r hit; do
|
||||
echo " CRITICAL $hit — data module imports security (DAG violation)"
|
||||
((ERRORS++)) || true
|
||||
done <<< "$DAG_HITS"
|
||||
else
|
||||
echo " OK"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# ── 11. DAG: security must not import data ────────────────
|
||||
echo "[Rule: dag-security-no-data]"
|
||||
DAG_HITS2=$(grep -rn 'import com\.ljwx\.platform\.data' \
|
||||
ljwx-platform-security/src/ 2>/dev/null || true)
|
||||
if [[ -n "$DAG_HITS2" ]]; then
|
||||
while IFS= read -r hit; do
|
||||
echo " CRITICAL $hit — security module imports data (DAG violation)"
|
||||
((ERRORS++)) || true
|
||||
done <<< "$DAG_HITS2"
|
||||
else
|
||||
echo " OK"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# ── Summary ───────────────────────────────────────────────
|
||||
echo "════════════════════════════════════════"
|
||||
echo " ERRORS: $ERRORS | WARNINGS: $WARNINGS"
|
||||
if [[ "$ERRORS" -gt 0 ]]; then
|
||||
echo " DIFF REVIEW: FAILED"
|
||||
exit 1
|
||||
else
|
||||
echo " DIFF REVIEW: PASSED"
|
||||
exit 0
|
||||
fi
|
||||
@@ -0,0 +1,8 @@
|
||||
# LJWX Platform 规格文档
|
||||
|
||||
本文件为入口指引,不包含实际规格内容。
|
||||
|
||||
- **AI Agent**: 请读取 `CLAUDE.md`(自动加载),然后按工作流程读取 `spec/phase/phase-{NN}.md`
|
||||
- **人类开发者**: 请读取 `spec/INDEX.md` 获取完整文档索引
|
||||
- 完整规格按模块拆分在 `spec/` 目录下
|
||||
- 版本号的唯一来源是 `CLAUDE.md` 的"版本锁定"段
|
||||
@@ -0,0 +1,83 @@
|
||||
# 硬约束
|
||||
|
||||
> 所有版本号见 CLAUDE.md "版本锁定"段,本文件不重复。
|
||||
|
||||
## 审计字段
|
||||
|
||||
所有业务表(Quartz 系统表除外)必须包含以下 7 列:
|
||||
|
||||
```sql
|
||||
tenant_id BIGINT NOT NULL,
|
||||
created_by BIGINT NOT NULL DEFAULT 0,
|
||||
created_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_by BIGINT NOT NULL DEFAULT 0,
|
||||
updated_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
deleted BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
version INT NOT NULL DEFAULT 1
|
||||
```
|
||||
|
||||
**规则:** NOT NULL + 有 DEFAULT,因此 INSERT 时即使拦截器未触发也不会报错。AuditFieldInterceptor(MyBatis Interceptor)在 INSERT/UPDATE 时从 CurrentUserHolder(core 接口)读取当前用户 ID 写入 created_by / updated_by。
|
||||
|
||||
**验收检查:** 每个 Flyway migration 中的 CREATE TABLE(Quartz 表除外)必须包含这 7 列关键字。gate-manifest.sh 中循环检查全部 7 列。
|
||||
|
||||
## 多租户行级隔离
|
||||
|
||||
所有查询通过 TenantLineInterceptor(MyBatis Interceptor)自动追加 WHERE tenant_id = ?。tenant_id 取自 CurrentTenantHolder.get()(core 接口)。DTO 中禁止出现 tenant_id 字段。前端禁止传递 tenant_id。
|
||||
|
||||
## JWT 认证
|
||||
|
||||
| 项目 | 值 |
|
||||
|------|-----|
|
||||
| Access Token 有效期 | 30 min |
|
||||
| Refresh Token 有效期 | 7 days |
|
||||
| 签名算法 | HS256 |
|
||||
| Payload claims | sub(userId), tenantId, username, type("access"|"refresh"), authorities(权限字符串数组) |
|
||||
| authorities claim name | authorities(自定义 claim,非 Spring 默认 scope) |
|
||||
| authorities converter | 自定义 JwtAuthenticationConverter:读取 authorities claim → 转为 SimpleGrantedAuthority,不添加 ROLE_ 前缀 |
|
||||
| ROLE_ 前缀 | 不使用。RBAC 权限均为 resource:action 格式。`@PreAuthorize("hasAuthority('user:read')")` |
|
||||
| 密钥配置 | application.yml 中 ljwx.jwt.secret 占位,.env.example 提供示例值 |
|
||||
|
||||
**Refresh 流程:** 前端 Axios 拦截 401 → 用 refresh token 调 /api/auth/refresh → 成功则重放原请求 → 失败则跳转登录。
|
||||
|
||||
## RBAC 权限
|
||||
|
||||
种子数据权限字符串(写入 sys_permission 表):
|
||||
|
||||
```
|
||||
user:read, user:write, user:delete,
|
||||
role:read, role:write, role:delete,
|
||||
tenant:read, tenant:write,
|
||||
job:read, job:write, job:execute,
|
||||
dict:read, dict:write,
|
||||
config:read, config:write,
|
||||
log:read, log:export,
|
||||
file:read, file:upload, file:delete,
|
||||
notice:read, notice:write,
|
||||
screen:read
|
||||
```
|
||||
|
||||
默认 admin 用户拥有全部权限。Controller 方法必须标注 @PreAuthorize。
|
||||
|
||||
## Quartz 调度
|
||||
|
||||
作业存储 per-tenant 隔离:JobKey(name="{jobId}", group="TENANT_{tenantId}")。Quartz 表使用标准 QRTZ_ 前缀,由 Flyway V010 脚本创建(不使用 IF NOT EXISTS,Quartz 表不含审计字段)。
|
||||
|
||||
## 操作日志
|
||||
|
||||
异步记录,独立线程池(core size=2, max=4, queue=1024)。日志体超 4096 字节截断。敏感字段脱敏规则:password → ***,phone → 中间四位 *,idCard → 中间段 *。
|
||||
|
||||
## 文件管理
|
||||
|
||||
Snowflake ID 命名。上传限制 50 MB。白名单后缀:jpg, jpeg, png, gif, webp, svg, pdf, doc, docx, xls, xlsx, ppt, pptx, txt, csv, zip, rar, 7z, mp4, mp3。存储路径 `${ljwx.file.base-path}/tenant_{tenantId}/{yyyy}/{MM}/{dd}/`。
|
||||
|
||||
## 缓存策略
|
||||
|
||||
字典(dict)和系统配置(config)使用 JVM 本地缓存(Caffeine,由 Spring Boot 管理)。TTL = 10 min。不使用 Redis / MQ / JPA。
|
||||
|
||||
## TypeScript 约束
|
||||
|
||||
禁止 any 类型。tsconfig.json 开启 strict: true。所有 API 返回值必须有类型定义(放在 packages/shared/src/types/)。
|
||||
|
||||
## Vue Router 5 兼容性约束
|
||||
|
||||
路由实现必须以 vue-router @~5.0.2 的 API 为准。Vue Router 5 相对 v4 有关键变更(如将 unplugin-vue-router 合并进核心包,路由文件约定发生变化)。以官方文档(https://router.vuejs.org/guide/migration/v4-to-v5)与类型定义为准,禁止按 v4 经验写代码。若某个 v4 API 在 v5 中已被标记 deprecated 或移除,必须使用 v5 推荐的替代方案。
|
||||
@@ -0,0 +1,226 @@
|
||||
# 架构
|
||||
|
||||
> 版本号见 CLAUDE.md "版本锁定"段。
|
||||
|
||||
## 仓库结构
|
||||
|
||||
```
|
||||
ljwx-platform/
|
||||
├── CLAUDE.md
|
||||
├── spec.md # stub 入口
|
||||
├── spec/ # 拆分后的规格文档
|
||||
├── .claude/ # Claude Code 扩展(skills / agents)
|
||||
├── pom.xml # Maven parent
|
||||
├── .mvn/wrapper/ # Maven Wrapper
|
||||
├── pnpm-workspace.yaml
|
||||
├── package.json # 前端 root
|
||||
├── .npmrc
|
||||
├── .nvmrc # 内容:22.22.0
|
||||
├── docker-compose.yml
|
||||
├── .env.example
|
||||
├── .gitignore
|
||||
├── .editorconfig
|
||||
│
|
||||
├── ljwx-platform-core/ # Java: 核心(接口 + 通用类)
|
||||
│ ├── pom.xml
|
||||
│ └── src/main/java/com/ljwx/platform/core/
|
||||
│ ├── result/ # Result, ErrorCode
|
||||
│ ├── id/ # SnowflakeIdGenerator
|
||||
│ ├── entity/ # BaseEntity (审计字段)
|
||||
│ └── context/ # CurrentUserHolder, CurrentTenantHolder 接口
|
||||
│
|
||||
├── ljwx-platform-data/ # Java: 数据层(仅依赖 core)
|
||||
│ ├── pom.xml
|
||||
│ └── src/main/java/com/ljwx/platform/data/
|
||||
│ ├── interceptor/ # AuditFieldInterceptor, TenantLineInterceptor
|
||||
│ └── config/
|
||||
│
|
||||
├── ljwx-platform-security/ # Java: 安全(仅依赖 core)
|
||||
│ ├── pom.xml
|
||||
│ └── src/main/java/com/ljwx/platform/security/
|
||||
│ ├── context/ # SecurityContextUserHolder, SecurityContextTenantHolder
|
||||
│ ├── jwt/
|
||||
│ ├── filter/
|
||||
│ └── config/
|
||||
│
|
||||
├── ljwx-platform-web/ # Java: Web 层(依赖 security + core 传递)
|
||||
│ ├── pom.xml
|
||||
│ └── src/main/java/com/ljwx/platform/web/
|
||||
│ ├── advice/ # GlobalExceptionHandler
|
||||
│ └── config/
|
||||
│
|
||||
├── ljwx-platform-app/ # Java: 应用层(聚合 web + data)
|
||||
│ ├── pom.xml
|
||||
│ └── src/main/java/com/ljwx/platform/app/
|
||||
│ ├── LjwxPlatformApplication.java
|
||||
│ ├── controller/
|
||||
│ ├── facade/
|
||||
│ ├── appservice/
|
||||
│ ├── domain/
|
||||
│ └── infra/
|
||||
│
|
||||
├── packages/
|
||||
│ └── shared/ # 前端共享 TS 包
|
||||
│ ├── package.json
|
||||
│ ├── tsconfig.json
|
||||
│ └── src/
|
||||
│ ├── types/
|
||||
│ ├── constants/
|
||||
│ └── utils/
|
||||
│
|
||||
├── ljwx-platform-admin/ # Vue3 管理后台
|
||||
│ ├── package.json
|
||||
│ ├── vite.config.ts
|
||||
│ ├── tsconfig.json
|
||||
│ ├── env.d.ts
|
||||
│ ├── .env.development
|
||||
│ ├── .env.production
|
||||
│ ├── index.html
|
||||
│ └── src/
|
||||
│ ├── main.ts
|
||||
│ ├── App.vue
|
||||
│ ├── router/
|
||||
│ ├── stores/
|
||||
│ ├── api/
|
||||
│ ├── composables/
|
||||
│ ├── layouts/
|
||||
│ ├── views/
|
||||
│ └── styles/
|
||||
│
|
||||
├── ljwx-platform-mobile/ # uni-app 移动端
|
||||
│ ├── package.json
|
||||
│ ├── manifest.json
|
||||
│ ├── pages.json
|
||||
│ └── src/
|
||||
│
|
||||
├── ljwx-platform-screen/ # Vue3 数据大屏
|
||||
│ ├── package.json
|
||||
│ ├── vite.config.ts
|
||||
│ └── src/
|
||||
│
|
||||
├── docs/
|
||||
│ ├── adr/ # Architecture Decision Records + 变更日志
|
||||
│ └── contracts/ # OpenAPI JSON
|
||||
│
|
||||
└── scripts/
|
||||
├── gates/
|
||||
│ ├── gate-all.sh
|
||||
│ ├── gate-compile.sh
|
||||
│ ├── gate-integration.sh
|
||||
│ ├── gate-contract.sh
|
||||
│ ├── gate-manifest.sh
|
||||
│ └── gate-nfr.sh
|
||||
├── tools/
|
||||
│ └── export-openapi.sh
|
||||
└── acceptance/
|
||||
└── smoke-test.sh
|
||||
```
|
||||
|
||||
## 模块依赖图(DAG — 禁止循环)
|
||||
|
||||
```
|
||||
┌──────────────────┐
|
||||
│ ljwx-core │ ← 仅接口 + 通用类
|
||||
└────────▲─────────┘
|
||||
│
|
||||
┌────────┴─────────┐
|
||||
│ │
|
||||
┌────────┴──────┐ ┌───────┴──────────┐
|
||||
│ ljwx-security │ │ ljwx-data │
|
||||
│ (implements │ │ (interceptors │
|
||||
│ holders from │ │ depend on core │
|
||||
│ core) │ │ interfaces) │
|
||||
└────────▲──────┘ └───────▲──────────┘
|
||||
│ │
|
||||
└────────┬─────────┘
|
||||
│
|
||||
┌────────┴─────────┐
|
||||
│ ljwx-web │ ← 依赖 security(+ core 传递)
|
||||
└────────▲─────────┘
|
||||
│
|
||||
┌────────┴─────────┐
|
||||
│ ljwx-app │ ← 聚合:web + data
|
||||
└──────────────────┘
|
||||
```
|
||||
|
||||
**依赖规则:**
|
||||
|
||||
| 模块 | 直接依赖 |
|
||||
|------|---------|
|
||||
| ljwx-platform-core | 无(仅外部依赖) |
|
||||
| ljwx-platform-security | core |
|
||||
| ljwx-platform-data | core |
|
||||
| ljwx-platform-web | security(core 经传递依赖获得) |
|
||||
| ljwx-platform-app | web + data(security 和 core 经传递依赖获得) |
|
||||
|
||||
**关键:** security 和 data 互不依赖,仅共同依赖 core 的接口契约。data 模块的 AuditFieldInterceptor 和 TenantLineInterceptor 通过 Spring 依赖注入获取 CurrentUserHolder / CurrentTenantHolder 的实现(由 security 模块在运行时提供)。
|
||||
|
||||
## Core 模块关键接口
|
||||
|
||||
```java
|
||||
// com.ljwx.platform.core.context.CurrentUserHolder
|
||||
public interface CurrentUserHolder {
|
||||
Long getUserId();
|
||||
String getUsername();
|
||||
}
|
||||
|
||||
// com.ljwx.platform.core.context.CurrentTenantHolder
|
||||
public interface CurrentTenantHolder {
|
||||
Long getTenantId();
|
||||
}
|
||||
```
|
||||
|
||||
## POM 依赖声明
|
||||
|
||||
### ljwx-platform-security/pom.xml
|
||||
|
||||
```xml
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.ljwx.platform</groupId>
|
||||
<artifactId>ljwx-platform-core</artifactId>
|
||||
</dependency>
|
||||
<!-- Spring Security, JWT libs, etc. -->
|
||||
</dependencies>
|
||||
```
|
||||
|
||||
### ljwx-platform-data/pom.xml
|
||||
|
||||
```xml
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.ljwx.platform</groupId>
|
||||
<artifactId>ljwx-platform-core</artifactId>
|
||||
</dependency>
|
||||
<!-- MyBatis, PostgreSQL driver, etc. -->
|
||||
</dependencies>
|
||||
```
|
||||
|
||||
### ljwx-platform-web/pom.xml
|
||||
|
||||
```xml
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.ljwx.platform</groupId>
|
||||
<artifactId>ljwx-platform-security</artifactId>
|
||||
<!-- core comes transitively -->
|
||||
</dependency>
|
||||
</dependencies>
|
||||
```
|
||||
|
||||
### ljwx-platform-app/pom.xml
|
||||
|
||||
```xml
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.ljwx.platform</groupId>
|
||||
<artifactId>ljwx-platform-web</artifactId>
|
||||
<!-- security + core come transitively -->
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.ljwx.platform</groupId>
|
||||
<artifactId>ljwx-platform-data</artifactId>
|
||||
<!-- core comes transitively -->
|
||||
</dependency>
|
||||
</dependencies>
|
||||
```
|
||||
+124
@@ -0,0 +1,124 @@
|
||||
# API 设计
|
||||
|
||||
## 统一响应
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"message": "success",
|
||||
"data": {},
|
||||
"traceId": "uuid"
|
||||
}
|
||||
```
|
||||
|
||||
## 错误码(整数枚举)
|
||||
|
||||
| 码 | 含义 |
|
||||
|-----|------|
|
||||
| 200 | 成功 |
|
||||
| 400001 | 参数校验失败 |
|
||||
| 401001 | Token 无效 |
|
||||
| 401002 | Token 过期 |
|
||||
| 403001 | 租户拒绝 |
|
||||
| 403002 | 权限不足 |
|
||||
| 404001 | 资源不存在 |
|
||||
| 500001 | 系统内部错误 |
|
||||
|
||||
## 路由表
|
||||
|
||||
### Auth
|
||||
|
||||
| 方法 | 路径 | 权限 | 说明 |
|
||||
|------|------|------|------|
|
||||
| POST | /api/auth/login | 匿名 | 登录 |
|
||||
| POST | /api/auth/refresh | 匿名 | 刷新 Token |
|
||||
| POST | /api/auth/logout | 已认证 | 登出 |
|
||||
|
||||
### Users
|
||||
|
||||
| 方法 | 路径 | 权限 | 说明 |
|
||||
|------|------|------|------|
|
||||
| GET | /api/users | user:read | 用户列表 |
|
||||
| POST | /api/users | user:write | 创建用户 |
|
||||
| PUT | /api/users/{id} | user:write | 更新用户 |
|
||||
| DELETE | /api/users/{id} | user:delete | 删除用户 |
|
||||
|
||||
### Roles & Permissions
|
||||
|
||||
| 方法 | 路径 | 权限 | 说明 |
|
||||
|------|------|------|------|
|
||||
| GET | /api/roles | role:read | 角色列表 |
|
||||
| POST | /api/roles | role:write | 创建角色 |
|
||||
| PUT | /api/roles/{id} | role:write | 更新角色 |
|
||||
| DELETE | /api/roles/{id} | role:delete | 删除角色 |
|
||||
| GET | /api/permissions | role:read | 权限列表 |
|
||||
|
||||
### Tenants
|
||||
|
||||
| 方法 | 路径 | 权限 | 说明 |
|
||||
|------|------|------|------|
|
||||
| GET | /api/tenants | tenant:read | 租户列表 |
|
||||
| POST | /api/tenants | tenant:write | 创建租户 |
|
||||
| PUT | /api/tenants/{id} | tenant:write | 更新租户 |
|
||||
|
||||
### Jobs
|
||||
|
||||
| 方法 | 路径 | 权限 | 说明 |
|
||||
|------|------|------|------|
|
||||
| GET | /api/jobs | job:read | 定时任务列表 |
|
||||
| POST | /api/jobs | job:write | 创建任务 |
|
||||
| PUT | /api/jobs/{id} | job:write | 更新任务 |
|
||||
| POST | /api/jobs/{id}/execute | job:execute | 立即执行 |
|
||||
| POST | /api/jobs/{id}/pause | job:write | 暂停 |
|
||||
| POST | /api/jobs/{id}/resume | job:write | 恢复 |
|
||||
|
||||
### Dicts
|
||||
|
||||
| 方法 | 路径 | 权限 | 说明 |
|
||||
|------|------|------|------|
|
||||
| GET | /api/dicts | dict:read | 字典列表 |
|
||||
| POST | /api/dicts | dict:write | 创建字典 |
|
||||
| PUT | /api/dicts/{id} | dict:write | 更新字典 |
|
||||
| GET | /api/dicts/type/{type} | dict:read | 按类型查字典项 |
|
||||
|
||||
### Configs
|
||||
|
||||
| 方法 | 路径 | 权限 | 说明 |
|
||||
|------|------|------|------|
|
||||
| GET | /api/configs | config:read | 配置列表 |
|
||||
| POST | /api/configs | config:write | 创建配置 |
|
||||
| PUT | /api/configs/{id} | config:write | 更新配置 |
|
||||
| GET | /api/configs/key/{key} | config:read | 按 key 查配置 |
|
||||
|
||||
### Logs
|
||||
|
||||
| 方法 | 路径 | 权限 | 说明 |
|
||||
|------|------|------|------|
|
||||
| GET | /api/logs/operation | log:read | 操作日志列表 |
|
||||
| GET | /api/logs/login | log:read | 登录日志列表 |
|
||||
| POST | /api/logs/export | log:export | 导出日志 |
|
||||
|
||||
### Files
|
||||
|
||||
| 方法 | 路径 | 权限 | 说明 |
|
||||
|------|------|------|------|
|
||||
| GET | /api/files | file:read | 文件列表 |
|
||||
| POST | /api/files/upload | file:upload | 上传文件 |
|
||||
| DELETE | /api/files/{id} | file:delete | 删除文件 |
|
||||
| GET | /api/files/{id}/download | file:read | 下载文件 |
|
||||
|
||||
### Notices
|
||||
|
||||
| 方法 | 路径 | 权限 | 说明 |
|
||||
|------|------|------|------|
|
||||
| GET | /api/notices | notice:read | 通知列表 |
|
||||
| POST | /api/notices | notice:write | 发布通知 |
|
||||
| PUT | /api/notices/{id} | notice:write | 更新通知 |
|
||||
|
||||
### Screen
|
||||
|
||||
| 方法 | 路径 | 权限 | 说明 |
|
||||
|------|------|------|------|
|
||||
| GET | /api/screen/overview | screen:read | 大屏概览数据 |
|
||||
| GET | /api/screen/realtime | screen:read | 大屏实时数据 |
|
||||
| GET | /api/screen/trend | screen:read | 大屏趋势数据 |
|
||||
@@ -0,0 +1,35 @@
|
||||
# 数据库迁移(Flyway)
|
||||
|
||||
## 规则
|
||||
|
||||
- 不使用 `IF NOT EXISTS`
|
||||
- 所有 DDL 在 `V{NNN}__description.sql` 中
|
||||
- Quartz 表不含审计字段
|
||||
- 其余业务表必须含 7 列审计字段(见 spec/01-constraints.md §审计字段)
|
||||
- 迁移文件位于 `ljwx-platform-app/src/main/resources/db/migration/`
|
||||
|
||||
## 迁移清单
|
||||
|
||||
| 文件 | 说明 |
|
||||
|------|------|
|
||||
| V001__init_schema.sql | 创建 sys_tenant 表 |
|
||||
| V002__create_user.sql | 创建 sys_user 表 |
|
||||
| V003__create_role.sql | 创建 sys_role、sys_user_role 表 |
|
||||
| V004__create_permission.sql | 创建 sys_permission、sys_role_permission 表 |
|
||||
| V005__seed_default_tenant.sql | 插入默认租户 (id=1, name='默认租户') |
|
||||
| V006__seed_admin_user.sql | 插入 admin 用户 (BCrypt hash of Admin@12345, cost=10) |
|
||||
| V007__seed_permissions.sql | 插入所有权限字符串 |
|
||||
| V008__seed_admin_role.sql | 插入 admin 角色并关联全部权限 |
|
||||
| V009__assign_admin_role.sql | 将 admin 角色分配给 admin 用户 |
|
||||
| V010__create_quartz_tables.sql | Quartz 标准 PostgreSQL DDL(无审计字段) |
|
||||
| V011__create_sys_job.sql | 创建 sys_job 业务表(含审计字段) |
|
||||
| V012__create_sys_dict_type.sql | 字典类型表 |
|
||||
| V013__create_sys_dict_data.sql | 字典数据表 |
|
||||
| V014__create_sys_config.sql | 系统配置表 |
|
||||
| V015__create_sys_operation_log.sql | 操作日志表 |
|
||||
| V016__create_sys_login_log.sql | 登录日志表 |
|
||||
| V017__create_sys_file.sql | 文件管理表 |
|
||||
| V018__create_sys_notice.sql | 通知表 |
|
||||
| V019__seed_dict_data.sql | 初始字典数据(性别、状态等) |
|
||||
| V020__seed_config_data.sql | 初始配置数据 |
|
||||
| V021__create_indexes.sql | 常用索引 |
|
||||
@@ -0,0 +1,84 @@
|
||||
# 后端配置
|
||||
|
||||
> 版本号见 CLAUDE.md "版本锁定"段。
|
||||
|
||||
## application.yml 骨架
|
||||
|
||||
```yaml
|
||||
server:
|
||||
port: 8080
|
||||
servlet:
|
||||
context-path: /
|
||||
|
||||
spring:
|
||||
application:
|
||||
name: ljwx-platform
|
||||
datasource:
|
||||
url: jdbc:postgresql://${DB_HOST:localhost}:${DB_PORT:5432}/${DB_NAME:ljwx_platform}
|
||||
username: ${DB_USERNAME:postgres}
|
||||
password: ${DB_PASSWORD:postgres}
|
||||
driver-class-name: org.postgresql.Driver
|
||||
flyway:
|
||||
enabled: true
|
||||
locations: classpath:db/migration
|
||||
baseline-on-migrate: false
|
||||
quartz:
|
||||
job-store-type: jdbc
|
||||
jdbc:
|
||||
initialize-schema: never
|
||||
properties:
|
||||
org.quartz:
|
||||
scheduler:
|
||||
instanceName: LjwxScheduler
|
||||
instanceId: AUTO
|
||||
jobStore:
|
||||
class: org.quartz.impl.jdbcjobstore.JobStoreTX
|
||||
driverDelegateClass: org.quartz.impl.jdbcjobstore.PostgreSQLDelegate
|
||||
tablePrefix: QRTZ_
|
||||
isClustered: true
|
||||
clusterCheckinInterval: 15000
|
||||
threadPool:
|
||||
threadCount: 10
|
||||
servlet:
|
||||
multipart:
|
||||
max-file-size: 50MB
|
||||
max-request-size: 50MB
|
||||
cache:
|
||||
type: caffeine
|
||||
caffeine:
|
||||
spec: maximumSize=500,expireAfterWrite=600s
|
||||
|
||||
mybatis:
|
||||
mapper-locations: classpath*:mapper/**/*.xml
|
||||
configuration:
|
||||
map-underscore-to-camel-case: true
|
||||
default-enum-type-handler: org.apache.ibatis.type.EnumTypeHandler
|
||||
|
||||
springdoc:
|
||||
api-docs:
|
||||
path: /v3/api-docs
|
||||
swagger-ui:
|
||||
path: /swagger-ui.html
|
||||
|
||||
ljwx:
|
||||
jwt:
|
||||
secret: ${JWT_SECRET:your-256-bit-secret-key-here-change-in-production}
|
||||
access-token-expiration: 1800
|
||||
refresh-token-expiration: 604800
|
||||
file:
|
||||
base-path: ${FILE_BASE_PATH:./uploads}
|
||||
|
||||
management:
|
||||
endpoints:
|
||||
web:
|
||||
exposure:
|
||||
include: health,info,flyway
|
||||
endpoint:
|
||||
health:
|
||||
show-details: when-authorized
|
||||
|
||||
logging:
|
||||
level:
|
||||
com.ljwx.platform: DEBUG
|
||||
org.springframework.security: DEBUG
|
||||
```
|
||||
@@ -0,0 +1,351 @@
|
||||
# 前端配置
|
||||
|
||||
> 版本号见 CLAUDE.md "版本锁定"段。本文件中的 package.json 版本号均写 "见 CLAUDE.md",实际生成时从 CLAUDE.md 取值。
|
||||
|
||||
## pnpm-workspace.yaml
|
||||
|
||||
```yaml
|
||||
packages:
|
||||
- 'packages/*'
|
||||
- 'ljwx-platform-admin'
|
||||
- 'ljwx-platform-mobile'
|
||||
- 'ljwx-platform-screen'
|
||||
```
|
||||
|
||||
## Root package.json
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "ljwx-platform-frontend",
|
||||
"private": true,
|
||||
"packageManager": "pnpm@10.30.1",
|
||||
"engines": {
|
||||
"node": ">=20.19.0 || >=22.12.0"
|
||||
},
|
||||
"scripts": {
|
||||
"dev:admin": "pnpm --filter ljwx-platform-admin dev",
|
||||
"dev:mobile": "pnpm --filter ljwx-platform-mobile dev:h5",
|
||||
"dev:screen": "pnpm --filter ljwx-platform-screen dev",
|
||||
"build:shared": "pnpm --filter @ljwx/shared build",
|
||||
"build:admin": "pnpm run build:shared && pnpm --filter ljwx-platform-admin build",
|
||||
"build:mobile": "pnpm run build:shared && pnpm --filter ljwx-platform-mobile build:h5",
|
||||
"build:screen": "pnpm run build:shared && pnpm --filter ljwx-platform-screen build",
|
||||
"build:all": "pnpm run build:shared && pnpm --filter './ljwx-platform-*' build",
|
||||
"type-check": "pnpm --filter './ljwx-platform-*' type-check",
|
||||
"lint": "pnpm --filter './ljwx-platform-*' lint"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## .npmrc
|
||||
|
||||
```
|
||||
shamefully-hoist=true
|
||||
strict-peer-dependencies=false
|
||||
auto-install-peers=true
|
||||
```
|
||||
|
||||
## .nvmrc
|
||||
|
||||
```
|
||||
22.22.0
|
||||
```
|
||||
|
||||
## .env.example
|
||||
|
||||
```bash
|
||||
# ===== Backend =====
|
||||
DB_HOST=localhost
|
||||
DB_PORT=5432
|
||||
DB_NAME=ljwx_platform
|
||||
DB_USERNAME=postgres
|
||||
DB_PASSWORD=postgres
|
||||
JWT_SECRET=your-256-bit-secret-key-here-change-in-production
|
||||
FILE_BASE_PATH=./uploads
|
||||
|
||||
# ===== Frontend =====
|
||||
VITE_APP_BASE_API=/api
|
||||
VITE_APP_TITLE=LJWX Platform
|
||||
```
|
||||
|
||||
**注意:** 前端统一使用 `VITE_APP_BASE_API`。Admin / Mobile / Screen 的 `.env.development` 和 `.env.production` 均使用此变量名。
|
||||
|
||||
## Admin package.json
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "ljwx-platform-admin",
|
||||
"private": true,
|
||||
"version": "1.0.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "vue-tsc --noEmit && vite build",
|
||||
"type-check": "vue-tsc --noEmit",
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"vue": "~3.5.28",
|
||||
"vue-router": "~5.0.2",
|
||||
"pinia": "~3.0.4",
|
||||
"element-plus": "~2.13.2",
|
||||
"@element-plus/icons-vue": "~2.3.2",
|
||||
"axios": "~1.13.5",
|
||||
"@vueuse/core": "~14.2.1",
|
||||
"nprogress": "~0.2.0",
|
||||
"dayjs": "~1.11.19",
|
||||
"@ljwx/shared": "workspace:*"
|
||||
},
|
||||
"devDependencies": {
|
||||
"vite": "~7.3.1",
|
||||
"typescript": "~5.9.3",
|
||||
"vue-tsc": "~3.2.4",
|
||||
"@vitejs/plugin-vue": "~6.0.0",
|
||||
"unplugin-auto-import": "~21.0.0",
|
||||
"unplugin-vue-components": "~31.0.0",
|
||||
"sass": "~1.97.3",
|
||||
"@types/nprogress": "~0.2.3"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Admin vite.config.ts
|
||||
|
||||
```typescript
|
||||
import { defineConfig } from 'vite'
|
||||
import vue from '@vitejs/plugin-vue'
|
||||
import AutoImport from 'unplugin-auto-import/vite'
|
||||
import Components from 'unplugin-vue-components/vite'
|
||||
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'
|
||||
import { fileURLToPath, URL } from 'node:url'
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [
|
||||
vue(),
|
||||
AutoImport({
|
||||
imports: ['vue', 'vue-router', 'pinia'],
|
||||
resolvers: [ElementPlusResolver()],
|
||||
dts: 'src/auto-imports.d.ts',
|
||||
}),
|
||||
Components({
|
||||
resolvers: [ElementPlusResolver()],
|
||||
dts: 'src/components.d.ts',
|
||||
}),
|
||||
],
|
||||
resolve: {
|
||||
alias: {
|
||||
'@': fileURLToPath(new URL('./src', import.meta.url)),
|
||||
},
|
||||
},
|
||||
server: {
|
||||
port: 3000,
|
||||
proxy: {
|
||||
'/api': {
|
||||
target: 'http://localhost:8080',
|
||||
changeOrigin: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
## Admin Axios 封装要点
|
||||
|
||||
```typescript
|
||||
// src/api/request.ts
|
||||
import axios from 'axios'
|
||||
import type { AxiosInstance, InternalAxiosRequestConfig, AxiosResponse } from 'axios'
|
||||
import { useUserStore } from '@/stores/user'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import router from '@/router'
|
||||
|
||||
const service: AxiosInstance = axios.create({
|
||||
baseURL: import.meta.env.VITE_APP_BASE_API,
|
||||
timeout: 15000,
|
||||
})
|
||||
|
||||
// 请求拦截:注入 Authorization
|
||||
service.interceptors.request.use((config: InternalAxiosRequestConfig) => {
|
||||
const userStore = useUserStore()
|
||||
if (userStore.accessToken) {
|
||||
config.headers.Authorization = `Bearer ${userStore.accessToken}`
|
||||
}
|
||||
return config
|
||||
})
|
||||
|
||||
// 响应拦截:处理 401 刷新逻辑
|
||||
let isRefreshing = false
|
||||
let failedQueue: Array<{
|
||||
resolve: (value?: unknown) => void
|
||||
reject: (reason?: unknown) => void
|
||||
}> = []
|
||||
|
||||
service.interceptors.response.use(
|
||||
(response: AxiosResponse) => {
|
||||
const { code, message, data } = response.data
|
||||
if (code !== 200) {
|
||||
ElMessage.error(message || '请求失败')
|
||||
return Promise.reject(new Error(message))
|
||||
}
|
||||
return data
|
||||
},
|
||||
async (error) => {
|
||||
const originalRequest = error.config
|
||||
if (error.response?.status === 401 && !originalRequest._retry) {
|
||||
if (isRefreshing) {
|
||||
return new Promise((resolve, reject) => {
|
||||
failedQueue.push({ resolve, reject })
|
||||
}).then(() => service(originalRequest))
|
||||
}
|
||||
originalRequest._retry = true
|
||||
isRefreshing = true
|
||||
try {
|
||||
const userStore = useUserStore()
|
||||
await userStore.refreshToken()
|
||||
failedQueue.forEach(({ resolve }) => resolve(undefined))
|
||||
failedQueue = []
|
||||
return service(originalRequest)
|
||||
} catch {
|
||||
failedQueue.forEach(({ reject }) => reject(error))
|
||||
failedQueue = []
|
||||
const userStore = useUserStore()
|
||||
userStore.logout()
|
||||
router.push('/login')
|
||||
return Promise.reject(error)
|
||||
} finally {
|
||||
isRefreshing = false
|
||||
}
|
||||
}
|
||||
ElMessage.error(error.response?.data?.message || '网络错误')
|
||||
return Promise.reject(error)
|
||||
},
|
||||
)
|
||||
|
||||
export default service
|
||||
```
|
||||
|
||||
## Mobile package.json
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "ljwx-platform-mobile",
|
||||
"version": "1.0.0",
|
||||
"scripts": {
|
||||
"dev:h5": "uni -p h5",
|
||||
"build:h5": "uni build -p h5"
|
||||
},
|
||||
"dependencies": {
|
||||
"@dcloudio/uni-app": "~3.0.0-alpha-4060620250520001",
|
||||
"@dcloudio/uni-h5": "~3.0.0-alpha-4060620250520001",
|
||||
"@dcloudio/uni-components": "~3.0.0-alpha-4060620250520001",
|
||||
"vue": "~3.5.28",
|
||||
"pinia": "~3.0.4",
|
||||
"axios": "~1.13.5",
|
||||
"@ljwx/shared": "workspace:*"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@dcloudio/vite-plugin-uni": "~3.0.0-alpha-4060620250520001",
|
||||
"typescript": "~5.9.3",
|
||||
"vite": "~7.3.1"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Mobile pages.json
|
||||
|
||||
```json
|
||||
{
|
||||
"pages": [
|
||||
{ "path": "pages/login/index", "style": { "navigationBarTitleText": "登录" } },
|
||||
{ "path": "pages/home/index", "style": { "navigationBarTitleText": "首页" } },
|
||||
{ "path": "pages/work/index", "style": { "navigationBarTitleText": "工作台" } },
|
||||
{ "path": "pages/message/index", "style": { "navigationBarTitleText": "消息" } },
|
||||
{ "path": "pages/mine/index", "style": { "navigationBarTitleText": "我的" } }
|
||||
],
|
||||
"tabBar": {
|
||||
"list": [
|
||||
{ "pagePath": "pages/home/index", "text": "首页" },
|
||||
{ "pagePath": "pages/work/index", "text": "工作台" },
|
||||
{ "pagePath": "pages/message/index", "text": "消息" },
|
||||
{ "pagePath": "pages/mine/index", "text": "我的" }
|
||||
]
|
||||
},
|
||||
"globalStyle": {
|
||||
"navigationBarTextStyle": "black",
|
||||
"navigationBarTitleText": "LJWX",
|
||||
"navigationBarBackgroundColor": "#ffffff"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Screen package.json
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "ljwx-platform-screen",
|
||||
"private": true,
|
||||
"version": "1.0.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "vue-tsc --noEmit && vite build",
|
||||
"type-check": "vue-tsc --noEmit",
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"vue": "~3.5.28",
|
||||
"echarts": "~6.0.0",
|
||||
"@kjgl77/datav-vue3": "~1.7.4",
|
||||
"axios": "~1.13.5",
|
||||
"@vueuse/core": "~14.2.1",
|
||||
"@ljwx/shared": "workspace:*"
|
||||
},
|
||||
"devDependencies": {
|
||||
"vite": "~7.3.1",
|
||||
"typescript": "~5.9.3",
|
||||
"vue-tsc": "~3.2.4",
|
||||
"@vitejs/plugin-vue": "~6.0.0",
|
||||
"sass": "~1.97.3"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Screen ECharts 暗色主题注册
|
||||
|
||||
```typescript
|
||||
// src/utils/echarts-setup.ts
|
||||
import * as echarts from 'echarts'
|
||||
import darkTheme from './echarts-dark-theme'
|
||||
|
||||
echarts.registerTheme('ljwx-dark', darkTheme)
|
||||
|
||||
export { echarts }
|
||||
```
|
||||
|
||||
## Screen 自适应缩放 Composable
|
||||
|
||||
```typescript
|
||||
// src/composables/useScreenAdapt.ts
|
||||
import { ref, onMounted, onUnmounted } from 'vue'
|
||||
|
||||
export function useScreenAdapt(designWidth = 1920, designHeight = 1080) {
|
||||
const scale = ref(1)
|
||||
|
||||
function updateScale() {
|
||||
const scaleX = window.innerWidth / designWidth
|
||||
const scaleY = window.innerHeight / designHeight
|
||||
scale.value = Math.min(scaleX, scaleY)
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
updateScale()
|
||||
window.addEventListener('resize', updateScale)
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
window.removeEventListener('resize', updateScale)
|
||||
})
|
||||
|
||||
return { scale }
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,216 @@
|
||||
# DevOps
|
||||
|
||||
> 版本号见 CLAUDE.md "版本锁定"段。
|
||||
|
||||
## 工具链安装策略
|
||||
|
||||
| 工具 | 推荐安装方式 | CI 跳过策略 |
|
||||
|------|-------------|------------|
|
||||
| JDK 21 | sdkman / actions/setup-java@v4 | 若 `java -version` 满足则跳过 |
|
||||
| Maven | Maven Wrapper `./mvnw` | 始终使用 wrapper |
|
||||
| Node.js | fnm / actions/setup-node@v4 | 若 `node -v` 满足则跳过 |
|
||||
| pnpm | `corepack enable && corepack prepare` | corepack 自动处理 |
|
||||
| Docker | 系统级 / docker/setup-buildx-action | Testcontainers 必需 |
|
||||
|
||||
## Docker Compose
|
||||
|
||||
```yaml
|
||||
version: '3.8'
|
||||
|
||||
services:
|
||||
postgres:
|
||||
image: postgres:16.12-alpine
|
||||
container_name: ljwx-postgres
|
||||
ports:
|
||||
- "${DB_PORT:-5432}:5432"
|
||||
environment:
|
||||
POSTGRES_DB: ${DB_NAME:-ljwx_platform}
|
||||
POSTGRES_USER: ${DB_USERNAME:-postgres}
|
||||
POSTGRES_PASSWORD: ${DB_PASSWORD:-postgres}
|
||||
volumes:
|
||||
- pgdata:/var/lib/postgresql/data
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U ${DB_USERNAME:-postgres} -d ${DB_NAME:-ljwx_platform}"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
|
||||
volumes:
|
||||
pgdata:
|
||||
```
|
||||
|
||||
## CI Gate 脚本
|
||||
|
||||
### scripts/gates/gate-all.sh
|
||||
|
||||
```bash
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
echo "===== LJWX Platform CI Gate ====="
|
||||
|
||||
echo "[1/6] Manifest Check..."
|
||||
bash scripts/gates/gate-manifest.sh
|
||||
|
||||
echo "[2/6] Backend Compile + Unit Test..."
|
||||
bash scripts/gates/gate-compile.sh
|
||||
|
||||
echo "[3/6] Integration Test (Testcontainers)..."
|
||||
bash scripts/gates/gate-integration.sh
|
||||
|
||||
echo "[4/6] Contract Check (OpenAPI)..."
|
||||
bash scripts/gates/gate-contract.sh
|
||||
|
||||
echo "[5/6] NFR Check..."
|
||||
bash scripts/gates/gate-nfr.sh
|
||||
|
||||
echo "[6/6] Frontend Build..."
|
||||
pnpm install --frozen-lockfile
|
||||
pnpm run build:all
|
||||
|
||||
echo "===== ALL GATES PASSED ====="
|
||||
```
|
||||
|
||||
### scripts/gates/gate-compile.sh
|
||||
|
||||
```bash
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
./mvnw clean verify -DskipITs -T1C
|
||||
```
|
||||
|
||||
### scripts/gates/gate-integration.sh
|
||||
|
||||
```bash
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
./mvnw verify -PIT -Dfailsafe.rerunFailingTestsCount=1
|
||||
```
|
||||
|
||||
### scripts/gates/gate-manifest.sh
|
||||
|
||||
```bash
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
PHASE_FILE="${1:-PHASE_MANIFEST.txt}"
|
||||
|
||||
echo "===== Phase-Local Manifest Check ====="
|
||||
|
||||
# ---- Phase-local file existence check ----
|
||||
if [[ -f "$PHASE_FILE" ]]; then
|
||||
MISSING=0
|
||||
while IFS= read -r file; do
|
||||
[[ -z "$file" || "$file" =~ ^# ]] && continue
|
||||
if [[ ! -e "$file" ]]; then
|
||||
echo "MISSING: $file"
|
||||
MISSING=$((MISSING + 1))
|
||||
fi
|
||||
done < "$PHASE_FILE"
|
||||
|
||||
if [[ $MISSING -gt 0 ]]; then
|
||||
echo "FAIL: $MISSING file(s) missing from phase manifest."
|
||||
exit 1
|
||||
fi
|
||||
echo "Phase-local file check: OK"
|
||||
else
|
||||
echo "WARN: No phase manifest file found ($PHASE_FILE). Skipping phase-local check."
|
||||
fi
|
||||
|
||||
# ---- Check for ^ in package.json (repo-wide) ----
|
||||
CARET_FOUND=$(grep -RIn '"\^' \
|
||||
package.json \
|
||||
packages/ \
|
||||
ljwx-platform-admin/ \
|
||||
ljwx-platform-mobile/ \
|
||||
ljwx-platform-screen/ \
|
||||
2>/dev/null || true)
|
||||
if [[ -n "$CARET_FOUND" ]]; then
|
||||
echo "FAIL: Found caret (^) version specifiers. Use tilde (~) only."
|
||||
echo "$CARET_FOUND"
|
||||
exit 1
|
||||
fi
|
||||
echo "Caret check: OK"
|
||||
|
||||
# ---- Check env var consistency (all three frontends) ----
|
||||
DEPRECATED_VAR=$(grep -RIn 'VITE_API_BASE_URL' \
|
||||
ljwx-platform-admin/ \
|
||||
ljwx-platform-mobile/ \
|
||||
ljwx-platform-screen/ \
|
||||
2>/dev/null || true)
|
||||
if [[ -n "$DEPRECATED_VAR" ]]; then
|
||||
echo "FAIL: Found deprecated VITE_API_BASE_URL. Use VITE_APP_BASE_API."
|
||||
echo "$DEPRECATED_VAR"
|
||||
exit 1
|
||||
fi
|
||||
echo "Env var consistency check: OK"
|
||||
|
||||
# ---- Check audit fields in SQL migrations (7 columns) ----
|
||||
AUDIT_COLUMNS="tenant_id created_by created_time updated_by updated_time deleted version"
|
||||
for sql in ljwx-platform-app/src/main/resources/db/migration/V*.sql; do
|
||||
[[ ! -f "$sql" ]] && continue
|
||||
if grep -qi "QRTZ_" "$sql"; then continue; fi
|
||||
if grep -qi "CREATE TABLE" "$sql"; then
|
||||
for col in $AUDIT_COLUMNS; do
|
||||
if ! grep -qi "$col" "$sql"; then
|
||||
echo "FAIL: $sql has CREATE TABLE but missing audit column: $col"
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
fi
|
||||
done
|
||||
echo "Audit field check: OK"
|
||||
|
||||
echo "===== Manifest Check PASSED ====="
|
||||
```
|
||||
|
||||
### scripts/gates/gate-contract.sh
|
||||
|
||||
```bash
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
bash scripts/tools/export-openapi.sh
|
||||
if [[ ! -f docs/contracts/openapi.json ]]; then
|
||||
echo "FAIL: openapi.json not generated"
|
||||
exit 1
|
||||
fi
|
||||
echo "Contract check PASSED"
|
||||
```
|
||||
|
||||
### scripts/acceptance/smoke-test.sh
|
||||
|
||||
```bash
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
BASE_URL="${1:-http://localhost:8080}"
|
||||
|
||||
echo "===== Smoke Test ====="
|
||||
|
||||
# Health check
|
||||
for i in $(seq 1 30); do
|
||||
if curl -sf "$BASE_URL/actuator/health" > /dev/null 2>&1; then
|
||||
echo "Health check passed (attempt $i)"
|
||||
break
|
||||
fi
|
||||
if [[ $i -eq 30 ]]; then
|
||||
echo "FAIL: Health check timed out"
|
||||
exit 1
|
||||
fi
|
||||
sleep 2
|
||||
done
|
||||
|
||||
# Login test
|
||||
RESP=$(curl -sf -X POST "$BASE_URL/api/auth/login" \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{"username":"admin","password":"Admin@12345"}')
|
||||
|
||||
CODE=$(echo "$RESP" | grep -o '"code":[0-9]*' | head -1 | cut -d: -f2)
|
||||
if [[ "$CODE" != "200" ]]; then
|
||||
echo "FAIL: Login returned code $CODE"
|
||||
echo "$RESP"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "===== Smoke Test PASSED ====="
|
||||
```
|
||||
@@ -0,0 +1,45 @@
|
||||
# 输出规则
|
||||
|
||||
## Phase 输出格式
|
||||
|
||||
每个 Phase 的输出必须包含以下段落(按此顺序):
|
||||
|
||||
```
|
||||
PHASE {N}: {标题}
|
||||
|
||||
PHASE-LOCAL MANIFEST
|
||||
(仅列出本阶段新增或修改的文件路径)
|
||||
|
||||
NEW FILES
|
||||
(完整文件内容,不省略)
|
||||
|
||||
PATCHES
|
||||
(对已有文件的 unified diff)
|
||||
|
||||
TESTS
|
||||
(本阶段应通过的测试)
|
||||
|
||||
COMMANDS
|
||||
(本阶段执行的验证命令)
|
||||
|
||||
ACCEPTANCE
|
||||
(本阶段的验收条件)
|
||||
|
||||
RISKS & ROLLBACK
|
||||
(风险点与回退方案)
|
||||
```
|
||||
|
||||
## 强制规则
|
||||
|
||||
1. **不省略**: 每个 NEW FILES 段必须输出完整文件内容。禁止 `// ... 省略 ...` 或 `/* same as before */`
|
||||
2. **Unified Diff**: 对已有文件的修改使用标准 `--- a/path` / `+++ b/path` 格式
|
||||
3. **Phase-Local Manifest**: 每个 Phase 的 MANIFEST 仅列出本阶段的文件,不列未来 Phase 的文件
|
||||
4. **PATCHES 最小化**: 仅修改与本 Phase 直接相关的文件。禁止顺手重构、重新格式化、批量重写无关文件
|
||||
|
||||
## Phase 19 Final Manifest
|
||||
|
||||
Phase 19 的 gate-manifest.sh 执行 FULL 模式:遍历仓库所有预期文件路径,确认每个文件存在且非空。此时生成 FULL_MANIFEST.txt,包含仓库内所有文件的完整列表。
|
||||
|
||||
## Bundle 执行规则
|
||||
|
||||
允许一次指令执行多个 Phase(Bundle),但每个 Phase 必须有独立的输出块。例如执行 Phase 2+3 时,输出中必须分别有 `## PHASE 2` 和 `## PHASE 3` 两个完整块,各含自己的 MANIFEST、NEW FILES、ACCEPTANCE 等。
|
||||
@@ -0,0 +1,23 @@
|
||||
# LJWX Platform 规格文档索引
|
||||
|
||||
## 文档职责边界
|
||||
|
||||
| 文件 | 内容 | 单一事实源规则 |
|
||||
|------|------|---------------|
|
||||
| `CLAUDE.md` | 硬规则 + 版本锁定 + 代码风格 + 反模式 | **版本号的唯一来源**。其他文件引用不重复 |
|
||||
| `spec/01-constraints.md` | 审计字段、多租户、JWT、RBAC、Quartz、日志、文件、缓存、TS 约束的完整定义 | 约束细节的唯一来源 |
|
||||
| `spec/02-architecture.md` | 仓库结构、模块依赖 DAG、Core 接口定义、POM 依赖声明 | 架构的唯一来源 |
|
||||
| `spec/03-api.md` | 统一响应格式、错误码、完整路由表 | API 合约的唯一来源 |
|
||||
| `spec/04-database.md` | Flyway 迁移文件清单 + DDL 规则 | 数据库 schema 的唯一来源 |
|
||||
| `spec/05-backend-config.md` | application.yml 骨架 | 后端配置的唯一来源 |
|
||||
| `spec/06-frontend-config.md` | pnpm-workspace、root package.json、.npmrc、.nvmrc、.env.example、Admin / Mobile / Screen 各端配置 | 前端配置的唯一来源 |
|
||||
| `spec/07-devops.md` | Docker Compose、CI Gate 脚本、smoke-test、工具链安装策略 | DevOps 的唯一来源 |
|
||||
| `spec/08-output-rules.md` | Phase 输出格式、完整性规则 | 输出规范的唯一来源 |
|
||||
| `spec/phase/phase-{NN}.md` | 每个 Phase 的具体任务、读取清单、验收条件 | Phase 执行计划的唯一来源 |
|
||||
|
||||
## 防漂移规则
|
||||
|
||||
1. **版本号**只在 `CLAUDE.md` 的"版本锁定"段中维护。其他 spec 文件需要引用版本时,写"版本见 CLAUDE.md"
|
||||
2. **同一约束**不在两个文件中重复定义。在唯一来源中定义,其他文件通过"见 spec/XX-xxx.md §章节名"引用
|
||||
3. 如发现任何不一致,以"单一事实源"文件为准
|
||||
4. 变更日志和审阅记录存放在 `docs/adr/` 目录,不进入生成上下文
|
||||
@@ -0,0 +1,82 @@
|
||||
---
|
||||
phase: 0
|
||||
title: "Project Skeleton"
|
||||
targets:
|
||||
backend: true
|
||||
frontend: true
|
||||
depends_on: []
|
||||
bundle_with: [1]
|
||||
scope:
|
||||
- "pom.xml"
|
||||
- ".mvn/**"
|
||||
- "pnpm-workspace.yaml"
|
||||
- "package.json"
|
||||
- ".npmrc"
|
||||
- ".nvmrc"
|
||||
- "docker-compose.yml"
|
||||
- ".env.example"
|
||||
- ".gitignore"
|
||||
- ".editorconfig"
|
||||
- "scripts/gates/**"
|
||||
- "scripts/tools/**"
|
||||
- "scripts/acceptance/**"
|
||||
- "PHASE_MANIFEST.txt"
|
||||
---
|
||||
# Phase 0: Skeleton
|
||||
|
||||
## 读取清单
|
||||
|
||||
- `CLAUDE.md`(自动加载)
|
||||
- `spec/02-architecture.md` — §仓库结构
|
||||
- `spec/06-frontend-config.md` — §pnpm-workspace.yaml、§Root package.json、§.npmrc、§.nvmrc、§.env.example
|
||||
- `spec/07-devops.md` — §Docker Compose、§CI Gate 脚本全部
|
||||
- `spec/08-output-rules.md` — 全文
|
||||
|
||||
## 任务
|
||||
|
||||
创建项目骨架:所有配置文件和 CI 脚本。不包含任何 Java 源码或前端源码。
|
||||
|
||||
## Phase-Local Manifest
|
||||
|
||||
```
|
||||
pom.xml
|
||||
.mvn/wrapper/maven-wrapper.properties
|
||||
pnpm-workspace.yaml
|
||||
package.json
|
||||
.npmrc
|
||||
.nvmrc
|
||||
docker-compose.yml
|
||||
.env.example
|
||||
.gitignore
|
||||
.editorconfig
|
||||
scripts/gates/gate-all.sh
|
||||
scripts/gates/gate-compile.sh
|
||||
scripts/gates/gate-integration.sh
|
||||
scripts/gates/gate-contract.sh
|
||||
scripts/gates/gate-manifest.sh
|
||||
scripts/gates/gate-nfr.sh
|
||||
scripts/tools/export-openapi.sh
|
||||
scripts/acceptance/smoke-test.sh
|
||||
PHASE_MANIFEST.txt
|
||||
```
|
||||
|
||||
## 验收条件
|
||||
|
||||
1. `pom.xml` 中 Java source/target = 21,Spring Boot parent 版本来自 CLAUDE.md
|
||||
2. `package.json` 的 `packageManager` 字段与 CLAUDE.md 一致
|
||||
3. `.nvmrc` 内容与 CLAUDE.md 一致
|
||||
4. `docker-compose.yml` 使用 `postgres:16.12-alpine`
|
||||
5. 所有 `package.json` 中无 `^`
|
||||
6. `.env.example` 使用 `VITE_APP_BASE_API`,无 `VITE_API_BASE_URL`
|
||||
7. `gate-manifest.sh` 包含 7 列审计字段循环检查 + caret 全局扫描 + env 变量三端扫描
|
||||
|
||||
## 验证命令
|
||||
|
||||
```bash
|
||||
grep -RIn '"\^' package.json || echo "OK: No caret in root"
|
||||
grep -RIn 'VITE_API_BASE_URL' .env.example && echo "FAIL" || echo "OK"
|
||||
```
|
||||
|
||||
## 可 Bundle
|
||||
|
||||
可与 Phase 1 一起执行,但必须分开两个 Phase 块输出。
|
||||
@@ -0,0 +1,50 @@
|
||||
---
|
||||
phase: 1
|
||||
title: "Core Module"
|
||||
targets:
|
||||
backend: true
|
||||
frontend: false
|
||||
depends_on: [0]
|
||||
bundle_with: [0]
|
||||
scope:
|
||||
- "ljwx-platform-core/**"
|
||||
---
|
||||
# Phase 1: Core Module
|
||||
|
||||
## 读取清单
|
||||
|
||||
- `CLAUDE.md`(自动加载)
|
||||
- `spec/01-constraints.md` — §审计字段、§多租户行级隔离
|
||||
- `spec/02-architecture.md` — §模块依赖图、§Core 模块关键接口
|
||||
- `spec/03-api.md` — §统一响应、§错误码
|
||||
- `spec/08-output-rules.md`
|
||||
|
||||
## 任务
|
||||
|
||||
实现 ljwx-platform-core 模块:Result、ErrorCode、BaseEntity、SnowflakeIdGenerator、CurrentUserHolder 接口、CurrentTenantHolder 接口。
|
||||
|
||||
## Phase-Local Manifest
|
||||
|
||||
```
|
||||
ljwx-platform-core/pom.xml
|
||||
ljwx-platform-core/src/main/java/com/ljwx/platform/core/result/Result.java
|
||||
ljwx-platform-core/src/main/java/com/ljwx/platform/core/result/ErrorCode.java
|
||||
ljwx-platform-core/src/main/java/com/ljwx/platform/core/result/PageResult.java
|
||||
ljwx-platform-core/src/main/java/com/ljwx/platform/core/id/SnowflakeIdGenerator.java
|
||||
ljwx-platform-core/src/main/java/com/ljwx/platform/core/entity/BaseEntity.java
|
||||
ljwx-platform-core/src/main/java/com/ljwx/platform/core/context/CurrentUserHolder.java
|
||||
ljwx-platform-core/src/main/java/com/ljwx/platform/core/context/CurrentTenantHolder.java
|
||||
```
|
||||
|
||||
## 验收条件
|
||||
|
||||
1. `pom.xml` 无对其他 ljwx 模块的依赖
|
||||
2. Result 类包含 code、message、data、traceId 字段
|
||||
3. ErrorCode 枚举包含 spec/03-api.md 中定义的所有错误码
|
||||
4. BaseEntity 包含 7 个审计字段(匹配 spec/01-constraints.md)
|
||||
5. CurrentUserHolder 和 CurrentTenantHolder 是接口(非实现类)
|
||||
6. `./mvnw compile -pl ljwx-platform-core` 通过
|
||||
|
||||
## 可 Bundle
|
||||
|
||||
可与 Phase 0 一起执行。
|
||||
@@ -0,0 +1,51 @@
|
||||
---
|
||||
phase: 2
|
||||
title: "Data Module"
|
||||
targets:
|
||||
backend: true
|
||||
frontend: false
|
||||
depends_on: [1]
|
||||
bundle_with: [3]
|
||||
scope:
|
||||
- "ljwx-platform-data/**"
|
||||
---
|
||||
# Phase 2: Data Module
|
||||
|
||||
## 读取清单
|
||||
|
||||
- `CLAUDE.md`(自动加载)
|
||||
- `spec/01-constraints.md` — §审计字段、§多租户行级隔离
|
||||
- `spec/02-architecture.md` — §模块依赖图、§Core 模块关键接口、§POM 依赖声明(data 部分)
|
||||
- `spec/08-output-rules.md`
|
||||
|
||||
## 任务
|
||||
|
||||
实现 ljwx-platform-data 模块:AuditFieldInterceptor、TenantLineInterceptor、MyBatis 配置。
|
||||
|
||||
## 关键约束
|
||||
|
||||
- data 模块**仅依赖 core**,import 路径只能出现 `com.ljwx.platform.core.*`
|
||||
- AuditFieldInterceptor 通过 `CurrentUserHolder`(core 接口)获取用户 ID
|
||||
- TenantLineInterceptor 通过 `CurrentTenantHolder`(core 接口)获取租户 ID
|
||||
- 实现类由 security 模块在运行时通过 Spring DI 提供
|
||||
|
||||
## Phase-Local Manifest
|
||||
|
||||
```
|
||||
ljwx-platform-data/pom.xml
|
||||
ljwx-platform-data/src/main/java/com/ljwx/platform/data/interceptor/AuditFieldInterceptor.java
|
||||
ljwx-platform-data/src/main/java/com/ljwx/platform/data/interceptor/TenantLineInterceptor.java
|
||||
ljwx-platform-data/src/main/java/com/ljwx/platform/data/config/MyBatisConfig.java
|
||||
```
|
||||
|
||||
## 验收条件
|
||||
|
||||
1. `pom.xml` 依赖仅含 `ljwx-platform-core`,无 `ljwx-platform-security`
|
||||
2. 全部 import 语句无 `com.ljwx.platform.security`
|
||||
3. AuditFieldInterceptor 处理 INSERT 和 UPDATE
|
||||
4. TenantLineInterceptor 自动追加 `WHERE tenant_id = ?`
|
||||
5. `./mvnw compile -pl ljwx-platform-data` 通过
|
||||
|
||||
## 可 Bundle
|
||||
|
||||
可与 Phase 3 一起执行,但分开输出。
|
||||
@@ -0,0 +1,57 @@
|
||||
---
|
||||
phase: 3
|
||||
title: "Security Module"
|
||||
targets:
|
||||
backend: true
|
||||
frontend: false
|
||||
depends_on: [1]
|
||||
bundle_with: [2]
|
||||
scope:
|
||||
- "ljwx-platform-security/**"
|
||||
---
|
||||
# Phase 3: Security Module
|
||||
|
||||
## 读取清单
|
||||
|
||||
- `CLAUDE.md`(自动加载)
|
||||
- `spec/01-constraints.md` — §JWT 认证
|
||||
- `spec/02-architecture.md` — §模块依赖图、§Core 模块关键接口、§POM 依赖声明(security 部分)
|
||||
- `spec/08-output-rules.md`
|
||||
|
||||
## 任务
|
||||
|
||||
实现 ljwx-platform-security 模块:JWT 工具类、SecurityContextUserHolder / SecurityContextTenantHolder 实现、Security Filter Config。
|
||||
|
||||
## 关键约束
|
||||
|
||||
- security 模块**仅依赖 core**,禁止依赖 data
|
||||
- SecurityContextUserHolder 实现 `CurrentUserHolder` 接口(core 中定义)
|
||||
- SecurityContextTenantHolder 实现 `CurrentTenantHolder` 接口(core 中定义)
|
||||
- JWT authorities claim name = `authorities`,不添加 ROLE_ 前缀
|
||||
- JWT converter:读取 authorities → SimpleGrantedAuthority
|
||||
|
||||
## Phase-Local Manifest
|
||||
|
||||
```
|
||||
ljwx-platform-security/pom.xml
|
||||
ljwx-platform-security/src/main/java/com/ljwx/platform/security/context/SecurityContextUserHolder.java
|
||||
ljwx-platform-security/src/main/java/com/ljwx/platform/security/context/SecurityContextTenantHolder.java
|
||||
ljwx-platform-security/src/main/java/com/ljwx/platform/security/jwt/JwtTokenProvider.java
|
||||
ljwx-platform-security/src/main/java/com/ljwx/platform/security/jwt/JwtProperties.java
|
||||
ljwx-platform-security/src/main/java/com/ljwx/platform/security/filter/JwtAuthenticationFilter.java
|
||||
ljwx-platform-security/src/main/java/com/ljwx/platform/security/config/SecurityConfig.java
|
||||
```
|
||||
|
||||
## 验收条件
|
||||
|
||||
1. `pom.xml` 依赖仅含 `ljwx-platform-core`,无 `ljwx-platform-data`
|
||||
2. 全部 import 无 `com.ljwx.platform.data`
|
||||
3. SecurityContextUserHolder 实现 CurrentUserHolder
|
||||
4. SecurityContextTenantHolder 实现 CurrentTenantHolder
|
||||
5. JwtTokenProvider 使用 HS256,支持 access/refresh 两种 type
|
||||
6. SecurityConfig 中 `/api/auth/login` 和 `/api/auth/refresh` 允许匿名
|
||||
7. `./mvnw compile -pl ljwx-platform-security` 通过
|
||||
|
||||
## 可 Bundle
|
||||
|
||||
可与 Phase 2 一起执行。
|
||||
@@ -0,0 +1,43 @@
|
||||
---
|
||||
phase: 4
|
||||
title: "Web Module"
|
||||
targets:
|
||||
backend: true
|
||||
frontend: false
|
||||
depends_on: [2, 3]
|
||||
bundle_with: [5]
|
||||
scope:
|
||||
- "ljwx-platform-web/**"
|
||||
---
|
||||
# Phase 4: Web Module
|
||||
|
||||
## 读取清单
|
||||
|
||||
- `CLAUDE.md`(自动加载)
|
||||
- `spec/02-architecture.md` — §模块依赖图、§POM 依赖声明(web 部分)
|
||||
- `spec/03-api.md` — §统一响应、§错误码
|
||||
- `spec/08-output-rules.md`
|
||||
|
||||
## 任务
|
||||
|
||||
实现 ljwx-platform-web 模块:GlobalExceptionHandler、ResponseAdvice、WebMvcConfig。
|
||||
|
||||
## Phase-Local Manifest
|
||||
|
||||
```
|
||||
ljwx-platform-web/pom.xml
|
||||
ljwx-platform-web/src/main/java/com/ljwx/platform/web/advice/GlobalExceptionHandler.java
|
||||
ljwx-platform-web/src/main/java/com/ljwx/platform/web/advice/ResponseAdvice.java
|
||||
ljwx-platform-web/src/main/java/com/ljwx/platform/web/config/WebMvcConfig.java
|
||||
```
|
||||
|
||||
## 验收条件
|
||||
|
||||
1. `pom.xml` 依赖含 `ljwx-platform-security`(core 经传递获得)
|
||||
2. GlobalExceptionHandler 处理常见异常并返回 Result 格式
|
||||
3. ErrorCode 中定义的所有错误码均有对应异常处理
|
||||
4. `./mvnw compile -pl ljwx-platform-web` 通过
|
||||
|
||||
## 可 Bundle
|
||||
|
||||
可与 Phase 5 一起执行。
|
||||
@@ -0,0 +1,67 @@
|
||||
---
|
||||
phase: 5
|
||||
title: "App Skeleton"
|
||||
targets:
|
||||
backend: true
|
||||
frontend: false
|
||||
depends_on: [4]
|
||||
bundle_with: [4]
|
||||
scope:
|
||||
- "ljwx-platform-app/pom.xml"
|
||||
- "ljwx-platform-app/src/main/java/com/ljwx/platform/app/LjwxPlatformApplication.java"
|
||||
- "ljwx-platform-app/src/main/resources/application.yml"
|
||||
- "ljwx-platform-app/src/main/resources/db/migration/V001__init_schema.sql"
|
||||
- "ljwx-platform-app/src/main/resources/db/migration/V002__create_user.sql"
|
||||
- "ljwx-platform-app/src/main/resources/db/migration/V003__create_role.sql"
|
||||
- "ljwx-platform-app/src/main/resources/db/migration/V004__create_permission.sql"
|
||||
- "ljwx-platform-app/src/main/resources/db/migration/V005__seed_default_tenant.sql"
|
||||
- "ljwx-platform-app/src/main/resources/db/migration/V006__seed_admin_user.sql"
|
||||
- "ljwx-platform-app/src/main/resources/db/migration/V007__seed_permissions.sql"
|
||||
- "ljwx-platform-app/src/main/resources/db/migration/V008__seed_admin_role.sql"
|
||||
- "ljwx-platform-app/src/main/resources/db/migration/V009__assign_admin_role.sql"
|
||||
---
|
||||
# Phase 5: App Skeleton
|
||||
|
||||
## 读取清单
|
||||
|
||||
- `CLAUDE.md`(自动加载)
|
||||
- `spec/02-architecture.md` — §POM 依赖声明(app 部分)
|
||||
- `spec/04-database.md` — V001 ~ V009
|
||||
- `spec/05-backend-config.md` — §application.yml 骨架
|
||||
- `spec/01-constraints.md` — §审计字段、§RBAC 权限
|
||||
- `spec/08-output-rules.md`
|
||||
|
||||
## 任务
|
||||
|
||||
实现 ljwx-platform-app 骨架:Spring Boot Main、application.yml、Flyway V001-V009(基础表 + 种子数据)。
|
||||
|
||||
## Phase-Local Manifest
|
||||
|
||||
```
|
||||
ljwx-platform-app/pom.xml
|
||||
ljwx-platform-app/src/main/java/com/ljwx/platform/app/LjwxPlatformApplication.java
|
||||
ljwx-platform-app/src/main/resources/application.yml
|
||||
ljwx-platform-app/src/main/resources/db/migration/V001__init_schema.sql
|
||||
ljwx-platform-app/src/main/resources/db/migration/V002__create_user.sql
|
||||
ljwx-platform-app/src/main/resources/db/migration/V003__create_role.sql
|
||||
ljwx-platform-app/src/main/resources/db/migration/V004__create_permission.sql
|
||||
ljwx-platform-app/src/main/resources/db/migration/V005__seed_default_tenant.sql
|
||||
ljwx-platform-app/src/main/resources/db/migration/V006__seed_admin_user.sql
|
||||
ljwx-platform-app/src/main/resources/db/migration/V007__seed_permissions.sql
|
||||
ljwx-platform-app/src/main/resources/db/migration/V008__seed_admin_role.sql
|
||||
ljwx-platform-app/src/main/resources/db/migration/V009__assign_admin_role.sql
|
||||
```
|
||||
|
||||
## 验收条件
|
||||
|
||||
1. `pom.xml` 依赖含 `ljwx-platform-web` + `ljwx-platform-data`
|
||||
2. application.yml 与 spec/05-backend-config.md 一致
|
||||
3. V001-V004 的 CREATE TABLE 均含 7 列审计字段
|
||||
4. V006 中 admin 密码使用 BCrypt cost=10 的 hash
|
||||
5. V007 包含 spec/01-constraints.md §RBAC 中的所有权限字符串
|
||||
6. 无 `IF NOT EXISTS`
|
||||
7. `./mvnw compile -pl ljwx-platform-app` 通过
|
||||
|
||||
## 可 Bundle
|
||||
|
||||
可与 Phase 4 一起执行。
|
||||
@@ -0,0 +1,43 @@
|
||||
---
|
||||
phase: 6
|
||||
title: "AI Context Docs"
|
||||
targets:
|
||||
backend: false
|
||||
frontend: false
|
||||
depends_on: [5]
|
||||
bundle_with: []
|
||||
scope:
|
||||
- "CLAUDE.md"
|
||||
- "spec.md"
|
||||
- "spec/INDEX.md"
|
||||
---
|
||||
# Phase 6: AI Context Docs
|
||||
|
||||
## 读取清单
|
||||
|
||||
- `CLAUDE.md`(自动加载)
|
||||
- `spec/08-output-rules.md`
|
||||
|
||||
## 任务
|
||||
|
||||
确认并微调 CLAUDE.md、spec.md(stub)、spec/INDEX.md。此时这些文件应已存在(在开始使用 Claude Code 前手动创建)。本 Phase 主要确认内容正确、无遗漏。
|
||||
|
||||
如果 AGENTS.md 需要创建(用于非 Claude Code 的 AI 工具),在本 Phase 生成。
|
||||
|
||||
## Phase-Local Manifest
|
||||
|
||||
```
|
||||
CLAUDE.md
|
||||
spec.md
|
||||
spec/INDEX.md
|
||||
```
|
||||
|
||||
## 验收条件
|
||||
|
||||
1. CLAUDE.md 中 Current Phase 已更新到 6
|
||||
2. spec.md 是 stub,指向 CLAUDE.md 和 spec/INDEX.md
|
||||
3. spec/INDEX.md 包含完整的文档索引表和防漂移规则
|
||||
|
||||
## 说明
|
||||
|
||||
本 Phase 产出量小,可与相邻 Phase Bundle。
|
||||
@@ -0,0 +1,61 @@
|
||||
---
|
||||
phase: 7
|
||||
title: "Quartz Integration"
|
||||
targets:
|
||||
backend: true
|
||||
frontend: false
|
||||
depends_on: [5]
|
||||
bundle_with: [8]
|
||||
scope:
|
||||
- "ljwx-platform-app/src/main/resources/db/migration/V010__create_quartz_tables.sql"
|
||||
- "ljwx-platform-app/src/main/resources/db/migration/V011__create_sys_job.sql"
|
||||
- "ljwx-platform-app/src/main/java/com/ljwx/platform/app/controller/JobController.java"
|
||||
- "ljwx-platform-app/src/main/java/com/ljwx/platform/app/appservice/JobAppService.java"
|
||||
- "ljwx-platform-app/src/main/java/com/ljwx/platform/app/domain/entity/SysJob.java"
|
||||
- "ljwx-platform-app/src/main/java/com/ljwx/platform/app/infra/mapper/SysJobMapper.java"
|
||||
- "ljwx-platform-app/src/main/resources/mapper/SysJobMapper.xml"
|
||||
---
|
||||
# Phase 7: Quartz Integration
|
||||
|
||||
## 读取清单
|
||||
|
||||
- `CLAUDE.md`(自动加载)
|
||||
- `spec/01-constraints.md` — §Quartz 调度
|
||||
- `spec/03-api.md` — §Jobs 路由
|
||||
- `spec/04-database.md` — V010、V011
|
||||
- `spec/08-output-rules.md`
|
||||
|
||||
## 任务
|
||||
|
||||
Quartz 集成:V010(Quartz 标准表)、V011(sys_job 业务表)、Job Controller / Service / Mapper、per-tenant 调度。
|
||||
|
||||
## 关键约束
|
||||
|
||||
- V010:Quartz 标准 PostgreSQL DDL,QRTZ_ 前缀,**无审计字段**,**无 IF NOT EXISTS**
|
||||
- V011:sys_job 业务表,**含 7 列审计字段**
|
||||
- JobKey 格式:`(name="{jobId}", group="TENANT_{tenantId}")`
|
||||
- Controller 每个方法必须 `@PreAuthorize`
|
||||
|
||||
## Phase-Local Manifest
|
||||
|
||||
```
|
||||
ljwx-platform-app/src/main/resources/db/migration/V010__create_quartz_tables.sql
|
||||
ljwx-platform-app/src/main/resources/db/migration/V011__create_sys_job.sql
|
||||
ljwx-platform-app/src/main/java/com/ljwx/platform/app/controller/JobController.java
|
||||
ljwx-platform-app/src/main/java/com/ljwx/platform/app/appservice/JobAppService.java
|
||||
ljwx-platform-app/src/main/java/com/ljwx/platform/app/domain/entity/SysJob.java
|
||||
ljwx-platform-app/src/main/java/com/ljwx/platform/app/infra/mapper/SysJobMapper.java
|
||||
ljwx-platform-app/src/main/resources/mapper/SysJobMapper.xml
|
||||
```
|
||||
|
||||
## 验收条件
|
||||
|
||||
1. V010 含 QRTZ_ 表,无审计字段,无 IF NOT EXISTS
|
||||
2. V011 含 7 列审计字段
|
||||
3. JobController 所有方法有 @PreAuthorize,权限字符串匹配 spec/03-api.md
|
||||
4. per-tenant 隔离:JobKey group 含 `TENANT_`
|
||||
5. `./mvnw compile -pl ljwx-platform-app` 通过
|
||||
|
||||
## 可 Bundle
|
||||
|
||||
可与 Phase 8 一起执行。
|
||||
@@ -0,0 +1,81 @@
|
||||
---
|
||||
phase: 8
|
||||
title: "Dict and Config"
|
||||
targets:
|
||||
backend: true
|
||||
frontend: false
|
||||
depends_on: [7]
|
||||
bundle_with: [7]
|
||||
scope:
|
||||
- "ljwx-platform-app/src/main/resources/db/migration/V012__create_sys_dict_type.sql"
|
||||
- "ljwx-platform-app/src/main/resources/db/migration/V013__create_sys_dict_data.sql"
|
||||
- "ljwx-platform-app/src/main/resources/db/migration/V014__create_sys_config.sql"
|
||||
- "ljwx-platform-app/src/main/resources/db/migration/V019__seed_dict_data.sql"
|
||||
- "ljwx-platform-app/src/main/resources/db/migration/V020__seed_config_data.sql"
|
||||
- "ljwx-platform-app/src/main/java/com/ljwx/platform/app/controller/DictController.java"
|
||||
- "ljwx-platform-app/src/main/java/com/ljwx/platform/app/controller/ConfigController.java"
|
||||
- "ljwx-platform-app/src/main/java/com/ljwx/platform/app/appservice/DictAppService.java"
|
||||
- "ljwx-platform-app/src/main/java/com/ljwx/platform/app/appservice/ConfigAppService.java"
|
||||
- "ljwx-platform-app/src/main/java/com/ljwx/platform/app/domain/entity/SysDictType.java"
|
||||
- "ljwx-platform-app/src/main/java/com/ljwx/platform/app/domain/entity/SysDictData.java"
|
||||
- "ljwx-platform-app/src/main/java/com/ljwx/platform/app/domain/entity/SysConfig.java"
|
||||
- "ljwx-platform-app/src/main/java/com/ljwx/platform/app/infra/mapper/SysDictTypeMapper.java"
|
||||
- "ljwx-platform-app/src/main/java/com/ljwx/platform/app/infra/mapper/SysDictDataMapper.java"
|
||||
- "ljwx-platform-app/src/main/java/com/ljwx/platform/app/infra/mapper/SysConfigMapper.java"
|
||||
- "ljwx-platform-app/src/main/resources/mapper/SysDictTypeMapper.xml"
|
||||
- "ljwx-platform-app/src/main/resources/mapper/SysDictDataMapper.xml"
|
||||
- "ljwx-platform-app/src/main/resources/mapper/SysConfigMapper.xml"
|
||||
---
|
||||
# Phase 8: Dict & Config
|
||||
|
||||
## 读取清单
|
||||
|
||||
- `CLAUDE.md`(自动加载)
|
||||
- `spec/01-constraints.md` — §缓存策略、§审计字段
|
||||
- `spec/03-api.md` — §Dicts 路由、§Configs 路由
|
||||
- `spec/04-database.md` — V012 ~ V014、V019 ~ V020
|
||||
- `spec/08-output-rules.md`
|
||||
|
||||
## 任务
|
||||
|
||||
字典和系统配置 CRUD + Caffeine 缓存:V012-V014(表结构)、V019-V020(种子数据)、Controller / Service / Mapper。
|
||||
|
||||
## 关键约束
|
||||
|
||||
- Caffeine 缓存,TTL = 10 min,由 Spring Boot 管理
|
||||
- 不使用 Redis / MQ / JPA
|
||||
- 所有 CREATE TABLE 含 7 列审计字段
|
||||
|
||||
## Phase-Local Manifest
|
||||
|
||||
```
|
||||
ljwx-platform-app/src/main/resources/db/migration/V012__create_sys_dict_type.sql
|
||||
ljwx-platform-app/src/main/resources/db/migration/V013__create_sys_dict_data.sql
|
||||
ljwx-platform-app/src/main/resources/db/migration/V014__create_sys_config.sql
|
||||
ljwx-platform-app/src/main/resources/db/migration/V019__seed_dict_data.sql
|
||||
ljwx-platform-app/src/main/resources/db/migration/V020__seed_config_data.sql
|
||||
ljwx-platform-app/src/main/java/com/ljwx/platform/app/controller/DictController.java
|
||||
ljwx-platform-app/src/main/java/com/ljwx/platform/app/controller/ConfigController.java
|
||||
ljwx-platform-app/src/main/java/com/ljwx/platform/app/appservice/DictAppService.java
|
||||
ljwx-platform-app/src/main/java/com/ljwx/platform/app/appservice/ConfigAppService.java
|
||||
ljwx-platform-app/src/main/java/com/ljwx/platform/app/domain/entity/SysDictType.java
|
||||
ljwx-platform-app/src/main/java/com/ljwx/platform/app/domain/entity/SysDictData.java
|
||||
ljwx-platform-app/src/main/java/com/ljwx/platform/app/domain/entity/SysConfig.java
|
||||
ljwx-platform-app/src/main/java/com/ljwx/platform/app/infra/mapper/SysDictTypeMapper.java
|
||||
ljwx-platform-app/src/main/java/com/ljwx/platform/app/infra/mapper/SysDictDataMapper.java
|
||||
ljwx-platform-app/src/main/java/com/ljwx/platform/app/infra/mapper/SysConfigMapper.java
|
||||
ljwx-platform-app/src/main/resources/mapper/SysDictTypeMapper.xml
|
||||
ljwx-platform-app/src/main/resources/mapper/SysDictDataMapper.xml
|
||||
ljwx-platform-app/src/main/resources/mapper/SysConfigMapper.xml
|
||||
```
|
||||
|
||||
## 验收条件
|
||||
|
||||
1. V012-V014 含 7 列审计字段,无 IF NOT EXISTS
|
||||
2. DictAppService / ConfigAppService 使用 `@Cacheable` / `@CacheEvict`
|
||||
3. Controller 方法均有 @PreAuthorize
|
||||
4. 编译通过
|
||||
|
||||
## 可 Bundle
|
||||
|
||||
可与 Phase 7 一起执行。
|
||||
@@ -0,0 +1,94 @@
|
||||
---
|
||||
phase: 9
|
||||
title: "Logs Notice and File"
|
||||
targets:
|
||||
backend: true
|
||||
frontend: false
|
||||
depends_on: [8]
|
||||
bundle_with: [10]
|
||||
scope:
|
||||
- "ljwx-platform-app/src/main/resources/db/migration/V015__create_sys_operation_log.sql"
|
||||
- "ljwx-platform-app/src/main/resources/db/migration/V016__create_sys_login_log.sql"
|
||||
- "ljwx-platform-app/src/main/resources/db/migration/V017__create_sys_file.sql"
|
||||
- "ljwx-platform-app/src/main/resources/db/migration/V018__create_sys_notice.sql"
|
||||
- "ljwx-platform-app/src/main/java/com/ljwx/platform/app/controller/OperationLogController.java"
|
||||
- "ljwx-platform-app/src/main/java/com/ljwx/platform/app/controller/LoginLogController.java"
|
||||
- "ljwx-platform-app/src/main/java/com/ljwx/platform/app/controller/FileController.java"
|
||||
- "ljwx-platform-app/src/main/java/com/ljwx/platform/app/controller/NoticeController.java"
|
||||
- "ljwx-platform-app/src/main/java/com/ljwx/platform/app/appservice/OperationLogAppService.java"
|
||||
- "ljwx-platform-app/src/main/java/com/ljwx/platform/app/appservice/FileAppService.java"
|
||||
- "ljwx-platform-app/src/main/java/com/ljwx/platform/app/appservice/NoticeAppService.java"
|
||||
- "ljwx-platform-app/src/main/java/com/ljwx/platform/app/domain/entity/SysOperationLog.java"
|
||||
- "ljwx-platform-app/src/main/java/com/ljwx/platform/app/domain/entity/SysLoginLog.java"
|
||||
- "ljwx-platform-app/src/main/java/com/ljwx/platform/app/domain/entity/SysFile.java"
|
||||
- "ljwx-platform-app/src/main/java/com/ljwx/platform/app/domain/entity/SysNotice.java"
|
||||
- "ljwx-platform-app/src/main/java/com/ljwx/platform/app/infra/mapper/SysOperationLogMapper.java"
|
||||
- "ljwx-platform-app/src/main/java/com/ljwx/platform/app/infra/mapper/SysLoginLogMapper.java"
|
||||
- "ljwx-platform-app/src/main/java/com/ljwx/platform/app/infra/mapper/SysFileMapper.java"
|
||||
- "ljwx-platform-app/src/main/java/com/ljwx/platform/app/infra/mapper/SysNoticeMapper.java"
|
||||
- "ljwx-platform-app/src/main/resources/mapper/SysOperationLogMapper.xml"
|
||||
- "ljwx-platform-app/src/main/resources/mapper/SysLoginLogMapper.xml"
|
||||
- "ljwx-platform-app/src/main/resources/mapper/SysFileMapper.xml"
|
||||
- "ljwx-platform-app/src/main/resources/mapper/SysNoticeMapper.xml"
|
||||
---
|
||||
# Phase 9: Logs, Notice & File
|
||||
|
||||
## 读取清单
|
||||
|
||||
- `CLAUDE.md`(自动加载)
|
||||
- `spec/01-constraints.md` — §操作日志、§文件管理、§审计字段
|
||||
- `spec/03-api.md` — §Logs、§Files、§Notices 路由
|
||||
- `spec/04-database.md` — V015 ~ V018
|
||||
- `spec/08-output-rules.md`
|
||||
|
||||
## 任务
|
||||
|
||||
操作日志(异步 + 脱敏)、登录日志、通知、文件管理:V015-V018 + 全部 Controller / Service / Mapper。
|
||||
|
||||
## 关键约束
|
||||
|
||||
- 操作日志异步线程池:core=2, max=4, queue=1024
|
||||
- 日志体 > 4096 字节截断
|
||||
- 脱敏:password → `***`,phone → 中间四位 `*`,idCard → 中间段 `*`
|
||||
- 文件:Snowflake ID 命名,50MB 限制,白名单后缀,路径含 tenant_id
|
||||
|
||||
## Phase-Local Manifest
|
||||
|
||||
```
|
||||
ljwx-platform-app/src/main/resources/db/migration/V015__create_sys_operation_log.sql
|
||||
ljwx-platform-app/src/main/resources/db/migration/V016__create_sys_login_log.sql
|
||||
ljwx-platform-app/src/main/resources/db/migration/V017__create_sys_file.sql
|
||||
ljwx-platform-app/src/main/resources/db/migration/V018__create_sys_notice.sql
|
||||
ljwx-platform-app/src/main/java/com/ljwx/platform/app/controller/OperationLogController.java
|
||||
ljwx-platform-app/src/main/java/com/ljwx/platform/app/controller/LoginLogController.java
|
||||
ljwx-platform-app/src/main/java/com/ljwx/platform/app/controller/FileController.java
|
||||
ljwx-platform-app/src/main/java/com/ljwx/platform/app/controller/NoticeController.java
|
||||
ljwx-platform-app/src/main/java/com/ljwx/platform/app/appservice/OperationLogAppService.java
|
||||
ljwx-platform-app/src/main/java/com/ljwx/platform/app/appservice/FileAppService.java
|
||||
ljwx-platform-app/src/main/java/com/ljwx/platform/app/appservice/NoticeAppService.java
|
||||
ljwx-platform-app/src/main/java/com/ljwx/platform/app/domain/entity/SysOperationLog.java
|
||||
ljwx-platform-app/src/main/java/com/ljwx/platform/app/domain/entity/SysLoginLog.java
|
||||
ljwx-platform-app/src/main/java/com/ljwx/platform/app/domain/entity/SysFile.java
|
||||
ljwx-platform-app/src/main/java/com/ljwx/platform/app/domain/entity/SysNotice.java
|
||||
ljwx-platform-app/src/main/java/com/ljwx/platform/app/infra/mapper/SysOperationLogMapper.java
|
||||
ljwx-platform-app/src/main/java/com/ljwx/platform/app/infra/mapper/SysLoginLogMapper.java
|
||||
ljwx-platform-app/src/main/java/com/ljwx/platform/app/infra/mapper/SysFileMapper.java
|
||||
ljwx-platform-app/src/main/java/com/ljwx/platform/app/infra/mapper/SysNoticeMapper.java
|
||||
ljwx-platform-app/src/main/resources/mapper/SysOperationLogMapper.xml
|
||||
ljwx-platform-app/src/main/resources/mapper/SysLoginLogMapper.xml
|
||||
ljwx-platform-app/src/main/resources/mapper/SysFileMapper.xml
|
||||
ljwx-platform-app/src/main/resources/mapper/SysNoticeMapper.xml
|
||||
```
|
||||
|
||||
## 验收条件
|
||||
|
||||
1. V015-V018 含 7 列审计字段(日志表也需要 tenant_id 和 created_time 等)
|
||||
2. 操作日志使用异步线程池
|
||||
3. 脱敏逻辑已实现
|
||||
4. 文件上传有后缀白名单校验和 50MB 限制
|
||||
5. Controller 方法均有 @PreAuthorize
|
||||
6. 编译通过
|
||||
|
||||
## 可 Bundle
|
||||
|
||||
可与 Phase 10 一起执行。
|
||||
@@ -0,0 +1,44 @@
|
||||
---
|
||||
phase: 10
|
||||
title: "Index and Contract"
|
||||
targets:
|
||||
backend: true
|
||||
frontend: false
|
||||
depends_on: [9]
|
||||
bundle_with: [9]
|
||||
scope:
|
||||
- "ljwx-platform-app/src/main/resources/db/migration/V021__create_indexes.sql"
|
||||
- "scripts/tools/export-openapi.sh"
|
||||
- "docs/contracts/.gitkeep"
|
||||
---
|
||||
# Phase 10: Index & Contract
|
||||
|
||||
## 读取清单
|
||||
|
||||
- `CLAUDE.md`(自动加载)
|
||||
- `spec/04-database.md` — V021
|
||||
- `spec/05-backend-config.md` — §springdoc 部分
|
||||
- `spec/08-output-rules.md`
|
||||
|
||||
## 任务
|
||||
|
||||
V021(常用索引)、OpenAPI export 脚本、springdoc 配置验证。
|
||||
|
||||
## Phase-Local Manifest
|
||||
|
||||
```
|
||||
ljwx-platform-app/src/main/resources/db/migration/V021__create_indexes.sql
|
||||
scripts/tools/export-openapi.sh
|
||||
docs/contracts/.gitkeep
|
||||
```
|
||||
|
||||
## 验收条件
|
||||
|
||||
1. V021 为常用字段创建索引(tenant_id、username、created_time 等)
|
||||
2. export-openapi.sh 可启动应用并导出 openapi.json
|
||||
3. springdoc 在 application.yml 中配置正确
|
||||
4. 编译通过
|
||||
|
||||
## 可 Bundle
|
||||
|
||||
可与 Phase 9 一起执行。
|
||||
@@ -0,0 +1,50 @@
|
||||
---
|
||||
phase: 11
|
||||
title: "Shared Package"
|
||||
targets:
|
||||
backend: false
|
||||
frontend: true
|
||||
depends_on: [10]
|
||||
bundle_with: []
|
||||
scope:
|
||||
- "packages/shared/**"
|
||||
---
|
||||
# Phase 11: Shared Package
|
||||
|
||||
## 读取清单
|
||||
|
||||
- `CLAUDE.md`(自动加载)
|
||||
- `spec/03-api.md` — §统一响应、§错误码(用于类型定义)
|
||||
- `spec/06-frontend-config.md` — §Root package.json(检查 workspace 配置)
|
||||
- `spec/01-constraints.md` — §TypeScript 约束、§RBAC 权限(用于常量)
|
||||
- `spec/08-output-rules.md`
|
||||
|
||||
## 任务
|
||||
|
||||
前端共享 TS 包 packages/shared:types(API 类型定义)、constants(错误码、权限字符串)、utils、tsup 构建。
|
||||
|
||||
## Phase-Local Manifest
|
||||
|
||||
```
|
||||
packages/shared/package.json
|
||||
packages/shared/tsconfig.json
|
||||
packages/shared/tsup.config.ts
|
||||
packages/shared/src/index.ts
|
||||
packages/shared/src/types/index.ts
|
||||
packages/shared/src/types/api.ts
|
||||
packages/shared/src/types/user.ts
|
||||
packages/shared/src/types/role.ts
|
||||
packages/shared/src/types/common.ts
|
||||
packages/shared/src/constants/index.ts
|
||||
packages/shared/src/constants/error-codes.ts
|
||||
packages/shared/src/constants/permissions.ts
|
||||
packages/shared/src/utils/index.ts
|
||||
```
|
||||
|
||||
## 验收条件
|
||||
|
||||
1. 无 `any` 类型
|
||||
2. tsconfig `strict: true`
|
||||
3. 错误码与 spec/03-api.md 一致
|
||||
4. 权限常量与 spec/01-constraints.md §RBAC 一致
|
||||
5. `pnpm run build:shared` 通过
|
||||
@@ -0,0 +1,67 @@
|
||||
---
|
||||
phase: 12
|
||||
title: "Admin Scaffold"
|
||||
targets:
|
||||
backend: false
|
||||
frontend: true
|
||||
depends_on: [11]
|
||||
bundle_with: []
|
||||
scope:
|
||||
- "ljwx-platform-admin/**"
|
||||
---
|
||||
# Phase 12: Admin Scaffold
|
||||
|
||||
## 读取清单
|
||||
|
||||
- `CLAUDE.md`(自动加载)
|
||||
- `spec/06-frontend-config.md` — §Admin package.json、§Admin vite.config.ts、§Admin Axios 封装
|
||||
- `spec/01-constraints.md` — §JWT 认证(Refresh 流程)、§Vue Router 5 兼容性约束、§TypeScript 约束
|
||||
- `spec/08-output-rules.md`
|
||||
|
||||
## 任务
|
||||
|
||||
Vue 3 + Vite + Element Plus 骨架:router(vue-router @5 API)、stores(user store 含 token 管理)、layouts(侧边栏 + 顶栏)、login 页面。
|
||||
|
||||
## 关键约束
|
||||
|
||||
- **必须使用 vue-router @5 API**,禁止按 v4 写
|
||||
- 无 `any`,`strict: true`
|
||||
- Axios baseURL 使用 `import.meta.env.VITE_APP_BASE_API`
|
||||
- 401 刷新逻辑参考 spec/06-frontend-config.md §Axios 封装
|
||||
|
||||
## Phase-Local Manifest
|
||||
|
||||
```
|
||||
ljwx-platform-admin/package.json
|
||||
ljwx-platform-admin/vite.config.ts
|
||||
ljwx-platform-admin/tsconfig.json
|
||||
ljwx-platform-admin/tsconfig.app.json
|
||||
ljwx-platform-admin/tsconfig.node.json
|
||||
ljwx-platform-admin/env.d.ts
|
||||
ljwx-platform-admin/.env.development
|
||||
ljwx-platform-admin/.env.production
|
||||
ljwx-platform-admin/index.html
|
||||
ljwx-platform-admin/src/main.ts
|
||||
ljwx-platform-admin/src/App.vue
|
||||
ljwx-platform-admin/src/router/index.ts
|
||||
ljwx-platform-admin/src/stores/user.ts
|
||||
ljwx-platform-admin/src/stores/app.ts
|
||||
ljwx-platform-admin/src/api/request.ts
|
||||
ljwx-platform-admin/src/api/auth.ts
|
||||
ljwx-platform-admin/src/layouts/DefaultLayout.vue
|
||||
ljwx-platform-admin/src/layouts/components/Sidebar.vue
|
||||
ljwx-platform-admin/src/layouts/components/Navbar.vue
|
||||
ljwx-platform-admin/src/views/login/index.vue
|
||||
ljwx-platform-admin/src/views/dashboard/index.vue
|
||||
ljwx-platform-admin/src/styles/index.scss
|
||||
ljwx-platform-admin/src/styles/variables.scss
|
||||
```
|
||||
|
||||
## 验收条件
|
||||
|
||||
1. package.json 所有依赖版本用 `~`,无 `^`
|
||||
2. .env 文件使用 `VITE_APP_BASE_API`
|
||||
3. router 使用 vue-router @5 API
|
||||
4. user store 包含 accessToken / refreshToken / login / logout / refreshToken 方法
|
||||
5. Axios 封装含 401 刷新队列逻辑
|
||||
6. `pnpm run build:admin` 通过(先执行 build:shared)
|
||||
@@ -0,0 +1,55 @@
|
||||
---
|
||||
phase: 13
|
||||
title: "Admin CRUD Pages"
|
||||
targets:
|
||||
backend: false
|
||||
frontend: true
|
||||
depends_on: [12]
|
||||
bundle_with: []
|
||||
scope:
|
||||
- "ljwx-platform-admin/src/api/**"
|
||||
- "ljwx-platform-admin/src/views/**"
|
||||
---
|
||||
# Phase 13: Admin CRUD Pages
|
||||
|
||||
## 读取清单
|
||||
|
||||
- `CLAUDE.md`(自动加载)
|
||||
- `spec/03-api.md` — 全部路由表(Users / Roles / Tenants / Dicts / Configs / Logs / Files / Notices)
|
||||
- `spec/01-constraints.md` — §TypeScript 约束
|
||||
- `spec/08-output-rules.md`
|
||||
|
||||
## 任务
|
||||
|
||||
Admin 管理后台所有 CRUD 页面:用户 / 角色 / 租户 / 字典 / 配置 / 日志 / 文件 / 通知管理。
|
||||
|
||||
## Phase-Local Manifest
|
||||
|
||||
```
|
||||
ljwx-platform-admin/src/api/user.ts
|
||||
ljwx-platform-admin/src/api/role.ts
|
||||
ljwx-platform-admin/src/api/tenant.ts
|
||||
ljwx-platform-admin/src/api/dict.ts
|
||||
ljwx-platform-admin/src/api/config.ts
|
||||
ljwx-platform-admin/src/api/log.ts
|
||||
ljwx-platform-admin/src/api/file.ts
|
||||
ljwx-platform-admin/src/api/notice.ts
|
||||
ljwx-platform-admin/src/api/job.ts
|
||||
ljwx-platform-admin/src/views/system/user/index.vue
|
||||
ljwx-platform-admin/src/views/system/role/index.vue
|
||||
ljwx-platform-admin/src/views/system/tenant/index.vue
|
||||
ljwx-platform-admin/src/views/system/dict/index.vue
|
||||
ljwx-platform-admin/src/views/system/config/index.vue
|
||||
ljwx-platform-admin/src/views/system/job/index.vue
|
||||
ljwx-platform-admin/src/views/monitor/operlog/index.vue
|
||||
ljwx-platform-admin/src/views/monitor/loginlog/index.vue
|
||||
ljwx-platform-admin/src/views/system/file/index.vue
|
||||
ljwx-platform-admin/src/views/system/notice/index.vue
|
||||
```
|
||||
|
||||
## 验收条件
|
||||
|
||||
1. 每个 API 文件有完整的类型定义,无 `any`
|
||||
2. 每个页面支持列表查询、新增、编辑、删除(按路由表对应的操作)
|
||||
3. 所有请求路径与 spec/03-api.md 一致
|
||||
4. type-check 通过
|
||||
@@ -0,0 +1,48 @@
|
||||
---
|
||||
phase: 14
|
||||
title: "Admin Permission and Polish"
|
||||
targets:
|
||||
backend: false
|
||||
frontend: true
|
||||
depends_on: [13]
|
||||
bundle_with: []
|
||||
scope:
|
||||
- "ljwx-platform-admin/src/router/guards.ts"
|
||||
- "ljwx-platform-admin/src/stores/permission.ts"
|
||||
- "ljwx-platform-admin/src/composables/usePermission.ts"
|
||||
- "ljwx-platform-admin/src/composables/useTheme.ts"
|
||||
- "ljwx-platform-admin/src/layouts/components/Breadcrumb.vue"
|
||||
- "ljwx-platform-admin/src/layouts/components/TagsView.vue"
|
||||
- "ljwx-platform-admin/src/layouts/components/ThemeSwitch.vue"
|
||||
---
|
||||
# Phase 14: Admin Permission & Polish
|
||||
|
||||
## 读取清单
|
||||
|
||||
- `CLAUDE.md`(自动加载)
|
||||
- `spec/01-constraints.md` — §RBAC 权限
|
||||
- `spec/08-output-rules.md`
|
||||
|
||||
## 任务
|
||||
|
||||
动态路由权限过滤、面包屑、标签页、主题切换。
|
||||
|
||||
## Phase-Local Manifest
|
||||
|
||||
```
|
||||
ljwx-platform-admin/src/router/guards.ts
|
||||
ljwx-platform-admin/src/stores/permission.ts
|
||||
ljwx-platform-admin/src/composables/usePermission.ts
|
||||
ljwx-platform-admin/src/layouts/components/Breadcrumb.vue
|
||||
ljwx-platform-admin/src/layouts/components/TagsView.vue
|
||||
ljwx-platform-admin/src/layouts/components/ThemeSwitch.vue
|
||||
ljwx-platform-admin/src/composables/useTheme.ts
|
||||
```
|
||||
|
||||
## 验收条件
|
||||
|
||||
1. 路由守卫根据用户权限动态过滤菜单
|
||||
2. 无权限的路由不可访问
|
||||
3. 面包屑、标签页正常显示
|
||||
4. 主题切换(亮色/暗色)功能正常
|
||||
5. build 通过
|
||||
@@ -0,0 +1,53 @@
|
||||
---
|
||||
phase: 15
|
||||
title: "Mobile Scaffold"
|
||||
targets:
|
||||
backend: false
|
||||
frontend: true
|
||||
depends_on: [11]
|
||||
bundle_with: [16]
|
||||
scope:
|
||||
- "ljwx-platform-mobile/**"
|
||||
---
|
||||
# Phase 15: Mobile Scaffold
|
||||
|
||||
## 读取清单
|
||||
|
||||
- `CLAUDE.md`(自动加载)
|
||||
- `spec/06-frontend-config.md` — §Mobile package.json、§Mobile pages.json
|
||||
- `spec/01-constraints.md` — §TypeScript 约束、§JWT 认证
|
||||
- `spec/08-output-rules.md`
|
||||
|
||||
## 任务
|
||||
|
||||
uni-app 骨架:登录、首页、tabBar 配置。
|
||||
|
||||
## Phase-Local Manifest
|
||||
|
||||
```
|
||||
ljwx-platform-mobile/package.json
|
||||
ljwx-platform-mobile/manifest.json
|
||||
ljwx-platform-mobile/pages.json
|
||||
ljwx-platform-mobile/tsconfig.json
|
||||
ljwx-platform-mobile/.env.development
|
||||
ljwx-platform-mobile/.env.production
|
||||
ljwx-platform-mobile/src/main.ts
|
||||
ljwx-platform-mobile/src/App.vue
|
||||
ljwx-platform-mobile/src/pages/login/index.vue
|
||||
ljwx-platform-mobile/src/pages/home/index.vue
|
||||
ljwx-platform-mobile/src/stores/user.ts
|
||||
ljwx-platform-mobile/src/api/request.ts
|
||||
ljwx-platform-mobile/src/api/auth.ts
|
||||
```
|
||||
|
||||
## 验收条件
|
||||
|
||||
1. package.json 依赖全部用 `~`,无 `^`
|
||||
2. .env 使用 `VITE_APP_BASE_API`
|
||||
3. 登录页面可正常显示
|
||||
4. tabBar 包含 4 个页面
|
||||
5. 无 `any`
|
||||
|
||||
## 可 Bundle
|
||||
|
||||
可与 Phase 16 一起执行。
|
||||
@@ -0,0 +1,44 @@
|
||||
---
|
||||
phase: 16
|
||||
title: "Mobile Feature Pages"
|
||||
targets:
|
||||
backend: false
|
||||
frontend: true
|
||||
depends_on: [15]
|
||||
bundle_with: [15]
|
||||
scope:
|
||||
- "ljwx-platform-mobile/src/pages/**"
|
||||
- "ljwx-platform-mobile/src/api/notice.ts"
|
||||
- "ljwx-platform-mobile/src/api/user.ts"
|
||||
---
|
||||
# Phase 16: Mobile Feature Pages
|
||||
|
||||
## 读取清单
|
||||
|
||||
- `CLAUDE.md`(自动加载)
|
||||
- `spec/06-frontend-config.md` — §Mobile pages.json
|
||||
- `spec/08-output-rules.md`
|
||||
|
||||
## 任务
|
||||
|
||||
移动端工作台、消息、个人中心页面。
|
||||
|
||||
## Phase-Local Manifest
|
||||
|
||||
```
|
||||
ljwx-platform-mobile/src/pages/work/index.vue
|
||||
ljwx-platform-mobile/src/pages/message/index.vue
|
||||
ljwx-platform-mobile/src/pages/mine/index.vue
|
||||
ljwx-platform-mobile/src/api/notice.ts
|
||||
ljwx-platform-mobile/src/api/user.ts
|
||||
```
|
||||
|
||||
## 验收条件
|
||||
|
||||
1. 四个 tabBar 页面均有内容
|
||||
2. 无 `any`
|
||||
3. build:h5 通过(如 uni-app CLI 可用)
|
||||
|
||||
## 可 Bundle
|
||||
|
||||
可与 Phase 15 一起执行。
|
||||
@@ -0,0 +1,58 @@
|
||||
---
|
||||
phase: 17
|
||||
title: "Screen Scaffold"
|
||||
targets:
|
||||
backend: false
|
||||
frontend: true
|
||||
depends_on: [11]
|
||||
bundle_with: [18]
|
||||
scope:
|
||||
- "ljwx-platform-screen/**"
|
||||
---
|
||||
# Phase 17: Screen Scaffold
|
||||
|
||||
## 读取清单
|
||||
|
||||
- `CLAUDE.md`(自动加载)
|
||||
- `spec/06-frontend-config.md` — §Screen package.json、§Screen ECharts 暗色主题、§Screen 自适应缩放
|
||||
- `spec/01-constraints.md` — §TypeScript 约束
|
||||
- `spec/08-output-rules.md`
|
||||
|
||||
## 任务
|
||||
|
||||
Vue 3 + ECharts + DataV 骨架:暗色主题注册、自适应缩放 composable、基础布局。
|
||||
|
||||
## Phase-Local Manifest
|
||||
|
||||
```
|
||||
ljwx-platform-screen/package.json
|
||||
ljwx-platform-screen/vite.config.ts
|
||||
ljwx-platform-screen/tsconfig.json
|
||||
ljwx-platform-screen/.env.development
|
||||
ljwx-platform-screen/.env.production
|
||||
ljwx-platform-screen/index.html
|
||||
ljwx-platform-screen/src/main.ts
|
||||
ljwx-platform-screen/src/App.vue
|
||||
ljwx-platform-screen/src/router/index.ts
|
||||
ljwx-platform-screen/src/composables/useScreenAdapt.ts
|
||||
ljwx-platform-screen/src/utils/echarts-setup.ts
|
||||
ljwx-platform-screen/src/utils/echarts-dark-theme.ts
|
||||
ljwx-platform-screen/src/layouts/ScreenLayout.vue
|
||||
ljwx-platform-screen/src/views/home/index.vue
|
||||
ljwx-platform-screen/src/api/request.ts
|
||||
ljwx-platform-screen/src/api/screen.ts
|
||||
ljwx-platform-screen/src/styles/index.scss
|
||||
```
|
||||
|
||||
## 验收条件
|
||||
|
||||
1. package.json 依赖全部用 `~`,无 `^`
|
||||
2. .env 使用 `VITE_APP_BASE_API`
|
||||
3. 暗色主题已注册
|
||||
4. 自适应缩放 composable 正确(设计宽度 1920×1080)
|
||||
5. 无 `any`
|
||||
6. build 通过
|
||||
|
||||
## 可 Bundle
|
||||
|
||||
可与 Phase 18 一起执行。
|
||||
@@ -0,0 +1,56 @@
|
||||
---
|
||||
phase: 18
|
||||
title: "Screen Components"
|
||||
targets:
|
||||
backend: false
|
||||
frontend: true
|
||||
depends_on: [17]
|
||||
bundle_with: [17]
|
||||
scope:
|
||||
- "ljwx-platform-screen/src/components/**"
|
||||
- "ljwx-platform-screen/src/views/home/index.vue"
|
||||
---
|
||||
# Phase 18: Screen Components
|
||||
|
||||
## 读取清单
|
||||
|
||||
- `CLAUDE.md`(自动加载)
|
||||
- `spec/03-api.md` — §Screen 路由
|
||||
- `spec/08-output-rules.md`
|
||||
|
||||
## 任务
|
||||
|
||||
ECharts 图表组件(≥13 个)、大屏完整布局、数据对接 screen API。
|
||||
|
||||
## Phase-Local Manifest
|
||||
|
||||
```
|
||||
ljwx-platform-screen/src/components/charts/BarChart.vue
|
||||
ljwx-platform-screen/src/components/charts/LineChart.vue
|
||||
ljwx-platform-screen/src/components/charts/PieChart.vue
|
||||
ljwx-platform-screen/src/components/charts/RadarChart.vue
|
||||
ljwx-platform-screen/src/components/charts/GaugeChart.vue
|
||||
ljwx-platform-screen/src/components/charts/ScatterChart.vue
|
||||
ljwx-platform-screen/src/components/charts/MapChart.vue
|
||||
ljwx-platform-screen/src/components/charts/FunnelChart.vue
|
||||
ljwx-platform-screen/src/components/charts/HeatmapChart.vue
|
||||
ljwx-platform-screen/src/components/charts/TreemapChart.vue
|
||||
ljwx-platform-screen/src/components/charts/SankeyChart.vue
|
||||
ljwx-platform-screen/src/components/charts/WaterBallChart.vue
|
||||
ljwx-platform-screen/src/components/charts/RingChart.vue
|
||||
ljwx-platform-screen/src/components/widgets/NumberFlip.vue
|
||||
ljwx-platform-screen/src/components/widgets/ScrollTable.vue
|
||||
ljwx-platform-screen/src/views/home/index.vue
|
||||
```
|
||||
|
||||
## 验收条件
|
||||
|
||||
1. ≥ 13 个图表组件
|
||||
2. 每个组件接收 typed props,无 `any`
|
||||
3. 大屏首页布局完整,使用 DataV 边框装饰
|
||||
4. 数据对接 `/api/screen/*` 接口
|
||||
5. build 通过
|
||||
|
||||
## 可 Bundle
|
||||
|
||||
可与 Phase 17 一起执行。
|
||||
@@ -0,0 +1,63 @@
|
||||
---
|
||||
phase: 19
|
||||
title: "Final Gate and Docs"
|
||||
targets:
|
||||
backend: false
|
||||
frontend: false
|
||||
depends_on: [18]
|
||||
bundle_with: []
|
||||
scope:
|
||||
- "FULL_MANIFEST.txt"
|
||||
- "docs/adr/**"
|
||||
- "README.md"
|
||||
---
|
||||
# Phase 19: Final Gate & Docs
|
||||
|
||||
## 读取清单
|
||||
|
||||
- `CLAUDE.md`(自动加载)
|
||||
- `spec/` 目录全部文件(本 Phase 例外,允许全量扫描)
|
||||
- `spec/08-output-rules.md`
|
||||
|
||||
## 任务
|
||||
|
||||
1. 执行 gate-all.sh 全量校验
|
||||
2. 生成 FULL_MANIFEST.txt(仓库所有文件的完整列表)
|
||||
3. 编写 ADR 文档
|
||||
4. 编写 README.md
|
||||
5. 最终版本验证
|
||||
|
||||
## Phase-Local Manifest
|
||||
|
||||
```
|
||||
FULL_MANIFEST.txt
|
||||
docs/adr/001-module-dependency-dag.md
|
||||
docs/adr/002-audit-field-interceptor.md
|
||||
docs/adr/003-jwt-authorities-convention.md
|
||||
docs/adr/004-frontend-semver-tilde-only.md
|
||||
docs/adr/005-changelog.md
|
||||
README.md
|
||||
```
|
||||
|
||||
## 验收条件
|
||||
|
||||
1. `bash scripts/gates/gate-all.sh` 全部通过
|
||||
2. FULL_MANIFEST.txt 包含仓库所有文件
|
||||
3. gate-manifest.sh FULL 模式校验通过
|
||||
4. 至少 4 个 ADR 文档
|
||||
5. README.md 包含项目简介、快速启动、技术栈、目录结构
|
||||
6. 所有硬规则均满足(可使用 code-reviewer subagent 验证)
|
||||
|
||||
## 版本验证命令
|
||||
|
||||
```bash
|
||||
java -version # 含 21.0.10
|
||||
./mvnw dependency:tree | grep spring-boot # 3.5.11
|
||||
./mvnw dependency:tree | grep mybatis # 3.0.5
|
||||
./mvnw dependency:tree | grep springdoc # 2.8.15
|
||||
docker run --rm postgres:16.12-alpine postgres --version # 16.12
|
||||
node -v # v22.22.0
|
||||
pnpm -v # 10.30.1
|
||||
pnpm list vue --filter ljwx-platform-admin # 3.5.28
|
||||
pnpm list vite --filter ljwx-platform-admin # 7.3.x
|
||||
```
|
||||
Reference in New Issue
Block a user