From 1c664060aba68713f451855640b61abef02e31e0 Mon Sep 17 00:00:00 2001 From: lewis_yan Date: Wed, 1 Apr 2026 18:48:30 +0800 Subject: [PATCH] docs: add .planning/codebase/ with 7 structured documents Mapped codebase with 4 parallel agents covering: - TECH: STACK.md, INTEGRATIONS.md - ARCH: ARCHITECTURE.md, STRUCTURE.md - QUALITY: CONVENTIONS.md, TESTING.md - CONCERNS: CONCERNS.md --- .planning/codebase/ARCHITECTURE.md | 208 +++++++++++++++++++ .planning/codebase/CONCERNS.md | 248 ++++++++++++++++++++++ .planning/codebase/CONVENTIONS.md | 316 +++++++++++++++++++++++++++++ .planning/codebase/INTEGRATIONS.md | 295 +++++++++++++++++++++++++++ .planning/codebase/STACK.md | 227 +++++++++++++++++++++ .planning/codebase/STRUCTURE.md | 247 ++++++++++++++++++++++ .planning/codebase/TESTING.md | 238 ++++++++++++++++++++++ 7 files changed, 1779 insertions(+) create mode 100644 .planning/codebase/ARCHITECTURE.md create mode 100644 .planning/codebase/CONCERNS.md create mode 100644 .planning/codebase/CONVENTIONS.md create mode 100644 .planning/codebase/INTEGRATIONS.md create mode 100644 .planning/codebase/STACK.md create mode 100644 .planning/codebase/STRUCTURE.md create mode 100644 .planning/codebase/TESTING.md diff --git a/.planning/codebase/ARCHITECTURE.md b/.planning/codebase/ARCHITECTURE.md new file mode 100644 index 0000000..52e8bf6 --- /dev/null +++ b/.planning/codebase/ARCHITECTURE.md @@ -0,0 +1,208 @@ +# Architecture + +**Analysis Date:** 2026-04-01 + +## Pattern Overview + +**Overall:** Django REST Framework backend with Vue 3 SPA frontend, built on django-vue3-admin base + +**Key Characteristics:** +- Backend uses Django 4.2 with DRF for API-first design +- Frontend is Vue 3 + TypeScript + Vite SPA +- JWT authentication via SimpleJWT +- Real-time support via Django Channels (WebSocket) +- Backend-controlled routing (menu/permissions served via API) +- Custom apps extend dvadmin base RBAC system + +## Layers + +**Django Application Layer:** +- Purpose: Main Django project configuration and URL routing +- Location: `backend/application/` +- Contains: `settings.py` (imports config from `conf/env.py`), `urls.py`, `asgi.py`, `wsgi.py`, `celery.py` +- Depends on: All Django apps +- Used by: uvicorn/ASGI server + +**Custom Business Apps Layer (pisadmin, pissupplier):** +- Purpose: Procurement and supplier quotation business logic +- Location: `backend/apps/pisadmin/` and `backend/apps/pissupplier/` +- Contains: Models, Views (ViewSets), Serializers, URLs for procurement RFQ, cost templates, suppliers +- Depends on: dvadmin utils, Django REST Framework +- Used by: Frontend SPA via REST API + +**dvadmin Core Layer:** +- Purpose: Base RBAC system - users, roles, menus, departments, permissions +- Location: `backend/dvadmin/system/` +- Contains: Auth models (Users, Roles, Menus), views, serializers +- Depends on: Django contrib apps +- Used by: Custom apps, frontend + +**dvadmin Utils Layer:** +- Purpose: Reusable utilities - CustomModelViewSet, filters, permissions, pagination, exception handling +- Location: `backend/dvadmin/utils/` +- Contains: `viewset.py` (CustomModelViewSet base), `filters.py`, `permission.py`, `pagination.py`, `serializers.py` +- Depends on: Django REST Framework +- Used by: All ViewSets across apps + +**Frontend SPA Layer:** +- Purpose: Vue 3 single-page application for UI +- Location: `web/src/` +- Contains: Components, views, stores (Pinia), router, API clients +- Depends on: Vue 3, Element Plus, FastCRUD, Pinia, Vue Router + +## Data Flow + +**Frontend-to-Backend API Flow:** + +``` +Vue Component (Page) + -> Pinia Store (State Management) + -> API Client (src/api/*/index.ts) + -> HTTP Request (Axios wrapper) + -> Django REST Framework ViewSet + -> Django Serializer (Validation) + -> Django Model (Database) + -> Database Response + -> Serializer Response + -> JSON API Response + -> Pinia Store Update + -> Vue Component Re-render +``` + +**Authentication Flow:** + +``` +Login Page -> POST /api/login/ -> JWT Token Response + -> Token stored in Session storage + -> All subsequent requests include JWT header + -> Backend validates JWT, attaches user to request + -> Permission classes check role/menu permissions +``` + +**Menu/Routing Flow (Backend-Controlled):** + +``` +User Login -> GET /api/system/get_menu/ + -> Returns user-specific menu tree from Menu model + -> Frontend builds routes dynamically + -> Cached in frontendMenu store +``` + +**WebSocket Flow:** + +``` +Frontend connects to /ws// + -> Django Channels AuthMiddlewareStack validates JWT + -> URLRouter routes to MegCenter consumer + -> Consumer handles real-time messaging +``` + +## Key Design Patterns + +**CustomModelViewSet Pattern:** +All custom ViewSets inherit from `dvadmin.utils.viewset.CustomModelViewSet` which extends DRF's `ModelViewSet`. This provides: +- Standardized JSON responses (`SuccessResponse`, `DetailResponse`, `ErrorResponse`) +- Automatic filtering with `DataLevelPermissionMargeFilter` (data scope permissions) +- Field-level permissions via `FieldPermission` model +- Import/export mixins +- Custom serializer selection per action (`create_serializer_class`, `update_serializer_class`) + +**CoreModel Pattern:** +All models inherit from `dvadmin.utils.models.CoreModel` which provides: +- Audit fields: `creator`, `modifier`, `dept_belong_id`, `create_datetime`, `update_datetime` +- Auto-setting creator/modifier on create/update +- `SoftDeleteModel` support for soft deletes + +**Status Workflow Pattern (Inquiry/RFQ):** +The `Inquiry` model uses a status field with workflow transitions: +``` +1(开立) -> 2(确认) -> 3(发布) -> 4(报价中) -> 5(报价结束) + -> 6(比议价中) -> 7(价格审核) -> 8(核价通过) +9(落标结束) or 0(作废) +``` +Each transition is handled by a dedicated `@action` method on the ViewSet. + +**Template-Generated Quotation Pattern:** +- `CostEstimateTemplateHead` + `CostEstimateTemplateBody` define a cost structure template +- When Inquiry is published, `_create_supplier_quotations` iterates the template to generate `QuotationMaster` + child tables for each supplier +- Template fields with `is_computed=1` or `supplier_required in (1,2)` are prefilled in quotations + +## Module Responsibilities + +**pisadmin.miscprocurement:** +- `Inquiry`: RFQ (Request for Quotation) master with workflow states +- `InquirySupplier`: Supplier invite list per RFQ +- `InquiryMaterialCost`, `InquiryProcessCost`, `InquiryOtherCost`, `InquiryProfitCost`: Cost breakdown per RFQ line item +- `InquiryRfqItem`: Upper-level material items for the RFQ +- `CostEstimateTemplateHead/Body`: Cost template structure definition +- `MiscLowPriceHeader/Detail`: Comparison price records (lowest price tracking) +- `MiscNegotiationRecords`: Bargaining records +- `RFQOperationLogs`: Audit trail for RFQ operations + +**pisadmin.basicinfo:** +- `Company`: Company/factory information +- `Currency`: Currency and tax rate configuration +- `Supplier`: Supplier master data +- `SupplierUser`: Supplier user accounts +- `Unit`: Measurement units +- `SystemNoRule`: Document numbering rules (e.g., RFQ numbers, quotation numbers) +- `EmailNotice`: Email notification queue + +**pissupplier:** +- `QuotationMaster`: Supplier quotation header +- `QuotationMaterial`, `QuotationProcess`, `QuotationOther`, `QuotationProfit`: Quotation cost lines +- `QuotationItem`: Upper-level product items in quotation +- `QuotationAttachment`: Quotation attachments + +**dvadmin.system:** +- `Users`: User accounts with role/dept associations +- `Role`: Role definitions with permission keys +- `Menu`: Menu tree for UI and permission routing +- `MenuButton`: Button-level permissions on menus +- `MenuField`: Field-level permissions per model +- `Dept`: Department hierarchy +- `FieldPermission`: Role-field permission matrix +- `RoleMenuPermission`, `RoleMenuButtonPermission`: Role-menu assignments +- `OperationLog`, `LoginLog`: Audit logging +- `SystemConfig`: System configuration key-value store +- `Dictionary`: Dropdown value definitions +- `FileList`: File upload management +- `ApiWhiteList`: Public API endpoints bypassing auth + +## Error Handling + +**Backend Exception Handling:** +- Custom exception handler: `dvadmin.utils.exception.CustomExceptionHandler` +- DRF exceptions caught and formatted to standard JSON response +- Validation errors return 400 with field-specific messages + +**Frontend Error Handling:** +- Axios interceptor catches HTTP errors +- 401 redirects to login +- 403 shows permission denied +- 500 shows generic error message +- API errors display returned `msg` field + +## Cross-Cutting Concerns + +**Authentication:** SimpleJWT with 24-hour access tokens, 1-day refresh tokens. `CustomBackend` allows username OR email login. + +**Authorization:** +- Menu-level via `Menu` model and role assignments +- Button-level via `MenuButton` and `RoleMenuButtonPermission` +- Field-level via `MenuField` and `FieldPermission` +- Data scope via `DataLevelPermissionMargeFilter` (dept-based filtering) + +**Logging:** +- API access logged via `ApiLoggingMiddleware` +- Operation logs stored in `OperationLog` model +- RFQ operations logged to `RFQOperationLogs` + +**Validation:** +- Serializer-level validation in DRF +- Custom validators in `dvadmin.utils.validator` +- Business logic validation in ViewSet methods (e.g., workflow state checks) + +--- + +*Architecture analysis: 2026-04-01* diff --git a/.planning/codebase/CONCERNS.md b/.planning/codebase/CONCERNS.md new file mode 100644 index 0000000..f81ab46 --- /dev/null +++ b/.planning/codebase/CONCERNS.md @@ -0,0 +1,248 @@ +# Codebase Concerns + +**Analysis Date:** 2026-04-01 + +## Security Concerns + +### Hardcoded Django SECRET_KEY + +**Severity:** Critical + +- **File:** `backend/application/settings.py:31` +- **Issue:** Django SECRET_KEY is hardcoded in source code: `"django-insecure--z8%exyzt7e_%i@1+#1mm=%lb5=^fx_57=1@a+_y7bg5-w%)sm"` +- **Impact:** If this code is committed to version control or accessible externally, attackers can forge session cookies, CSRF tokens, and other signed data. +- **Recommendation:** Move SECRET_KEY to `conf/env.py` and load from environment variable. Generate a new key for production. + +### DEBUG Mode Enabled in Production Configuration + +**Severity:** High + +- **File:** `backend/conf/env.py:39` +- **Issue:** `DEBUG = True` is set in the actual environment configuration file +- **Impact:** This enables Swagger UI, detailed error pages, and sets `permission_classes = [permissions.AllowAny]` on schema views (`backend/application/urls.py:45`). It also bypasses authentication requirements for API documentation. +- **Recommendation:** Set `DEBUG = False` in `conf/env.py` for production deployments. The code already has conditional logic to handle this. + +### ALLOWED_HOSTS = ["*"] + +**Severity:** High + +- **Files:** `backend/conf/env.py:48`, `backend/conf/env.example.py:48`, `backend/application/settings.py:44` +- **Issue:** Backend accepts requests from any host +- **Impact:** Allows host header attacks, including cache poisoning and CSS injection. +- **Recommendation:** Specify explicit hosts in `ALLOWED_HOSTS` for production. + +### CORS Allows All Origins + +**Severity:** High + +- **File:** `backend/application/settings.py:179-181` +- **Issue:** + ```python + CORS_ORIGIN_ALLOW_ALL = True + CORS_ALLOW_CREDENTIALS = True + ``` +- **Impact:** Combined with `AllowAny` permission classes, any website can make authenticated requests to the API, potentially leaking user data. +- **Recommendation:** Configure `CORS_ALLOWED_ORIGINS` with explicit allowed origins. + +### Weak Password Hashing (MD5 Fallback) + +**Severity:** Critical + +- **Files:** + - `backend/dvadmin/utils/backends.py:30-33` + - `backend/dvadmin/system/models.py:91` + - `backend/dvadmin/system/views/login.py:275` + +- **Issue:** Authentication backend falls back to MD5 hashing when PBKDF2 verification fails: + ```python + verify_password = check_password(password, user.password) + if not verify_password: + password = hashlib.md5(password.encode(encoding='UTF-8')).hexdigest() + verify_password = check_password(password, user.password) + ``` + The `set_password` method also uses MD5: + ```python + def set_password(self, raw_password): + if raw_password: + super().set_password(hashlib.md5(raw_password.encode(encoding="UTF-8")).hexdigest()) + ``` + +- **Impact:** MD5 is cryptographically broken and provides no protection against rainbow table attacks. User passwords in the database are effectively stored in plaintext. +- **Recommendation:** Remove MD5 fallback entirely. Migrate existing MD5-stored passwords to proper bcrypt/PBKDF2 hashing on next login. + +### API Endpoint Allows Unauthenticated Access in DEBUG + +**Severity:** Medium + +- **File:** `backend/application/urls.py:45` +- **Issue:** Swagger schema view permission depends on DEBUG flag: + ```python + permission_classes = [permissions.AllowAny, ] if settings.DEBUG else [permissions.IsAuthenticated, ] + ``` +- **Impact:** Since DEBUG is True, API documentation is publicly accessible, potentially revealing endpoint structure. +- **Recommendation:** Disable DEBUG or ensure permission classes are properly set regardless of DEBUG. + +### Exception Handler Exposes Stack Traces + +**Severity:** Medium + +- **File:** `backend/dvadmin/utils/exception.py:113` +- **Issue:** + ```python + elif isinstance(ex, Exception): + logger.exception(traceback.format_exc()) + msg = str(ex) + ``` +- **Impact:** In DEBUG mode, raw exception messages can reveal internal implementation details. +- **Recommendation:** Return generic error messages in production. Current implementation logs but still exposes `str(ex)`. + +## Code Quality Concerns + +### Debug Print Statements in Production Code + +**Severity:** Low + +- **Files:** Multiple files contain print statements: + - `backend/application/dispatch.py:87, 110` - "请先进行数据库迁移!" + - `backend/util/currency.py:41, 57, 73` - Raw SQL execution with no logging + - `backend/apps/pisadmin/miscprocurement/cost_template_builder.py:317-345` - Extensive debug output with Unicode symbols + - `backend/dvadmin/utils/core_initialize.py:36, 56, 60, 86` - Initialization status prints + - `backend/dvadmin/system/tests.py:21, 44, 51, 53` - Test debugging output + +- **Impact:** Print statements can interfere with JSON API responses if any are output during request handling. They also expose internal logic in logs. +- **Recommendation:** Replace print statements with proper logging calls using the configured logger. + +### TODO Comments Found in Code + +**Severity:** Low + +- **Files:** + - `backend/dvadmin/utils/filters.py:127` - "TODO Rename this here and in `filter_queryset`" + - `backend/dvadmin/utils/filters.py:322, 329` - "TODO: remove assertion in 2.1" + +- **Impact:** Incomplete refactoring work can lead to confusion or breaking changes if assumptions change. +- **Recommendation:** Address TODOs before major version releases or refactoring phases. + +### Bare Except Clauses + +**Severity:** Medium + +- **File:** `backend/application/websocketConfig.py:90` +- **Issue:** + ```python + except Exception: + pass + ``` +- **Impact:** Silently ignores all exceptions, making debugging impossible and masking failures. +- **Recommendation:** Log exceptions or handle them with specific exception types. + +## Performance & Scalability Concerns + +### In-Memory Channel Layer for WebSocket + +**Severity:** High + +- **File:** `backend/application/settings.py:187-190` +- **Issue:** + ```python + CHANNEL_LAYERS = { + "default": { + "BACKEND": "channels.layers.InMemoryChannelLayer" + } + } + ``` +- **Impact:** WebSocket connections work only within a single worker process. With multiple uvicorn workers (configured as 8 in CLAUDE.md), WebSocket functionality will fail unpredictably across workers. +- **Recommendation:** Use Redis channel layer for production: + ```python + CHANNEL_LAYERS = { + 'default': { + 'BACKEND': 'channels_redis.core.RedisChannelLayer', + 'CONFIG': { + "hosts": [('127.0.0.1', 6379)], + }, + }, + } + ``` + Note: The commented-out configuration exists at lines 192-199 but is not active. + +### Celery Running as Root + +**Severity:** Medium + +- **File:** `backend/application/celery.py:22` +- **Issue:** `platforms.C_FORCE_ROOT = True` +- **Impact:** Security risk running Celery worker processes as root user. +- **Recommendation:** Configure proper user/group for Celery workers in deployment. + +## Dependency Risks + +### Potentially Outdated Dependencies + +**Severity:** Medium + +- **File:** `backend/requirements.txt` +- **Observations:** + - Django 4.2.14 (current latest is 4.2.x) - Acceptable but check for security patches + - SimpleJWT 5.4.0 (5.5.0 available) - Minor version behind + - DRF 3.15.2 (3.15.4 available) - Minor version behind + - Channels 4.1.0 (4.2.x available) + - PyInstaller 6.9.0 - Major build tool dependency + +- **Recommendation:** Run `pip list --outdated` to identify packages needing updates, particularly security patches. + +## Operational Concerns + +### Hardcoded Infrastructure IPs + +**Severity:** High + +- **File:** `backend/conf/env.py` +- **Issue:** + ```python + DATABASE_HOST = '192.168.80.29' + REDIS_HOST = '177.10.0.15' + REDIS_PASSWORD = 'redis_5pGXn2' + ``` + These are actual production IPs and credentials in the codebase. +- **Impact:** If committed to version control, infrastructure credentials are exposed. Also prevents portability across environments. +- **Recommendation:** Use environment variables for all infrastructure configuration. Never commit real credentials. + +### No Docker Compose for Infrastructure + +**Severity:** Medium + +- **Observation:** No `docker-compose.yml` found in project root (only `backend/Dockerfile` and `web/Dockerfile`) +- **Impact:** No documented way to spin up the full stack (Django, Redis, database) locally. Makes local development and testing harder. +- **Recommendation:** Create `docker-compose.yml` that orchestrates backend, Redis, and database containers. + +### Log Directory Runtime Creation + +**Severity:** Low + +- **File:** `backend/application/settings.py:209-210` +- **Issue:** + ```python + if not os.path.exists(os.path.join(BASE_DIR, "logs")): + os.makedirs(os.path.join(BASE_DIR, "logs")) + ``` +- **Impact:** If the logs directory cannot be created (permissions issue), the application will crash on startup. +- **Recommendation:** Ensure logs directory exists before deployment via CI/CD or container entrypoint. + +## Known Issues Summary + +| Area | Severity | Issue | Files | +|------|----------|-------|-------| +| Secrets | Critical | Hardcoded SECRET_KEY | `application/settings.py:31` | +| Auth | Critical | MD5 password hashing | `backends.py`, `models.py`, `login.py` | +| Config | High | DEBUG=True in prod env | `conf/env.py:39` | +| Config | High | ALLOWED_HOSTS=* | `conf/env.py:48` | +| CORS | High | Allow all origins | `settings.py:179-181` | +| WebSocket | High | In-memory channel layer | `settings.py:189` | +| Secrets | High | Hardcoded IPs/passwords | `conf/env.py` | +| Code Quality | Medium | Bare except clauses | `websocketConfig.py:90` | +| Code Quality | Low | Debug print statements | Multiple files | +| Dependencies | Medium | Outdated packages | `requirements.txt` | + +--- + +*Concerns audit: 2026-04-01* diff --git a/.planning/codebase/CONVENTIONS.md b/.planning/codebase/CONVENTIONS.md new file mode 100644 index 0000000..667c2e6 --- /dev/null +++ b/.planning/codebase/CONVENTIONS.md @@ -0,0 +1,316 @@ +# Coding Conventions + +**Analysis Date:** 2026-04-01 + +## Naming Patterns + +### Backend (Python/Django) + +**Files:** +- Python source files: snake_case (e.g., `email_utils.py`, `system_no_rule.py`) +- URL routing: `urls.py` +- Serializers: `serializers.py` +- Models: `models.py` +- Views: either single `views.py` or organized in `views/` directory with one file per resource + +**Classes:** +- Models: PascalCase (e.g., `Company`, `Supplier`, `SystemNoRule`) +- ViewSets: PascalCase ending with `ViewSet` (e.g., `CompanyViewSet`, `InquiryViewSet`) +- Serializers: PascalCase ending with `Serializer` (e.g., `CompanySerializer`, `InquirySerializer`) +- Custom serializers for create/update: `ModelNameCreateUpdateSerializer` + +**Functions/Methods:** +- snake_case (e.g., `allocate_system_number`, `get_request_username`) +- Private methods: prefixed with underscore (e.g., `_allocate_next_code_for_locked_rule`) + +**Database Fields:** +- snake_case (e.g., `company_code`, `supplier_name`, `create_datetime`) +- Primary key: `id` +- Foreign keys: `model_name_id` or just `field_name` (e.g., `quotation_no`) +- Timestamps: `create_datetime`, `update_datetime` +- User tracking: `createuser`, `updateuser` (char fields, not FK) + +**URL Patterns:** +- Router.register uses snake_case plural (e.g., `r'units'`, `r'companies'`, `r'inquiry'`) +- Full endpoint example: `/api/pisadmin/basicinfo/companies/` + +### Frontend (Vue 3/TypeScript) + +**Files:** +- Vue components: kebab-case in directory (e.g., `company/index.vue`) +- API modules: `api.ts` in same directory (e.g., `company/api.ts`) +- CRUD configuration: `.tsx` extension (e.g., `company/crud.tsx`) +- Stores: `userInfo.ts`, `dictionary.ts` +- Utilities: `service.ts`, `storage.ts`, `message.ts` + +**Components:** +- Vue components use ` + + +``` + +**FastCRUD Configuration Pattern:** +```typescript +export const createCrudOptions = function ({ crudExpose }: Partial): CreateCrudOptionsRet { + return { + crudOptions: { + request: { + pageRequest: async (query) => api.GetList(query), + addRequest: async ({ form }) => api.AddObj(form), + editRequest: async ({ form, row }) => api.UpdateObj({ ...form, id: row.id }), + delRequest: async ({ row }) => api.DelObj(row.id) + }, + columns: { + company_code: { + title: '公司代码', + type: 'input', + search: { show: true }, + form: { rules: [{ required: true, message: '请输入公司代码' }] } + } + } + } + } +} +``` + +**API Module Pattern:** +```typescript +// /@/utils/service exports `request` +import { request } from '/@/utils/service' + +const apiPrefix = '/api/pisadmin/basicinfo/companies/' + +export const GetList = (params: any) => request({ url: apiPrefix, method: 'get', params }) +export const AddObj = (data: any) => request({ url: apiPrefix, method: 'post', data }) +export const UpdateObj = (data: any) => request({ url: apiPrefix + data.id + '/', method: 'put', data }) +export const DelObj = (id: string | number) => request({ url: apiPrefix + id + '/', method: 'delete' }) +``` + +**Pinia Store Pattern:** +```typescript +export const useUserInfo = defineStore('userInfo', { + state: (): UserInfosStates => ({ + userInfos: { /* initial state */ } + }), + actions: { + async setUserInfos() { /* action */ } + } +}) +``` + +## Linting and Formatting Configuration + +### Frontend (ESLint) + +**Config File:** `web/.eslintrc.js` + +**Parser:** `vue-eslint-parser` with `@typescript-eslint/parser` + +**Key Rules:** +- `@typescript-eslint/no-unused-vars`: `'off'` (disabled) +- `@typescript-eslint/no-explicit-any`: `'off'` (disabled) +- `no-console`: `'error'` (console.log is error, use ElMessage instead) +- Vue component rules mostly `'off'` for flexibility + +**Run Lint Fix:** +```bash +yarn lint-fix +``` + +### Frontend (Prettier) + +**Config File:** `web/.prettierrc.js` + +**Key Settings:** +```javascript +{ + printWidth: 150, + tabWidth: 2, + useTabs: false, + semi: true, + singleQuote: true, + trailingComma: 'es5', + bracketSpacing: true, + arrowParens: 'always', + endOfLine: 'lf' +} +``` + +### Backend (Python) + +No explicit Python linting configuration detected. Python code follows PEP 8 implicitly. + +## Error Handling + +### Backend + +- Use `try/except` blocks with specific exception handling +- Use `serializers.ValidationError` for API validation errors +- Use `ErrorResponse` from dvadmin for consistent error responses +- Log errors with `logger = logging.getLogger(__name__)` + +### Frontend + +- Use `ElMessage.error()` for user-facing error messages +- Use try/catch with async/await +- API errors handled in `service.ts` interceptors + +## Logging + +### Backend +```python +import logging +logger = logging.getLogger(__name__) + +logger.warning("message with %s", value) +logger.exception("error message") # includes traceback +``` + +### Frontend +- `console.log` allowed for debugging (stripped in production build) +- Production uses `ElMessage` for user feedback +- No structured logging library detected + +## Comments and Documentation + +### Backend +- Chinese docstrings for business logic (e.g., `"""公司信息-序列化器"""`) +- Complex methods have inline comments explaining logic +- Use `# pragma: no cover` for untested branches + +### Frontend +- Component `name` attribute for debugging +- Chinese comments for complex business logic +- JSDoc style not strictly enforced + +--- + +*Convention analysis: 2026-04-01* diff --git a/.planning/codebase/INTEGRATIONS.md b/.planning/codebase/INTEGRATIONS.md new file mode 100644 index 0000000..76532b2 --- /dev/null +++ b/.planning/codebase/INTEGRATIONS.md @@ -0,0 +1,295 @@ +# External Integrations + +**Analysis Date:** 2026-04-01 + +## Database Connection + +**Primary Database:** +- **Engine:** Microsoft SQL Server (via mssql-django 1.7) +- **Connection Driver:** ODBC Driver 18 for SQL Server +- **Database Name:** `pisdb` +- **Host:** `192.168.80.29` +- **Port:** `1433` +- **Credentials:** Configured in `backend/conf/env.py` +- **Configuration File:** `backend/conf/env.py` lines 14-24 + +**Connection Settings:** +```python +DATABASE_ENGINE = "mssql" +DATABASE_NAME = 'pisdb' +DATABASE_HOST = '192.168.80.29' +DATABASE_PORT = 1433 +DATABASE_USER = "test" +DATABASE_PASSWORD = '123456' +OPTIONS = { + "driver": "ODBC Driver 18 for SQL Server", + "extra_params": "Encrypt=yes;TrustServerCertificate=yes" +} +``` + +**Alternative Database Support:** +- MySQL 8.0 (via mysqlclient 2.2.0) +- PostgreSQL (via psycopg2 2.9.9) + +**Table Prefix:** `pis_` (Procurement Inquiry System) + +**Django Settings:** `backend/application/settings.py` lines 105-119 + +## Redis Configuration + +**Connection:** +- **Host:** `177.10.0.15` +- **Port:** `6379` +- **Password:** `redis_5pGXn2` +- **URL Format:** `redis://:redis_5pGXn2@177.10.0.15:6379` + +**Usage in Application:** +- Redis DB 1: General caching (`REDIS_DB`) +- Redis DB 3: Celery broker (`CELERY_BROKER_DB`) + +**Configuration File:** `backend/conf/env.py` lines 31-35 + +**Cache Backend Configuration:** +```python +REDIS_DB = 1 +CELERY_BROKER_DB = 3 +REDIS_PASSWORD = 'redis_5pGXn2' +REDIS_HOST = '177.10.0.15' +REDIS_URL = f'redis://:{REDIS_PASSWORD or ""}@{REDIS_HOST}:6379' +``` + +## Celery Task Queue + +**Broker:** Redis (same server as cache) + +**Configuration Source:** `backend/application/celery.py` + +**Celery Setup:** +- Auto-discovers tasks from all installed apps +- Uses Django settings namespace for configuration (`CELERY_*`) +- Supports tenant-aware mode if `django_tenants` is installed +- Runs with `C_FORCE_ROOT = True` for root execution + +**Retry Strategy:** +- Default retry delay: 180 seconds +- Maximum retries: 3 +- Decorator: `@retry_base_task_error()` + +**Task Result Storage:** Django Celery Results (`django_celery_results`) + +**Celery Beat Configuration:** +- Timezone: `Asia/Shanghai` +- Result backend stores task results in database + +**Django Settings Import:** `from django.conf:settings, namespace='CELERY'` + +**Installed Apps Integration:** +```python +app.autodiscover_tasks(lambda: settings.INSTALLED_APPS) +``` + +## WebSocket / Django Channels + +**ASGI Application:** `backend/application/asgi.py` + +**Protocol Support:** +- HTTP (via Django) +- WebSocket (via Channels) + +**WebSocket URL Pattern:** +- Route: `ws//` +- Consumer: `MegCenter` (from `application.websocketConfig`) +- Routing: `backend/application/ws_routing.py` + +**Channel Layers:** +- Default (development): `InMemoryChannelLayer` +- Production: `channels_redis.core.RedisChannelLayer` (commented out) + +**Authentication:** +- WebSocket connections validated via JWT token in `service_uid` parameter +- Token decoded using Django `SECRET_KEY` +- User ID extracted from decoded token +- Channel group format: `user_{user_id}` + +**WebSocket Consumer Classes:** +- `DvadminWebSocket` - Base WebSocket consumer with JWT auth +- `MegCenter` - Message center consumer extending base class + +**Message Flow:** +1. Client connects to `ws://host/ws//` +2. Token validated, user_id extracted +3. User added to channel group `user_{user_id}` +4. Messages pushed to user's channel group +5. `websocket_push()` function sends messages to specific users + +**File:** `backend/application/websocketConfig.py` lines 57-114 + +## Authentication & Authorization + +**JWT Authentication:** +- **Package:** djangorestframework-simplejwt 5.4.0 +- **Header Type:** `JWT` +- **Access Token Lifetime:** 1440 minutes (24 hours) +- **Refresh Token Lifetime:** 1 day +- **Rotate Refresh Tokens:** Enabled + +**JWT Configuration (settings.py lines 321-329):** +```python +SIMPLE_JWT = { + "ACCESS_TOKEN_LIFETIME": timedelta(minutes=1440), + "REFRESH_TOKEN_LIFETIME": timedelta(days=1), + "AUTH_HEADER_TYPES": ("JWT",), + "ROTATE_REFRESH_TOKENS": True, +} +``` + +**Authentication Backends:** +- `rest_framework_simplejwt.authentication.JWTAuthentication` +- `rest_framework.authentication.SessionAuthentication` + +**Custom Backend:** `dvadmin.utils.backends.CustomBackend` + +**REST Framework Default Authentication (settings.py lines 304-307):** +```python +"DEFAULT_AUTHENTICATION_CLASSES": ( + "rest_framework_simplejwt.authentication.JWTAuthentication", + "rest_framework.authentication.SessionAuthentication", +), +``` + +**Permission:** `rest_framework.permissions.IsAuthenticated` + +## Email Configuration + +**SMTP Configuration (backend/conf/env.py lines 53-68):** +```python +EMAIL_FROM = "eip@avc.co" +EMAIL_HOST_USER = "" +EMAIL_HOST_PASSWORD = "" +EMAIL_HOST = "mailflow.avc.co" +EMAIL_PORT = 25 +EMAIL_USE_SSL = False +EMAIL_USE_TLS = False +``` + +**Email Features:** +- Plain text connection (no SSL/TLS) +- System emails sent from `eip@avc.co` + +## Cloud Storage Integrations + +**Alibaba Cloud OSS:** +- Package: `oss2 2.19.1` +- Used for file storage + +**Tencent Cloud COS:** +- Package: `cos-python-sdk-v5 1.9.37` +- Used for file storage + +## API Documentation + +**Swagger/OpenAPI:** +- Package: `drf-yasg 1.21.7` +- Login URL: `/apiLogin/` +- Logout URL: `/rest_framework:logout/` +- Auto schema generation with custom schema class +- JSON editor enabled in Swagger UI + +**Configuration (settings.py lines 334-354):** +```python +SWAGGER_SETTINGS = { + "SECURITY_DEFINITIONS": {"basic": {"type": "basic"}}, + "LOGIN_URL": "apiLogin/", + "LOGOUT_URL": "rest_framework:logout", + "JSON_EDITOR": True, + "DEFAULT_AUTO_SCHEMA_CLASS": "dvadmin.utils.swagger.CustomSwaggerAutoSchema", +} +``` + +## CORS Configuration + +**CORS Settings (settings.py lines 177-181):** +```python +CORS_ORIGIN_ALLOW_ALL = True +CORS_ALLOW_CREDENTIALS = True +``` + +**CORS Middleware:** `corsheaders.middleware.CorsMiddleware` + +## Static Files & Media + +**Static Files:** +- URL: `/static/` +- Storage: Whitenoise with compression +- Storage Class: `whitenoise.storage.CompressedStaticFilesStorage` + +**Media Files:** +- URL: `/media/` +- Root: `media` directory in project + +**Configuration (settings.py lines 156-169):** +```python +STATIC_URL = "/static/" +STATICFILES_DIRS = [os.path.join(BASE_DIR, "static")] +MEDIA_ROOT = "media" +MEDIA_URL = "/media/" +STATICFILES_FINDERS = ( + "django.contrib.staticfiles.finders.FileSystemFinder", + "django.contrib.staticfiles.finders.AppDirectoriesFinder" +) +``` + +## Proxy Configuration (Development) + +**Vite Dev Server Proxy (vite.config.ts lines 37-44):** +```typescript +proxy: { + '/gitee': { + target: 'https://gitee.com', + ws: true, + changeOrigin: true, + rewrite: (path) => path.replace(/^\/gitee/, ''), + }, +} +``` + +## Environment Variables + +**Configuration Source:** `backend/conf/env.py` + +**Key Environment Variables:** +| Variable | Purpose | +|----------|---------| +| `DATABASE_ENGINE` | Database backend type | +| `DATABASE_HOST` | Database server host | +| `DATABASE_PORT` | Database server port | +| `DATABASE_USER` | Database username | +| `DATABASE_PASSWORD` | Database password | +| `REDIS_HOST` | Redis server host | +| `REDIS_PASSWORD` | Redis password | +| `DEBUG` | Debug mode flag | +| `EMAIL_HOST` | SMTP server | +| `EMAIL_PORT` | SMTP port | + +**Note:** Actual credentials are stored in `backend/conf/env.py` (not committed to version control per best practices). + +## Key Dependencies Between Systems + +``` +Frontend (Vue 3) + | + | HTTP/REST + WebSocket + v +Backend (Django + DRF) + | + +---> Database (SQL Server/MySQL/PostgreSQL) + +---> Cache (Redis) + +---> Task Queue (Celery + Redis) + +---> WebSocket (Channels + Redis) + +---> Cloud Storage (OSS/COS) + +---> Email (SMTP) +``` + +--- + +*Integration audit: 2026-04-01* diff --git a/.planning/codebase/STACK.md b/.planning/codebase/STACK.md new file mode 100644 index 0000000..60a2721 --- /dev/null +++ b/.planning/codebase/STACK.md @@ -0,0 +1,227 @@ +# Technology Stack + +**Analysis Date:** 2026-04-01 + +## Languages + +**Primary:** +- Python 3.x - Backend development +- TypeScript 4.9.4 - Frontend development +- JavaScript (ESNext) - Frontend runtime + +**Secondary:** +- Vue 3.4.38 - Frontend UI framework +- HTML/CSS - Frontend markup and styling + +## Backend Framework + +**Django Core:** +- Django 4.2.14 - Web framework +- Django REST Framework 3.15.2 - REST API development +- channels 4.1.0 - WebSocket support +- Celery 5.x (via dvadmin3-celery 3.1.6) - Async task queue + +**Authentication:** +- djangorestframework-simplejwt 5.4.0 - JWT token authentication +- django-simple-captcha 0.6.0 - CAPTCHA verification + +**Database:** +- mssql-django 1.7 - SQL Server driver (configured for pisdb) +- mysqlclient 2.2.0 - MySQL driver (also available) +- psycopg2 2.9.9 - PostgreSQL driver + +**Key Django Plugins:** +- django-cors-headers 4.4.0 - Cross-origin resource sharing +- django-filter 24.2 - Database query filtering +- django-ranged-response 0.2.0 - HTTP range response support +- django-restql 0.15.4 - GraphQL-like query language for REST +- drf-yasg 1.21.7 - Swagger/OpenAPI documentation +- django-timezone-field 7.0 - Timezone support +- django-comment-migrate 0.1.7 - Comment migration utility +- Pillow 10.4.0 - Image processing +- openpyxl 3.1.5 - Excel file handling +- requests 2.32.4 - HTTP client +- pypinyin 0.51.0 - Chinese pinyin conversion +- ua-parser 0.18.0 - User agent parsing +- user-agents 2.2.0 - User agent detection +- six 1.16.0 - Python 2/3 compatibility +- whitenoise 6.7.0 - Static file serving +- pyparsing 3.1.2 - Parsing expression grammar +- typing-extensions 4.12.2 - Type hint extensions +- tzlocal 5.2 - Timezone localization + +**Server:** +- uvicorn 0.30.3 - ASGI server +- gunicorn 23.0.0 - WSGI server +- gevent 24.2.1 - Async networking library + +**Cloud Storage:** +- oss2 2.19.1 - Alibaba Cloud OSS integration +- cos-python-sdk-v5 1.9.37 - Tencent Cloud COS integration + +## Frontend Framework + +**Core:** +- Vue 3.4.38 - Progressive JavaScript framework +- TypeScript 4.9.4 - Typed superset of JavaScript +- Vite 5.4.1 - Next-generation frontend build tool +- Pinia 2.0.28 - State management +- Vue Router 4.4.3 - SPA routing +- Vue I18n 9.14.0 - Internationalization + +**UI Component Libraries:** +- Element Plus 2.8.0 - UI component library +- @element-plus/icons-vue 2.3.1 - Element Plus icons +- Vant 4.9.19 - Mobile UI component library +- vant4-kit 1.0.3 - Vant utility kit + +**FastCRUD:** +- @fast-crud/fast-crud 1.21.2 - Quick CRUD development +- @fast-crud/fast-extends 1.21.2 - FastCRUD extensions +- @fast-crud/ui-element 1.21.2 - Element Plus UI for FastCRUD +- @fast-crud/ui-interface 1.21.2 - FastCRUD UI interface + +**Data Visualization:** +- echarts 5.5.1 - Interactive charting library +- echarts-gl 2.0.9 - ECharts 3D visualization +- echarts-wordcloud 2.1.0 - Word cloud for ECharts +- vxe-table 4.6.18 - Grid/table component +- xe-utils 3.5.30 - Utility functions for vxe-table + +**Build & Development:** +- @vitejs/plugin-vue 5.1.2 - Vue plugin for Vite +- @vitejs/plugin-vue-jsx 4.0.1 - Vue JSX support +- vite-plugin-vue-setup-extend 0.4.0 - Vue setup syntax extension +- sass 1.56.2 - CSS preprocessor +- less 4.3.0 - CSS preprocessor +- autoprefixer 10.4.20 - CSS vendor prefixing +- postcss 8.4.21 - CSS transformation tool +- tailwindcss 3.2.7 - Utility-first CSS framework +- rollup 4.60.1 - Module bundler + +**Utilities:** +- axios 1.7.4 - HTTP client +- qs 6.11.0 - Query string parsing +- lodash-es 4.17.21 - Utility library +- js-cookie 3.0.5 - Cookie handling +- mitt 3.0.1 - Tiny event emitter +- nprogress 0.2.0 - Progress bar +- sortablejs 1.15.0 - Drag and drop sorting +- vue-draggable-plus 0.6.0 - Vue draggable component +- screenfull 6.0.2 - Fullscreen API wrapper +- print-js 1.6.0 - Print functionality +- qrcodejs2-fixes 0.0.2 - QR code generation +- cropperjs 1.6.2 - Image cropping +- vue-cropper 1.0.8 - Vue image cropper +- countup.js 2.8.0 - Animated number counting +- e-icon-picker 2.1.1 - Icon picker +- element-tree-line 0.2.1 - Tree line for Element +- font-awesome 4.7.0 - Icon library +- @iconify/vue 4.1.2 - Icon library +- vue-clipboard3 2.0.0 - Clipboard API wrapper +- ts-md5 1.3.1 - MD5 hashing +- vue-grid-layout 3.0.0-beta1 - Grid layout +- vue-qr 4.0.9 - QR code for Vue +- jsplumb 2.15.6 - Visual connectivity +- @wangeditor/editor 5.1.23 - Rich text editor +- @wangeditor/editor-for-vue 5.1.12 - Vue wrapper for WangEditor +- js-table2excel 1.1.2 - Table to Excel export +- date-holidays 3.24.1 - Holiday data +- lunar-javascript 1.7.1 - Chinese lunar calendar +- upgrade 1.1.0 - Upgrade utility +- @great-dream/dvadmin3-celery-web 3.1.3 - Celery web UI + +**Development Tools:** +- eslint 8.57.1 - Linting +- eslint-plugin-vue 9.27.0 - Vue ESLint plugin +- @typescript-eslint/parser 5.46.0 - TypeScript ESLint parser +- @typescript-eslint/eslint-plugin 5.46.0 - TypeScript ESLint plugin +- prettier 2.8.1 - Code formatting +- vue-eslint-parser 9.4.3 - Vue template parser +- @types/node 18.19.42 - Node.js type definitions +- @types/nprogress 0.2.3 - NProgress type definitions +- @types/sortablejs 1.15.8 - SortableJS type definitions +- @types/lodash 4.17.7 - Lodash type definitions +- baseline-browser-mapping 2.9.19 - Browser capability mapping + +## Infrastructure + +**Database:** +- MySQL 8.0 - Primary relational database (configured) +- SQL Server (via ODBC Driver 18) - Alternative database option + +**Cache & Queue:** +- Redis - Caching, session storage, Celery broker +- Celery - Async task processing + +**Container:** +- Docker - Containerization +- Docker Compose - Multi-container orchestration + +## Configuration Files + +**Backend:** +- `backend/conf/env.py` - Environment configuration (DB, Redis, Email) +- `backend/application/settings.py` - Django settings (imports from conf/env.py) +- `backend/application/celery.py` - Celery configuration +- `backend/application/asgi.py` - ASGI configuration (WebSocket support) +- `backend/application/ws_routing.py` - WebSocket URL routing +- `backend/application/websocketConfig.py` - WebSocket consumer implementation + +**Frontend:** +- `web/package.json` - Node.js dependencies and scripts +- `web/vite.config.ts` - Vite build configuration +- `web/tsconfig.json` - TypeScript compiler options + +## Project Structure + +``` +pis/ +├── backend/ +│ ├── application/ # Django project configuration +│ │ ├── settings.py # Main Django settings +│ │ ├── urls.py # Root URL configuration +│ │ ├── asgi.py # ASGI application (HTTP + WebSocket) +│ │ ├── celery.py # Celery task configuration +│ │ ├── ws_routing.py # WebSocket routing +│ │ └── websocketConfig.py # WebSocket consumers +│ ├── apps/ +│ │ ├── pisadmin/ # Custom procurement app +│ │ │ ├── basicinfo/ # Company, currency, supplier, unit +│ │ │ └── miscprocurement/ # RFQ, price templates, cost sections +│ │ └── pissupplier/ # Supplier quotation module +│ ├── dvadmin/ # Core RBAC system (dvadmin3) +│ │ └── system/ # Users, roles, menus, departments +│ └── conf/ +│ └── env.py # Environment configuration +├── web/ # Vue 3 frontend +│ ├── src/ +│ │ ├── views/ # Page components +│ │ ├── api/ # API clients +│ │ ├── stores/ # Pinia stores +│ │ └── router/ # Vue Router config +│ ├── vite.config.ts # Vite configuration +│ └── tsconfig.json # TypeScript config +└── docker-compose.yml # Docker deployment +``` + +## Build Commands + +**Backend:** +```bash +python manage.py makemigrations # Create migrations +python manage.py migrate # Apply migrations +python manage.py runserver # Development server +uvicorn application.asgi:application --workers 8 # Production +``` + +**Frontend:** +```bash +yarn dev # Development server (port 8080) +yarn build # Production build +yarn lint-fix # Lint and auto-fix +``` + +--- + +*Stack analysis: 2026-04-01* diff --git a/.planning/codebase/STRUCTURE.md b/.planning/codebase/STRUCTURE.md new file mode 100644 index 0000000..8072c21 --- /dev/null +++ b/.planning/codebase/STRUCTURE.md @@ -0,0 +1,247 @@ +# Codebase Structure + +**Analysis Date:** 2026-04-01 + +## Directory Layout + +``` +pis/ +├── backend/ +│ ├── application/ # Django project config (settings, urls, asgi) +│ ├── apps/ +│ │ ├── pisadmin/ # Custom procurement app +│ │ │ ├── basicinfo/ # Company, currency, supplier, unit, system no +│ │ │ └── miscprocurement/ # RFQ, price templates, cost sections +│ │ └── pissupplier/ # Supplier quotation module +│ ├── conf/ +│ │ └── env.py # Environment config (DB, Redis, Email) +│ ├── dvadmin/ # Base RBAC system +│ │ ├── system/ # Users, roles, menus, departments +│ │ └── utils/ # CoreModel, CustomViewSet, filters, etc. +│ ├── plugins/ # Plugin modules (celery, etc.) +│ ├── static/ # Static files +│ ├── templates/ # Django templates +│ └── util/ # Utility scripts +├── web/ # Vue 3 frontend +│ └── src/ +│ ├── api/ # API client modules +│ ├── components/ # Reusable Vue components +│ ├── layout/ # App layout (header, sidebar, nav) +│ ├── router/ # Vue Router config +│ ├── stores/ # Pinia state stores +│ ├── utils/ # Helper functions +│ └── views/ # Page components +├── docker-compose.yml # Container deployment (not in repo root) +├── Makefile # Build scripts +└── scripts/ # Utility scripts +``` + +## Backend Directory Purposes + +**`backend/application/`:** +- Purpose: Django project configuration entry point +- Key files: + - `settings.py`: Main Django settings (imports from `conf/env.py`) + - `urls.py`: Root URL routing (includes all app URLs) + - `asgi.py`: ASGI config for HTTP + WebSocket + - `ws_routing.py`: WebSocket URL patterns + - `celery.py`: Celery task configuration + +**`backend/conf/`:** +- Purpose: Environment-specific configuration +- Key file: `env.py` - Contains DATABASE_*, REDIS_*, EMAIL_* settings +- This file contains credentials - DO NOT commit to version control + +**`backend/apps/pisadmin/basicinfo/`:** +- Purpose: Master data for procurement (companies, currencies, suppliers, units) +- Structure: + - `models.py`: Company, Currency, Supplier, SupplierUser, Unit, SystemNoRule, EmailNotice + - `views/`: company.py, currency.py, supplier.py, unit.py, system_no_rule.py, email_template.py, email_utils.py + - `urls.py`: Routes to views + - `admin.py`: Django admin registration + +**`backend/apps/pisadmin/miscprocurement/`:** +- Purpose: RFQ (Request for Quotation) and cost template management +- Structure: + - `models.py`: Inquiry, InquirySupplier, InquiryAttachment, InquiryMaterialCost, InquiryProcessCost, InquiryOtherCost, InquiryProfitCost, InquiryRfqItem, CostEstimateTemplateHead, CostEstimateTemplateBody, MiscLowPriceHeader, MiscLowPriceDetail, MiscNegotiationRecords, RFQOperationLogs + - `views.py`: ViewSets for all models (1500+ lines with complex workflow logic) + - `serializers.py`: DRF serializers + - `urls.py`: Routes to views + - `cost_template_builder.py`: Template construction utility + +**`backend/apps/pissupplier/`:** +- Purpose: Supplier quotation management (separate from pisadmin for supplier-facing access) +- Structure: + - `models.py`: QuotationMaster, QuotationMaterial, QuotationProcess, QuotationOther, QuotationProfit, QuotationItem, QuotationAttachment + - `views.py`: Quotation ViewSets + - `serializers.py`: DRF serializers + - `urls.py`: Routes to views + +**`backend/dvadmin/system/`:** +- Purpose: Base RBAC system (users, roles, menus, departments, logs) +- Structure: + - `models.py`: Users, Role, Menu, MenuButton, MenuField, FieldPermission, Dept, Post, OperationLog, LoginLog, SystemConfig, Dictionary, FileList, Area, ApiWhiteList, MessageCenter + - `views/`: ViewSets for each model + - `urls.py`: Routes to views + - `fixtures/`: Initial data fixtures + - `management/commands/`: Django commands (init, init_area, check_dept_users) + +**`backend/dvadmin/utils/`:** +- Purpose: Reusable utilities for all apps +- Key files: + - `viewset.py`: CustomModelViewSet base class with standardized responses + - `models.py`: CoreModel, SoftDeleteModel base classes + - `filters.py`: DataLevelPermissionMargeFilter, CoreModelFilterBankend + - `permission.py`: CustomPermission class + - `pagination.py`: CustomPagination class + - `serializers.py`: Common serializer utilities + - `exception.py`: Custom exception handler + - `json_response.py`: SuccessResponse, DetailResponse, ErrorResponse wrappers + +## Frontend Directory Purposes + +**`web/src/api/`:** +- Purpose: API client modules +- Structure: + - `login/index.ts`: Login/auth API + - `menu/index.ts`: Menu/permission API + - `miscprocurement/priceTemplate.ts`: Cost template API + - `procurement/priceTemplate.ts`: (duplicate or alternative) + +**`web/src/components/`:** +- Purpose: Reusable Vue components +- Key components: + - `table/index.vue`: Custom table component with FastCRUD + - `auth/auth*.vue`: Permission directive components + - `fileSelector/`: File upload component + - `foreignKey/`: Foreign key selector + - `importExcel/`: Excel import component + +**`web/src/layout/`:** +- Purpose: Application layout structure +- Subdirectories: + - `component/`: aside.vue, header.vue, main.vue + - `navBars/`: breadcrumb, tagsView, user menu + - `navMenu/`: vertical and horizontal menu components + - `main/`: classic, columns, defaults, transverse layout variants + - `routerView/`: iframes, link, parent route views + +**`web/src/router/`:** +- Purpose: Vue Router configuration +- Key files: + - `index.ts`: Router creation, beforeEach guards, token validation + - `route.ts`: Static and dynamic route definitions + - `frontEnd.ts`: Frontend-controlled routing logic + - `backEnd.ts`: Backend-controlled routing logic + +**`web/src/stores/`:** +- Purpose: Pinia state management +- Key stores: + - `userInfo.ts`: User authentication state + - `frontendMenu.ts`: Menu tree cache + - `themeConfig.ts`: UI theme settings + - `tagsViewRoutes.ts`: Open tab tracking + - `routesList.ts`: Available routes + - `permission.ts`: Permission state + +**`web/src/views/`:** +- Purpose: Page-level Vue components +- Structure mirrors backend apps: + - `pisadmin/basicinfo/`: company, currency, supplier, unit, systemno, emailnotice + - `pisadmin/miscprocurement/`: cost_template, misc_materials, misc_parts, misc_stations + - `system/`: admin pages (dept, menu, role, user, dictionary, config, etc.) + - `pissupplier/` (if exists): Supplier-facing pages + +## Key Configuration File Locations + +**Backend Configuration:** +- `backend/application/settings.py` - Main Django settings +- `backend/conf/env.py` - Environment variables (DB, Redis, Email) +- `backend/application/urls.py` - URL routing +- `backend/application/asgi.py` - ASGI/WebSocket config + +**Frontend Configuration:** +- `web/src/settings.ts` - Frontend settings (API base URLs, app info) +- `web/src/main.ts` - Vue app initialization +- `web/src/App.vue` - Root Vue component +- `web/vite.config.ts` - Vite build configuration + +**Environment/Secrets (DO NOT COMMIT):** +- `backend/conf/env.py` - Database, Redis, Email credentials +- `.env` files (if present) + +## Custom Apps vs Base dvadmin Structure + +**Base dvadmin (`dvadmin/`):** +- Provided by django-vue3-admin template +- Contains generic functionality: user management, role management, menu management, RBAC +- Should NOT be modified for custom business logic +- Extended by custom apps for domain-specific features + +**Custom Apps (`apps/pisadmin/`, `apps/pissupplier/`):** +- `pisadmin`: Internal procurement admin (RFQ, cost templates, supplier master, etc.) +- `pissupplier`: Supplier portal (quotations) +- Custom apps import and extend dvadmin's `CustomModelViewSet`, `CoreModel`, `CustomPermission` +- Custom apps define their own models, serializers, views, urls + +**Extension Pattern:** +```python +from dvadmin.utils.viewset import CustomModelViewSet + +class InquiryViewSet(CustomModelViewSet): + queryset = Inquiry.objects.all() + serializer_class = InquirySerializer + # Inherits: create, list, retrieve, update, destroy, multiple_delete, get_by_ids + # Custom actions added via @action decorator +``` + +## Docker/Container Structure + +The project uses Docker for deployment (docker-compose not present in current branch): + +**Backend Container:** +- Based on Python 3.x +- Runs Django via uvicorn (ASGI) +- Port: 8000 +- Depends on: MySQL, Redis + +**Frontend Container:** +- Based on Node.js for build +- nginx for serving built SPA +- Port: 8080 (dev), 80 (prod) + +**External Dependencies:** +- MySQL 8.0: Database (configured via `conf/env.py`) +- Redis: Caching and Celery broker +- SMTP: Email notifications + +## Where to Add New Code + +**New Model (Backend):** +1. Add to appropriate app's `models.py` +2. Inherit from `CoreModel` +3. Create serializer in `serializers.py` +4. Create ViewSet extending `CustomModelViewSet` +5. Add URL route in app's `urls.py` +6. Run migrations: `python manage.py makemigrations apps..` +7. Apply: `python manage.py migrate` + +**New API Endpoint (Backend):** +1. If CRUD: Add ViewSet method or use default actions +2. If custom: Add `@action` method to ViewSet +3. Register route in app `urls.py` with `router.register()` + +**New Page (Frontend):** +1. Create Vue component in `web/src/views///` +2. Add API client in `web/src/api//` +3. Menu added via backend `Menu` model (not frontend route config) +4. Component uses FastCRUD pattern with custom table and form components + +**New Store (Frontend):** +1. Create in `web/src/stores/modules/` or root stores +2. Use Pinia `defineStore` +3. Persist sensitive data to Session storage + +--- + +*Structure analysis: 2026-04-01* diff --git a/.planning/codebase/TESTING.md b/.planning/codebase/TESTING.md new file mode 100644 index 0000000..1300ff4 --- /dev/null +++ b/.planning/codebase/TESTING.md @@ -0,0 +1,238 @@ +# Testing Patterns + +**Analysis Date:** 2026-04-01 + +## Test Framework + +### Backend (Python/Django) + +**No dedicated test framework detected.** + +- No `tests.py` files in Django apps +- No `pytest` configuration +- No `unittest` test suites +- Migrations are generated via `python manage.py makemigrations` + +### Frontend (Vue 3/TypeScript) + +**No test framework detected.** + +- No Jest, Vitest, or other test runner configuration +- No `.test.ts`, `.spec.ts` files in `web/src/` +- Build and lint commands only, no test commands in `package.json` + +## Test File Organization + +### Backend Structure + +``` +backend/ +├── application/ # Django settings +├── apps/ +│ ├── pisadmin/ # Custom procurement app +│ │ ├── basicinfo/ +│ │ │ ├── models.py +│ │ │ ├── views/ +│ │ │ ├── urls.py +│ │ │ └── migrations/ +│ │ └── miscprocurement/ +│ │ ├── models.py +│ │ ├── serializers.py +│ │ ├── views.py +│ │ └── migrations/ +│ └── pissupplier/ +└── dvadmin/ # Core RBAC system +``` + +**No `tests.py` directories or test files in application code.** + +### Frontend Structure + +``` +web/ +├── src/ +│ ├── api/ +│ ├── assets/ +│ ├── components/ +│ ├── directive/ +│ ├── layout/ +│ ├── plugin/ +│ ├── router/ +│ ├── stores/ +│ ├── theme/ +│ ├── types/ +│ ├── utils/ +│ ├── views/ # Page components +│ │ ├── pisadmin/ +│ │ │ ├── basicinfo/ +│ │ │ │ ├── company/ +│ │ │ │ │ ├── index.vue +│ │ │ │ ├── api.ts +│ │ │ │ └── crud.tsx +│ │ │ └── miscprocurement/ +│ │ │ ├── rfqmiscellaneous/ +│ │ │ └── cost_template/ +│ │ ├── pissupplier/ +│ │ └── system/ +│ ├── App.vue +│ └── main.ts +└── package.json +``` + +**No test files (`*.test.ts`, `*.spec.ts`) in `web/src/` directory.** + +## Available Scripts + +### Frontend (`web/package.json`) + +```json +{ + "scripts": { + "dev": "vite --force", + "build:dev": "vite build --mode development", + "build": "vite build", + "build:local": "vite build --mode local_prod", + "lint-fix": "eslint --fix --ext .js --ext .jsx --ext .vue src/", + "build:flowH5": "vite build --config flowH5.config.ts" + } +} +``` + +**No test-related scripts.** + +### Backend + +```bash +# Migrations +python manage.py makemigrations apps.pisadmin.apps.pisadmin +python manage.py makemigrations apps.pissupplier +python manage.py migrate + +# Development server +python manage.py runserver 0.0.0.0:8000 + +# Production-like +uvicorn application.asgi:application --port 8000 --host 0.0.0.0 --workers 8 +``` + +**No test commands.** + +## CI/CD Pipeline + +**No CI/CD configuration detected.** + +- No GitHub Actions workflows (`.github/workflows/`) +- No `.gitlab-ci.yml` +- No Jenkinsfile +- Docker Compose present but no automated testing in Dockerfile + +## Coverage + +**No coverage tools configured.** + +- No `coverage.py` configuration +- No `pytest-cov` or similar +- No Istanbul/nyc for frontend + +## Manual Testing Approach + +Based on project structure, manual testing likely involves: + +### Backend API Testing + +1. **Django Admin Interface** - Models registered in `admin.py` provide basic CRUD +2. **DRF Browsable API** - Available at `/api/` endpoints for interactive testing +3. **Direct HTTP requests** - Using curl or Postman + +### Frontend Testing + +1. **Browser testing** - Manual UI verification via `yarn dev` +2. **FastCRUD configuration** - CRUD options in `crud.tsx` files control behavior +3. **Vue DevTools** - For state inspection + +## What Could Be Tested + +### Backend + +- **ViewSet endpoints**: Each ViewSet handles CRUD + custom actions +- **Serializer validation**: Input/output transformation +- **Model methods**: Business logic like `SystemNoRule.allocate_system_number()` +- **Transaction integrity**: Database operations wrapped in `transaction.atomic()` +- **Status workflow**: Inquiry state machine (open -> confirmed -> published -> quoting -> quote_ended -> bargaining -> negotiated -> approved -> awarded) + +### Frontend + +- **FastCRUD options**: Configuration in `createCrudOptions()` +- **API integration**: Request/response handling +- **Store actions**: Pinia store mutations +- **Router guards**: Authentication and authorization checks + +## Recommendations for Testing Setup + +### Backend + +Add `pytest` with `pytest-django`: + +``` +# requirements.txt additions +pytest==7.4.0 +pytest-django==4.5.0 +pytest-cov==4.1.0 +``` + +Create test files: +``` +backend/apps/pisadmin/basicinfo/tests.py +backend/apps/pisadmin/miscprocurement/tests.py +``` + +### Frontend + +Add Vitest for testing: + +```bash +yarn add -D vitest @vue/test-utils jsdom +``` + +Add test script: +```json +{ + "scripts": { + "test": "vitest", + "coverage": "vitest run --coverage" + } +} +``` + +Create test files: +``` +web/src/views/pisadmin/basicinfo/company/__tests__/Company.spec.ts +``` + +## Build and Deployment + +### Build Commands + +**Frontend:** +```bash +cd web +yarn install +yarn build # Production build +yarn build:dev # Development build +``` + +**Backend:** +```bash +cd backend +pip install -r requirements.txt +python manage.py migrate +python manage.py init # Create superuser +``` + +### Docker + +The project uses Docker for deployment but no test containers. + +--- + +*Testing analysis: 2026-04-01*