Files
zkxw/AGENTS.md
T
zlwuandCursor 5e563a6669 fix: 子表保存改为就地同步,避免整批换 id 打断外键。
协议样本、生产产出和盘点明细按行更新并保留原 id;补齐存储证明质检预填、离职人员显示名,以及样本采集通知模板字段。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-28 10:51:53 +08:00

226 lines
19 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# AGENTS.md — zkxw (陇耘鲜达)
Two-package monorepo: Python backend + Vue 3 admin frontend. Named "sanqiang" internally.
## Repository Layout
```
backend/ FastAPI backend (Python 3.9, venv)
web-admin/ Vue 3 admin panel (pnpm, Vite 7, Element Plus, Tailwind CSS 4)
docs/ UI specs, SQL schemas, requirements docs
openspec/ OpenSpec: `config.yaml`; active changes under `openspec/changes/*/`
.cursorrules Detailed UI conventions and constraints (Chinese)
```
## OpenSpec engineering standards
Full specs: `openspec/changes/zkxw-engineering-standards/specs/*/spec.md`. Below are the key SHALL items every coding session MUST follow.
### UI Conventions (web-admin + miniprogram)
- **Visual authority**: `web-admin` SHALL follow `docs/web-admin-ui-spec-fresh-mint.md` (Fresh Mint palette, `--primary #10b981`). `miniprogram` SHALL follow `docs/miniprogram-ui-spec-sanqiang.md` (三墙 palette, brand green `#0a9f6d`).
- **No cross-app color mixing**: `web-admin` SHALL NOT use `#0a9f6d` as primary; `miniprogram` SHALL NOT use `#10b981` as primary.
- **Tree path display**: Dropdown/tree nodes show **node name only**; input fields, table columns, exports, and view dialogs SHALL show **full ancestor path**. Separator: ` / ` (space-slash-space). See `web-admin/src/utils/tree-path.ts`.
- **Dialog forms**: `label-position="top"`. No backdrop blur (`backdrop-filter: none`). Use `.dialog-form-layout` with `max-height: calc(100vh - 100px)`, only body scrolls.
- **Dialog grid**: Default 2-column (`.form-grid-2`). Max 5 columns. Full-width fields use `.form-col-span-2` (`grid-column: 1 / -1`). Narrow viewport SHALL fallback to fewer columns.
- **List panel heading**: `panel-top` → `h2.title-bar-heading` SHALL show primary-color vertical bar via `::before`. Ambiguous titles (e.g. "租户" containing "户") SHALL be registered in `App.vue` `panelTitleBarTextRules`.
- **Miniprogram media**: Thumbnails 1:1 `aspectFill`; carousel 4:3 `aspectFill`; detail images `widthFix`. Min tap target 44×44px.
- **Sidebar menu icons**: First-level menu items SHALL show icons before the name; second-level and deeper items SHALL NOT show icons (text only). Implemented via `level === 0` check in `SidebarSubmenu.vue`.
- **Tab bar (WorkTab) icons**: Tab bar tabs SHALL NOT show icons — only text titles. `meta.icon` is for sidebar only.
- **Form field annotations**: Field hints (e.g. "数值越小越靠前") SHALL appear as small text after the label via `<template #label>字段名<span class="form-label-tip">注释</span></template>`, NOT beside the input control. Style class: `.form-label-tip` (12px, `--mint-text-muted`).
- **Attachment upload card layout**: Upload trigger SHALL be a card (140×96px, dashed border `#a7f3d0`) inline as the first item in the same flex row as uploaded attachment cards; select file → immediate upload via `fetchUploadAttachment`; preview/download/delete on cards; hint below the row. Authority: `ProtocolFormDialog.vue` «协议附件». See `openspec/changes/zkxw-engineering-standards/specs/ui-conventions/spec.md` §「附件上传卡片化布局」for full Vue template + SCSS + interaction rules.
- **Button icons**: All `web-admin` `el-button` with visible text labels SHALL include a leading `<el-icon>` from `@element-plus/icons-vue` (explicit import). See `openspec/.../ui-conventions/spec.md` §「按钮须携带图标」.
- **Sort fields (sort_order/orderNum)**: web-admin 列表、新增弹窗、编辑弹窗中默认不展示排序字段。后续根据功能需要自行添加。
- **Status/conclusion/type fields as Tags**: All status, conclusion, and type fields in table columns and view dialogs SHALL render as `<el-tag>` with semantic colors. Tables: use `<el-tag :type="..." size="small">` in column templates. SystemViewDialog: use `tagType` property on `ViewField`. Semantic colors: `success`=正常/启用/已完成/通过, `warning`=进行中/待确认/已过期, `danger`=异常/已作废/拒绝/不通过/质检待执行/质检待结论, `info`=待生效/停用/默认, `primary`=特殊分类/来源类型. SHALL NOT use plain text or custom `<span class="tag">`. See `openspec/.../ui-conventions/spec.md` "状态、结论与类型字段使用 Tag 标签展示".
- **Template pages** — new pages SHALL match these exactly:
- Standard list: `src/views/system/unit/index.vue`
- Complex list: `src/views/system/order/index.vue`
- Product list + view dialog: `src/views/system/product/index.vue`
- Tree master data: `src/views/system/category/index.vue`
- User CRUD: `src/views/system/user/index.vue`
- **UI pattern index** — before implementing a new page, developers SHALL select a pattern code (P01–P27) per `docs/web-admin-ui-pattern-index.md` and OpenSpec `ui-conventions` §「界面模式索引与参考页选型」; PRs SHALL cite pattern code and reference page path.
### API Conventions (backend)
- **Cell-storage sample cross-table key**: Matching `biz_protocol_sample` to downstream rows (sample reception, QC work orders, etc.) SHALL use `tenant_no` + `sample_code` only—not `collection_protocol_id` or `protocol_sample_id` (protocol may change; sample code does not). See `openspec/changes/zkxw-engineering-standards/specs/api-conventions/spec.md` §「细胞存储样本跨表关联键」.
- **Tenant isolation**: All business data queries SHALL filter by current user's `tenant_no`. All write/update endpoints SHALL set `tenant_no` to the current user's tenant. MUST NOT trust client-submitted tenant overrides (unless multi-tenant admin API with security review).
- **Data permission**: All business queries SHALL apply data scope filtering based on the union of all roles assigned to the current user (menu + data permissions both use union semantics).
- **Default sort**: Lists SHALL sort by `create_time DESC` when no custom sort is specified.
- **Route registration**: New route modules SHALL be registered in `app/api/__init__.py` AND `app/main.py`. SHALL NOT bypass auth/crypto/audit middleware.
### Database Conventions
- **Engine**: MySQL 5.7, charset utf8mb4.
- **Public fields** (every business table SHALL include these after business fields): `id` VARCHAR(64) UUID, `tenant_no`, `order_num`, `remark`, `create_by`, `create_dept_id`, `create_time`, `update_by`, `update_time`, `del_flag` (default `0`).
- **Field order**: `id` → business fields → public fields (`tenant_no` through `del_flag`).
- **Soft delete**: SHALL NOT physically delete business data. Delete = set `del_flag=1`. Queries SHALL exclude `del_flag=1` by default.
- **Primary key**: `id` SHALL be `VARCHAR(64) NOT NULL`, UUID value.
### Architecture Conventions
- **Monorepo structure**: Python backend (`backend/`) + Vue 3 web-admin (`web-admin/`) + uni-app miniprogram (`miniprogram/`). Shared backend and tenant model.
- **File attachments**: SHALL use object storage (MinIO). Multiple attachments in one field SHALL be comma-separated URLs.
- **Authoritative docs**: `docs/中科兴皖需求V0.3.md` (requirements), `docs/web-admin-ui-spec-fresh-mint.md` (web UI), `docs/miniprogram-ui-spec-sanqiang.md` (miniprogram UI).
### Coding Conventions
- **Minimal changes**: Commits SHALL contain only what's needed. No unrelated refactoring or formatting.
- **Frontend (web-admin)**: pnpm, Prettier (single quotes, no semicolons, 2-space indent). Vue/Element Plus auto-imports — SHALL NOT manually import auto-imported APIs/components.
- **Backend**: Python 3.9, FastAPI + SQLAlchemy + Pydantic v2. Routes stay thin; business logic goes in `app/services/` and `app/crud/`. Config via `pydantic-settings` + `.env` files.
- **DB changes**: SHALL maintain SQL in `docs/sql/`. Schema changes SHALL align with `database-conventions` public field rules.
- **Commit messages**: SHALL be complete sentences describing "what changed and why". Follow Conventional Commits (`feat|fix|docs|…`).
- **Field consistency (CRUD ↔ Schema ↔ Frontend)**: When implementing CRUD for any entity, the `_row_to_dict` function is the **single source of truth** for all readable fields. The CreateBody/UpdateBody Pydantic schemas and the frontend `formData` initialization SHALL include every writable field present in `_row_to_dict` (excluding computed/read-only fields like `id`, `createTime`, `updateTime`, `stockQty`). Run `python scripts/check_field_consistency.py` after implementation to verify. See `docs/dev-field-consistency.md` for details.
- **Child-row save(禁止整表删除再新增)**: 保存子表时 SHALL 按行处理:已有行就地更新,只插入新增行,只软删除用户去掉的行。客户端可以提交稳定 `id`,字段变化时该行必须保留原 `id`。请求未带 `id` 时,先用稳定业务键(样本编码、物料编码、条码、组分 id 等)匹配一条尚未占用的有效行,匹配不到再插入新 UUID。属于其他父记录或其他租户的 `id` SHALL 拒绝。SHALL NOT 在保存时把子表全部软删除再整批插入新行——其他表会拿这些 id 做外键(`production_step_output_id`、`protocol_sample_id`、`donation_sample_id`),整批换 id 会把关联打断。删除父记录时可以级联软删除其子行。复制或克隆父记录时 SHALL 为子行分配新 id。实现入口:`backend/app/crud/child_row_sync.py` 的 `sync_child_rows`。
### Permission Conventions
- **Role-menu**: Selecting a child menu SHALL auto-include all ancestor menus (no orphan leaves).
- **Data scope**: Per role — all / own dept / own dept and below / self only / custom. Custom uses `sys_role_data_scope_detail` with `scope_type` (dept/user) + `scope_value`.
- **Multi-role union**: A user with multiple roles gets the **union** of menu permissions and the **union** of data scopes across all roles.
- **Dual menu trees**: Web and miniprogram each have a separate top-level virtual root (`parent_id=0`). New menus SHALL be placed under the correct root.
- **Tenant management**: Accessible ONLY by `super_admin`. All other system management runs within tenant context.
### Workflow Conventions (target design, not yet implemented)
- **Engine**: SHALL be implemented in Python backend as an independent, configurable approval/flow subsystem.
- **Node types**: Start, End, Approver, Conditional Branch (multi-branch supported). Extensible but SHALL NOT break published definitions.
- **Approver strategies**: Specified users, specified roles, or SQL-based resolution. Per-node: "any one passes" or "all must pass".
- **Versioning**: Each flow definition has a version. Running instances SHALL stick to their launch version; new versions only affect new launches.
- **Task centers**: System SHALL provide "Pending Tasks" and "Completed Tasks" views.
## Backend (`backend/`)
### Stack
FastAPI · SQLAlchemy · Pydantic v2 · MySQL (pymysql) · Redis · MinIO · JWT auth · Alembic migrations
### Commands
```bash
source venv/bin/activate
pip install -r requirements.txt
cp .env.example .env.local # fill in DB, Redis, MinIO, etc.
./start-local.sh # uvicorn --reload on 127.0.0.1:8000
./start-prod.sh start # production on :8111 (reads .env.production, PID-managed)
./start-prod.sh status|stop|restart|logs
./start-test.sh start # test on :8112 (reads .env.test, PID-managed)
./start-test.sh activate # source .venv/bin/activate (interactive shell)
./start-test.sh status|stop|restart|logs
```
### Entrypoint & Structure
- Entrypoint: `app/main.py` → `app.main:app`
- **`app/api/`** — Route files: `public.py` (no auth), `menu.py`, `admin.py`. Register new routers in `app/api/__init__.py` and `app/main.py`.
- **`app/core/`** — Config (`config.py`), DB (`database.py`), auth (`auth.py`, `security.py`), middleware (`api_crypto_middleware.py`, `api_request_log_middleware.py`), RSA login crypto (`login_crypto.py`)
- **`app/models/`** — SQLAlchemy models (`models.py`, `base.py`)
- **`app/schemas/`** — Pydantic schemas per domain (auth, order, product, user, etc.)
- **`app/crud/`** — Database CRUD operations (`base.py` has generic base)
- **`app/services/`** — Business logic layer
- **`app/tasks/`** — APScheduler scheduled tasks
- **`scripts/`** — SQL migrations and one-off scripts
### Key Patterns
- API prefix: `/api` (set via `API_V1_PREFIX` in config). Routes: `/api/public/*`, `/api/admin/*`, `/api/supplier/*`, `/api/client/*`, `/api/deliver/*`
- Auth: JWT + RSA-encrypted login. Login RSA key auto-generated or set via `LOGIN_RSA_PRIVATE_KEY_PEM` env var.
- API encryption middleware: symmetric crypto layer controlled by `API_ENCRYPT_*` env vars. Excluded paths listed in `API_ENCRYPT_EXCLUDE_PATHS`.
- Config: pydantic-settings (`app/core/config.py`), reads from `.env` / `.env.local` / `.env.production` / `.env.test` (via `ENV_FILE`).
- Database init / schema baseline: `docs/sql/zkxw.sql`(从当前库导出的全量 SQL)。Default admin: `admin` / `111111`, role `super_admin`.
- Swagger docs at `/docs`, ReDoc at `/redoc`.
- Tests: minimal, only `tests/test_api_crypto.py`. Run with `pytest`.
## Frontend (`web-admin/`)
### Stack
Vue 3 · TypeScript (strict: false, noImplicitAny: false) · Vite 7 · Element Plus · Tailwind CSS 4 · Pinia · vue-router (hash mode) · SCSS · Axios · pnpm
### Commands
```bash
pnpm install # if fails: pnpm install --ignore-scripts
pnpm dev # Vite dev server (port from .env.development)
pnpm build # production build
pnpm build:typecheck # typecheck + build (vue-tsc --noEmit)
pnpm lint # ESLint check
pnpm fix # ESLint --fix
pnpm lint:prettier # Prettier format all
pnpm lint:stylelint # Stylelint fix SCSS/Vue
pnpm commit # cz-git interactive commit
pnpm clean:dev # strip demo data for fresh start
```
### Entrypoint & Structure
- Entrypoint: `src/main.ts`
- **`src/views/system/`** — Main business pages: `category/`, `order/`, `product/`, `tenant/`, `unit/`, `user/`
- **`src/api/`** — Axios API modules: `auth.ts`, `admin.ts`, `business.ts`, `system.ts`, `chinaRegion.ts`
- **`src/store/modules/`** — Pinia stores: `user.ts`, `menu.ts`, `setting.ts`, `table.ts`, `worktab.ts`
- **`src/router/modules/`** — Route modules: `index.ts`, `sqxp.ts`, `exception.ts`
- **`src/utils/tree-path.ts`** — Tree path utilities (`buildTreePathLabelFromFlat`, etc.). Separator: ` / ` (spaced slash).
### Path Aliases (tsconfig + vite)
`@/*` → `src/*`, `@views/*`, `@imgs/*`, `@icons/*`, `@utils/*`, `@stores/*`, `@plugins/*`, `@styles/*`
### Key Patterns
- **Auto-imports**: `unplugin-auto-import` + `unplugin-vue-components` — Vue, vue-router, pinia, @vueuse/core APIs and Element Plus components are auto-imported. Don't manually import them.
- **Element Plus**: auto-imported resolvers — use components directly, no manual imports.
- **Tailwind CSS 4**: via `@tailwindcss/vite` plugin.
- **SCSS**: global mixin/data injected via `vite.config.ts` `additionalData` (uses `@styles/core/el-light.scss` and `@styles/core/mixin.scss`).
- **API proxy**: dev server proxies `/api` → `VITE_API_PROXY_URL` (localhost:8000). Also `/web` and `/mini` proxies configured.
- **Env files**: `.env.development` (local), `.env.production` (prod). Uses `VITE_*` prefix.
- **API encryption**: `VITE_API_ENCRYPT_ENABLED` toggles request/response crypto in dev. Production has it on.
- **Hash routing**: URLs use `/#/` format.
- **Access mode**: `VITE_ACCESS_MODE=frontend` (fixed frontend menu mode).
### Code Style
- **Prettier**: single quotes, no semicolons, 2-space indent, trailing comma: none, `vueIndentScriptAndStyle: true`
- **ESLint**: extends recommended + typescript + vue essential + prettier. `@typescript-eslint/no-explicit-any` is OFF.
- **Stylelint**: SCSS + Vue + recess-order property sorting.
- **Commit**: Conventional commits via `cz-git` + `commitlint`. Types: `feat|fix|docs|style|refactor|perf|test|build|ci|revert|chore|wip`.
- **Husky + lint-staged**: pre-commit auto-runs ESLint, Prettier, Stylelint on staged files.
## UI Design Authority
**The `.cursorrules` file and `docs/` are the authoritative source for all UI decisions.**
- **Web admin UI spec**: `docs/web-admin-ui-spec-fresh-mint.md` + `docs/fresh-mint-green.html`
- **Template pages** (match these exactly for new pages):
- Standard list: `src/views/system/unit/index.vue` (`/#/demo/unit`)
- Complex list: `src/views/system/order/index.vue` (`/#/demo/order`)
- Product list + view dialog: `src/views/system/product/index.vue`
- Tree master data: `src/views/system/category/index.vue`
- User CRUD: `src/views/system/user/index.vue`
- Console/dashboard: `src/views/dashboard/console/index.vue`
- **Color tokens** (web-admin): Primary `#10b981`, dark `#059669`, bg `#ecfdf5`. Fresh Mint palette. **Do NOT use miniprogram colors in web-admin.**
- **Tree display**: dropdown shows node name; selected/input/table shows full path (spaced slash separator).
## Knowledge Graph (代码知识图谱)
项目已生成代码知识图谱,用于理解模块间依赖关系、追踪修改影响范围。
### 图谱位置
| 项目 | 报告 | 可视化 | 数据 |
|------|------|--------|------|
| 后端 | `backend/graphify-out/GRAPH_REPORT.md` | `backend/graphify-out/graph.html` | `backend/graphify-out/graph.json` |
| 前端 | `web-admin/graphify-out/GRAPH_REPORT.md` | `web-admin/graphify-out/graph.html` | `web-admin/graphify-out/graph.json` |
### 使用方式
- **浏览器**:直接打开 `*/graphify-out/graph.html` 查看交互式图谱
- **AI Agent 查询**:已配置 MCP 服务(graphify),可通过工具调用查询图谱
- **手动更新**:`./scripts/update-graph.sh [backend|frontend|all]`
### Agent 更新规则
完成代码修改后(新建文件、修改函数签名、新增/删除 import、重构模块),Agent SHALL 执行 `./scripts/update-graph.sh` 增量更新图谱,确保图谱与代码保持同步。如果只修改了单个项目(backend 或 web-admin),只需更新对应项目即可。
## Important Constraints
- **Language**: User communication in Chinese (简体中文) when user writes in Chinese.
- **Minimal changes**: Don't expand scope, don't refactor unrelated code. Match surrounding naming/type/import style.
- **Cross-app colors**: Web admin uses Fresh Mint (`#10b981`), miniprogram uses 三墙 (`#0a9f6d`). Don't mix.
- **No backdrop blur on modals**: `backdrop-filter: none`.
- **Dialog forms**: `label-position="top"`.
- **Table action column**: `fixed="right"`, min-width ≥ 260px.
- **Table column widths**: All `el-table-column` SHALL use `min-width` (not `width`) for proportional scaling when viewport resizes.
- **Toolbar**: "已选 N 项" first, then primary action button (only one `type="primary"` per toolbar row), no `space-between` push.
- **Database changes**: always pair with schema SQL in `docs/sql/` and evaluate API/TypeScript type impact.
- **Child-row save**: SHALL NOT soft-delete every child row and insert new rows on save. Update in place, insert new rows, soft-delete only removed rows. See Coding Conventions.
- **进程重启**:修改 bug 时,如果前端或后端需要重启,Agent 只告知用户,由用户自行重启。Agent 不得自行停止、启动或重启 `web-admin` 开发服务与 `backend` 服务。