AGENTS.md 24 KB

AGENTS.md — shanxia2.0 (山峡化工)

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)

Ecosystem (multi-system)

Normative detail: openspec/changes/zkxw-engineering-standards/specs/ecosystem-landscape/spec.md.

System DDL snapshot (docs/sql/) Code location
山峡一体化门户(本仓库) shanxia2.0.sql backend/, web-admin/, miniprogram/
山峡 CRM sx.sql Sibling repo shanxia/crm/backend/ (Java), shanxia/crm/web/ (Vue 3)
地磅系统 dibang.sql Separate system (not this repo's backend)

Do not implement CRM or weighbridge features in shanxia2.0/backend unless an OpenSpec change explicitly covers integration.

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: Every sidebar tree item SHALL show an icon before the name (resolveSidebarMenuIcon; fallback folder/file).
  • 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 (web-admin): 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/.../ui-conventions/spec.md §「附件上传卡片化布局」.
  • Miniprogram photo evidence upload (miniprogram, baseline): Photo/album images only — uni.chooseImage + uni.uploadFile (name: file, formData.folder, Bearer header) → collect data.url → business API submits imageUrls. SHALL NOT use Web el-upload/FormData+request, Base64-in-JSON, or document pickers (chooseMessageFile). Upload path SHALL be in API_ENCRYPT_EXCLUDE_PATHS. Authority: openspec/.../ui-conventions §「小程序拍照凭证上传(基线)」+ api-conventions §「小程序拍照凭证上传接口」; reference pattern from sqxp-wx delivery/supplier evidence upload.
  • 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 (dark background + white text globally via el-ui.scss). Tables: use <el-tag :type="..." size="small"> in column templates. SystemViewDialog: use tagType property on ViewField. Semantic colors: success/primary=#059669, warning=#d97706, danger=#dc2626 (dark bg + white text); info=transparent bg + standard text #1e293b (avoid Element default #909399). SHALL NOT use plain text, custom <span class="tag">, or light-fill tags for non-info types. See openspec/.../ui-conventions/spec.md "状态、结论与类型字段使用 Tag 标签展示".
  • List primary column view link: List pages with read-only view dialogs SHALL use <el-link type="primary"> on the primary name/no column to open the view handler; SHALL NOT duplicate a「查看」button in the operation column when the link exists. Keep @row-dblclick. For standard CRUD lists (edit/delete only), SHALL NOT have an operation column—place「编辑」「删除」in the list toolbar (edit requires exactly one selected row; delete supports batch) and in SystemViewDialog #hero-actions. Reference: crm/product/index.vue, system/user/index.vue. See openspec/.../ui-conventions/spec.md §「列表主标识列链接查看与编辑删除外置」.
  • 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.

子数据保存必须差量更新(强制)

  • 禁止全删全增:编辑、保存、批量更新或导入更新已有主单时,SHALL NOT 先将该主单的全部子数据物理删除或统一设置 del_flag=1,再将请求中的子数据全部重新插入。不得通过前端先调用删除接口再批量新增、ORM 集合清空/替换或删除后重新生成 UUID 绕过此规则。全量提交列表不等于全量重建记录。
  • 差量处理:编辑已有主单时,SHALL 在当前租户、当前主单的有效子行上按下列规则处理。请求里带了本主单、本租户的有效 id:就地修改,保留原 id、创建人和创建时间。没带 id:生成新 id 插入。本次明确不再提交的旧行:软删除(del_flag=1)。不得把产品、人员等主数据 ID 当作明细 ID。
  • 业务键复用(仅供参考):不是每个子表都有稳定业务键,无业务键的子表 SHALL 按上一条处理,不得按名称或其它字段猜配旧行。只有该子表自己定义了稳定业务键时,才可额外约定:没带 id,且该业务键在未被占用的旧行里只命中一条,则复用那一行、不新建。产品品牌、别名以名称作为这类业务键。命中多条时保存前失败,不改任何行。纯关联表若没有明细 id、只有关联组合,仍按该组合比较集合。
  • 身份与历史字段:更新 SHALL 保留原明细 ID、创建人和创建时间,以及本次未提交的业务字段(如实际数量、交付状态、归档金额、回传和附件信息)。关系不变时 SHALL 保留关系记录 ID。编辑载入、提交、保存后继续编辑均须保留服务端明细 ID;复制为新单时须清除原单及各层子明细 ID,并重新建立新单内部关联。此要求同时适用于 Web、移动 H5 和小程序。
  • 遗漏与清空不同:请求里根本没传这个子集合时,原集合保持不变;显式 [] 才表示申请清空,并仍须通过删除校验。字段未提交与显式清空也 SHALL 分别处理,禁止用 schema 默认值或 items or [] 将遗漏误当清空。
  • 校验先于修改:SHALL 在写入前校验明细 ID 的存在性、有效状态、当前租户/主单归属及重复提交。重复 id、别人的 id、已删除 id 保存前直接失败,不改任何行,不得静默当新增。
  • 删除引用保护:移除或改变被引用明细的业务身份前,SHALL 检查运输、库存、回传、归档、审批等下游有效引用;存在引用时应拒绝或走已定义的业务撤销流程。不得让有效数据指向已删除明细,不得借普通保存自动重绑或清理历史异常。在途审批引用的流程节点不得因配置保存而被重建或破坏。
  • 事务与回归:主单、子单及关联变更 SHALL 在同一事务完成,禁止先提交主单再保存子单。相关修改 SHALL 覆盖无变更保存 ID 不变、修改/新增/移除、遗漏/空集合、重复/跨租户/跨主单 ID 拒绝、下游引用保护,以及各客户端编辑/复制 ID 往返的回归测试。
  • 适用边界:明确删除主单时的受控级联软删除、追加新的审批轮次/业务版本不是编辑既有子记录;这些操作仍须保留历史并校验引用。不得将普通保存命名为“重建”“同步”或“重新安排”来规避上述规则。审查时 SHALL 跟踪调用链确认业务语义,不能只凭函数名称判定。
  • 已确认的业务例外:无装货段计划在显式调整运输时,作废旧出入库单并生成新单及现场链接,是用户确认保留的业务流程。此例外仅适用于该出入库单作废重开流程,不适用于归档库存流水、地磅关联或其他明细的普通保存。

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: 工程规范 (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.

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

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 (reads .env.production, PID-managed)
./start-prod.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.
  • 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

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: Business data / action columns SHALL use min-width (not width) for proportional scaling. Exception: type="selection" and type="index" SHALL use fixed width (约 48–55 / 60–68) so they stay compact on wide screens.
  • 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.

graphify

This project has a graphify knowledge graph at graphify-out/.

Rules:

  • Before answering architecture or codebase questions, read graphify-out/GRAPH_REPORT.md for god nodes and community structure
  • If graphify-out/wiki/index.md exists, navigate it instead of reading raw files
  • For cross-module "how does X relate to Y" questions, prefer graphify query "<question>", graphify path "<A>" "<B>", or graphify explain "<concept>" over grep — these traverse the graph's EXTRACTED + INFERRED edges instead of scanning files
  • After modifying code files in this session, run graphify update . to keep the graph current (AST-only, no API cost)