执行中的运输计划不再随意改单;库存流水记到装卸产品明细;手工派车日历区分酸碱并记录数量。同时收紧合同附件与子表的差量保存,并清理已废弃的 bak 备份目录。 Co-authored-by: Cursor <cursoragent@cursor.com>
24 KiB
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-adminSHALL followdocs/web-admin-ui-spec-fresh-mint.md(Fresh Mint palette,--primary #10b981).miniprogramSHALL followdocs/miniprogram-ui-spec-sanqiang.md(三墙 palette, brand green#0a9f6d). - No cross-app color mixing:
web-adminSHALL NOT use#0a9f6das primary;miniprogramSHALL NOT use#10b981as 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). Seeweb-admin/src/utils/tree-path.ts. - Dialog forms:
label-position="top". No backdrop blur (backdrop-filter: none). Use.dialog-form-layoutwithmax-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-headingSHALL show primary-color vertical bar via::before. Ambiguous titles (e.g. "租户" containing "户") SHALL be registered inApp.vuepanelTitleBarTextRules. - Miniprogram media: Thumbnails 1:1
aspectFill; carousel 4:3aspectFill; detail imageswidthFix. 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.iconis 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 viafetchUploadAttachment; preview/download/delete on cards; hint below the row. Authority:ProtocolFormDialog.vue«协议附件». Seeopenspec/.../ui-conventions/spec.md§「附件上传卡片化布局」. - Miniprogram photo evidence upload (
miniprogram, baseline): Photo/album images only —uni.chooseImage+uni.uploadFile(name: file,formData.folder, Bearer header) → collectdata.url→ business API submitsimageUrls. SHALL NOT use Webel-upload/FormData+request, Base64-in-JSON, or document pickers (chooseMessageFile). Upload path SHALL be inAPI_ENCRYPT_EXCLUDE_PATHS. Authority:openspec/.../ui-conventions§「小程序拍照凭证上传(基线)」+api-conventions§「小程序拍照凭证上传接口」; reference pattern fromsqxp-wxdelivery/supplier evidence upload. - Button icons: All
web-adminel-buttonwith visible text labels SHALL include a leading<el-icon>from@element-plus/icons-vue(explicit import). Seeopenspec/.../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 viael-ui.scss). Tables: use<el-tag :type="..." size="small">in column templates. SystemViewDialog: usetagTypeproperty onViewField. 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. Seeopenspec/.../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 inSystemViewDialog#hero-actions. Reference:crm/product/index.vue,system/user/index.vue. Seeopenspec/.../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
- Standard list:
- UI pattern index — before implementing a new page, developers SHALL select a pattern code (P01–P27) per
docs/web-admin-ui-pattern-index.mdand OpenSpecui-conventions§「界面模式索引与参考页选型」; PRs SHALL cite pattern code and reference page path.
API Conventions (backend)
- Cell-storage sample cross-table key: Matching
biz_protocol_sampleto downstream rows (sample reception, QC work orders, etc.) SHALL usetenant_no+sample_codeonly—notcollection_protocol_idorprotocol_sample_id(protocol may change; sample code does not). Seeopenspec/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 settenant_noto 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 DESCwhen no custom sort is specified. - Route registration: New route modules SHALL be registered in
app/api/__init__.pyANDapp/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):
idVARCHAR(64) UUID,tenant_no,order_num,remark,create_by,create_dept_id,create_time,update_by,update_time,del_flag(default0). - Field order:
id→ business fields → public fields (tenant_nothroughdel_flag). - Soft delete: SHALL NOT physically delete business data. Delete = set
del_flag=1. Queries SHALL excludedel_flag=1by default. - Primary key:
idSHALL beVARCHAR(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/andapp/crud/. Config viapydantic-settings+.envfiles. - DB changes: SHALL maintain SQL in
docs/sql/. Schema changes SHALL align withdatabase-conventionspublic 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_dictfunction is the single source of truth for all readable fields. The CreateBody/UpdateBody Pydantic schemas and the frontendformDatainitialization SHALL include every writable field present in_row_to_dict(excluding computed/read-only fields likeid,createTime,updateTime,stockQty). Runpython scripts/check_field_consistency.pyafter implementation to verify. Seedocs/dev-field-consistency.mdfor 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_detailwithscope_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 inapp/api/__init__.pyandapp/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.pyhas generic base)app/services/— Business logic layerapp/tasks/— APScheduler scheduled tasksscripts/— SQL migrations and one-off scripts
Key Patterns
- API prefix:
/api(set viaAPI_V1_PREFIXin 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_PEMenv var. - API encryption middleware: symmetric crypto layer controlled by
API_ENCRYPT_*env vars. Excluded paths listed inAPI_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, rolesuper_admin. - Swagger docs at
/docs, ReDoc at/redoc. - Tests: minimal, only
tests/test_api_crypto.py. Run withpytest.
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.tssrc/store/modules/— Pinia stores:user.ts,menu.ts,setting.ts,table.ts,worktab.tssrc/router/modules/— Route modules:index.ts,sqxp.ts,exception.tssrc/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/viteplugin. - SCSS: global mixin/data injected via
vite.config.tsadditionalData(uses@styles/core/el-light.scssand@styles/core/mixin.scss). - API proxy: dev server proxies
/api→VITE_API_PROXY_URL(localhost:8000). Also/weband/miniproxies configured. - Env files:
.env.development(local),.env.production(prod). UsesVITE_*prefix. - API encryption:
VITE_API_ENCRYPT_ENABLEDtoggles 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-anyis 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
- Standard list:
- 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(notwidth) for proportional scaling. Exception:type="selection"andtype="index"SHALL use fixedwidth(约 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), nospace-betweenpush. - 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>", orgraphify 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)