Multi-project workspace. No monorepo tool — each sub-project is independent.
| Directory | Stack | Framework | Port | Notes |
|---|---|---|---|---|
crm/backend/ |
Java 17, Maven | RuoYi-Vue-Plus 5.4.0, Spring Boot 3.4.6 | 8080 | Main backend; Sa-Token auth, MyBatis-Plus ORM |
crm/web/ |
Node 18.18+, npm | Vue 3.5 + TypeScript 5.8 + Vite 6.3 + Element Plus 2.9 | 80 | Main admin frontend; auto-imports Vue APIs |
crm/py-backend/ |
Python | FastAPI / scripts | — | 主工程侧 Python 服务(若使用) |
crm/mobile/ |
Node, pnpm | uni-app Vue 2.6 + uview-ui 2.0 + vue-cli-service | 8081 (H5) | 主工程移动端(纳入仓库时使用该路径) |
bx/bx-backend/ |
Python 3.11+ | FastAPI + SQLAlchemy 2 + Uvicorn | 8011 | 报销(expense) sub-system backend |
bx/bx-web/ |
Node, npm | Vue 3.5 + TypeScript + Vite 6.3 + Element Plus | — | 报销 sub-system admin frontend |
bx/bx-mobile/ |
Node, npm | Vue 3.4 + TypeScript + Vite 5 + Vant 4 | — | 报销 sub-system mobile frontend |
crm/ |
— | — | — | 主工程(智慧校园 / RuoYi)根:crm/backend、crm/web、crm/sql、文档与原型等 |
bx/docs/ |
— | — | — | 报销子系统规范与计划(含 UI 规范、superpowers 计划) |
Non-app under crm/: crm/sql/、crm/yuanxing/、crm/graphify-out/、crm/docs/、crm/*.md 等;报销文档在 bx/docs/;另有 ui-ux-pro-max-skill-main/ 等工具目录。
cd crm/backend
# Build (skips tests by default — <skipTests>true</skipTests> in pom.xml)
mvn clean package -DskipTests
# Start all services in order (monitor → job → main app)
bash start-all.sh
# Or start individually (MUST follow this order):
bash start-monitor.sh # Monitor center → port 9090
bash start-job.sh # SnailJob scheduler → port 8800
bash hot-start.sh # Main app → port 8080 (interactive, supports hot-reload)
# Maven profiles: local, dev (default), prod, prod-local
# (prod-9900 is a Spring profile only, not a Maven profile)
mvn clean package -P dev
hot-start.sh compiles ruoyi-modules/ with mvn clean install first, then ruoyi-admin/. Supports flags: -p <profile>, -P <port>, -c (clean), -d (debug on port 5005), --no-hot.
cd crm/web
npm install --registry=https://registry.npmmirror.com
npm run dev # Dev server on port 80, proxies /prod-api → localhost:8080
npm run build:prod # Production build (increases heap to 8GB)
npm run lint:eslint # Lint
npm run lint:eslint:fix # Lint fix
npm run prettier # Format
Vitest is installed as devDependency but no test script is defined in package.json.
cd crm/mobile
pnpm install # Has .npmrc with shamefully-hoist=true; pnpm-lock.yaml present
pnpm run dev:h5 # H5 development (vue-cli-service uni-serve)
pnpm run dev:mp-weixin # WeChat miniprogram development
pnpm run build:h5 # H5 production build
cd crm/py-backend
bash start.sh # Uses .venv + uvicorn per project script
cd bx/bx-backend
python -m venv .venv && source .venv/bin/activate
pip install -e ".[dev]"
bash start.sh # Reads .env, runs uvicorn on port 8011 (default)
# With debug reload: set DEBUG=true in .env
# Tests: pytest (httpx + pytest in dev deps)
cd bx/bx-web && npm install && npm run dev # Proxies /api → localhost:8011
cd bx/bx-mobile && npm install && npm run dev
crm/backend/
├── ruoyi-admin/ # App entry point (SpringApplication.run)
├── ruoyi-common/ # Shared library modules (20+)
│ ├── ruoyi-common-core/
│ ├── ruoyi-common-mybatis/
│ ├── ruoyi-common-redis/
│ ├── ruoyi-common-satoken/
│ ├── ruoyi-common-web/
│ ├── ruoyi-common-excel/
│ └── ...
├── ruoyi-modules/ # Business modules
│ ├── ruoyi-system/ # System management (users, roles, menus)
│ ├── ruoyi-xg/ # 学工管理 (student affairs) ⭐ main custom module
│ ├── ruoyi-crm/ # CRM module
│ ├── ruoyi-workflow/ # Workflow (warm-flow engine)
│ ├── ruoyi-job/ # Scheduled tasks
│ ├── ruoyi-generator/ # Code generator
│ └── ruoyi-demo/ # Demo/examples
└── ruoyi-extend/
├── ruoyi-monitor-admin/ # Spring Boot Admin → port 9090
└── ruoyi-snailjob-server/ # SnailJob scheduler → port 8800
bx/bx-backend/app/
├── main.py # FastAPI entry (app.main:app)
├── config.py # Settings via pydantic-settings
├── database.py # SQLAlchemy engine/session
├── models/ # SQLAlchemy ORM models
├── schemas/ # Pydantic request/response schemas
├── routers/ # API route handlers
├── services/ # Business logic
└── utils/ # Helpers
org.dromara.{module} — e.g. org.dromara.xg, org.dromara.system{module}:{entity}:{action} (e.g. xg:student:list)BaseMapperPlus<Entity, VO> — provides selectVoPage, selectVoList etc.@AutoMapper annotation on BO/VO classes)TenantEntity, tenant filtering is automaticASSIGN_ID) — NOT auto-incrementdel_flag field with @TableLogic/swagger-ui/index.html (uses javadoc comments, no annotations needed)@ExcelProperty on VO fields for exportAddGroup / EditGroup for create vs update validationcontroller/ → REST endpoints, @SaCheckPermission, @Log, @RepeatSubmit
service/ → Interface (IXgXxxService)
service/impl → Implementation with LambdaQueryWrapper + MapstructUtils.convert
mapper/ → BaseMapperPlus<Xxx, XxxVo> (XML only for custom SQL)
domain/ → Entity (@TableName, extends TenantEntity)
domain/bo/ → Business Object (@AutoMapper(target=Entity), validation groups)
domain/vo/ → View Object (@AutoMapper(target=Entity), @ExcelProperty)
<script setup lang="ts"> with Composition APIunplugin-auto-import@ → src/src/api/{module}/xxx.ts — each file defines Query, Form, VO interfaces + CRUD functionssrc/views/{module}/xxx/index.vue — standard CRUD page (query form → toolbar → table → dialog)v-hasPermi="['xg:student:add']" for button-level permission controluseDict('dict_type') returns reactive dict data; <dict-tag> renders dict labelsVITE_APP_BASE_API (/prod-api) → http://localhost:8080VITE_APP_ENCRYPT=true), RSA key pair in .envuview-ui component library (NOT Vue 3)pnpm (not npm); .npmrc sets shamefully-hoist=true@vue/cli-service (webpack-based), NOT Vitesrc/pages.jsonsrc/common/bx/bx-backend 报销;若使用主工程 Python 则为 crm/py-backend/)sx (shared across all Spring profiles)Asia/Shanghai timezone (set by start scripts)start-all.sh for correct ordering.hot-start.sh builds ruoyi-modules/ before ruoyi-admin/: If you get compile errors in admin, check that ruoyi-modules installed first.<skipTests>true</skipTests> in pom.xmlapplication-local.yml, .env.*.local are gitignored. Only crm/web/.env (production defaults) is tracked.pnpm + webpack; web uses npm + Vite./warm-flow-ui./api → localhost:8011: Not the same proxy path as main web (/prod-api → localhost:8080).crm/backend/, admin UI crm/web/, optional Python crm/py-backend/ — run Maven/npm from these directories (not repo-root backend/ / web/)..cursorrules — Comprehensive code generation rules (Chinese), full-stack conventions, layer patterns, complete code templates. High-value reference for any code generation task.crm/README.md — 主工程目录约定;仓库根 scripts/crm.sh 可在不先 cd 时在 crm/backend|web|py-backend 下执行命令crm/工程本地启动指导.md — Local setup and startup guide with environment installation stepscrm/docs/backend/README-热启动脚本.md — Hot-reload startup script documentation.github/workflows/crm-backend-validate.yml — CI:在 crm/backend 下执行 mvn validate(推送/PR 触及后端时触发)Applies to
bx/bx-web/— Vue 3.5 + TypeScript + Element Plus project.
useServerPaged hook + ListPageScaffold (or FilterBlock + DataTablePanel + ListToolbar) for standard CRUD layout.BaseDialog.vue, BaseButton.vue, BaseInput.vue, BaseSelect.vue, BaseTable.vue, BaseTag.vue, BaseOpButton.vue, BaseClampText.vue, BaseDetailTableSection.vue, ApprovalFlowView.vueListPageScaffold.vue, FilterBlock.vue, DataTablePanel.vue, ListToolbar.vuealign-center prop on el-dialog / BaseDialog. Never use top offset.:loading bound to a loading state variable.loadingMap: Record<string, boolean> pattern to track loading state per action.filterForm values to pager.search({ ...filterForm }). Never call refresh() without filter params.search() not refresh() for user-triggered search: search() resets page to 1 before fetching. refresh() keeps current page.handleReset() should clear filterForm fields then call pager.search().:loading="pager.loading" (or equivalent).:loading: Including query, reset, save, submit, delete, approve, reject, export, etc.const loadingMap = reactive<Record<string, boolean>>({}), then :loading="loadingMap.xxx" on button, loadingMap.xxx = true/false around async call.<el-table-column label="操作"> 必须设置 fixed="right" + class-name="col-actions"。width="120" | 2 个按钮:width="200" | 3 个按钮:width="280" | 4 个按钮:width="360".table-scroll 包裹 el-table,设置 overflow-x: auto。class-name="col-actions" 配合 CSS .col-actions .cell { white-space: nowrap; }。