Compare commits

..

2 Commits

Author SHA1 Message Date
xuqiuyun
73051df002 docs: 添加OpenSpec规范文档和项目说明文件
添加AGENTS.md、project.md及相关命令文档,定义项目规范和OpenSpec工作流程
2025-11-10 14:47:55 +08:00
xuqiuyun
535ccc8e9a 更新项目文件 2025-11-07 14:56:59 +08:00
8 changed files with 861 additions and 1 deletions

View File

@@ -0,0 +1,23 @@
---
name: /openspec-apply
id: openspec-apply
category: OpenSpec
description: Implement an approved OpenSpec change and keep tasks in sync.
---
<!-- OPENSPEC:START -->
**Guardrails**
- Favor straightforward, minimal implementations first and add complexity only when it is requested or clearly required.
- Keep changes tightly scoped to the requested outcome.
- Refer to `openspec/AGENTS.md` (located inside the `openspec/` directory—run `ls openspec` or `openspec update` if you don't see it) if you need additional OpenSpec conventions or clarifications.
**Steps**
Track these steps as TODOs and complete them one by one.
1. Read `changes/<id>/proposal.md`, `design.md` (if present), and `tasks.md` to confirm scope and acceptance criteria.
2. Work through tasks sequentially, keeping edits minimal and focused on the requested change.
3. Confirm completion before updating statuses—make sure every item in `tasks.md` is finished.
4. Update the checklist after all work is done so each task is marked `- [x]` and reflects reality.
5. Reference `openspec list` or `openspec show <item>` when additional context is required.
**Reference**
- Use `openspec show <id> --json --deltas-only` if you need additional context from the proposal while implementing.
<!-- OPENSPEC:END -->

View File

@@ -0,0 +1,27 @@
---
name: /openspec-archive
id: openspec-archive
category: OpenSpec
description: Archive a deployed OpenSpec change and update specs.
---
<!-- OPENSPEC:START -->
**Guardrails**
- Favor straightforward, minimal implementations first and add complexity only when it is requested or clearly required.
- Keep changes tightly scoped to the requested outcome.
- Refer to `openspec/AGENTS.md` (located inside the `openspec/` directory—run `ls openspec` or `openspec update` if you don't see it) if you need additional OpenSpec conventions or clarifications.
**Steps**
1. Determine the change ID to archive:
- If this prompt already includes a specific change ID (for example inside a `<ChangeId>` block populated by slash-command arguments), use that value after trimming whitespace.
- If the conversation references a change loosely (for example by title or summary), run `openspec list` to surface likely IDs, share the relevant candidates, and confirm which one the user intends.
- Otherwise, review the conversation, run `openspec list`, and ask the user which change to archive; wait for a confirmed change ID before proceeding.
- If you still cannot identify a single change ID, stop and tell the user you cannot archive anything yet.
2. Validate the change ID by running `openspec list` (or `openspec show <id>`) and stop if the change is missing, already archived, or otherwise not ready to archive.
3. Run `openspec archive <id> --yes` so the CLI moves the change and applies spec updates without prompts (use `--skip-specs` only for tooling-only work).
4. Review the command output to confirm the target specs were updated and the change landed in `changes/archive/`.
5. Validate with `openspec validate --strict` and inspect with `openspec show <id>` if anything looks off.
**Reference**
- Use `openspec list` to confirm change IDs before archiving.
- Inspect refreshed specs with `openspec list --specs` and address any validation issues before handing off.
<!-- OPENSPEC:END -->

View File

@@ -0,0 +1,27 @@
---
name: /openspec-proposal
id: openspec-proposal
category: OpenSpec
description: Scaffold a new OpenSpec change and validate strictly.
---
<!-- OPENSPEC:START -->
**Guardrails**
- Favor straightforward, minimal implementations first and add complexity only when it is requested or clearly required.
- Keep changes tightly scoped to the requested outcome.
- Refer to `openspec/AGENTS.md` (located inside the `openspec/` directory—run `ls openspec` or `openspec update` if you don't see it) if you need additional OpenSpec conventions or clarifications.
- Identify any vague or ambiguous details and ask the necessary follow-up questions before editing files.
**Steps**
1. Review `openspec/project.md`, run `openspec list` and `openspec list --specs`, and inspect related code or docs (e.g., via `rg`/`ls`) to ground the proposal in current behaviour; note any gaps that require clarification.
2. Choose a unique verb-led `change-id` and scaffold `proposal.md`, `tasks.md`, and `design.md` (when needed) under `openspec/changes/<id>/`.
3. Map the change into concrete capabilities or requirements, breaking multi-scope efforts into distinct spec deltas with clear relationships and sequencing.
4. Capture architectural reasoning in `design.md` when the solution spans multiple systems, introduces new patterns, or demands trade-off discussion before committing to specs.
5. Draft spec deltas in `changes/<id>/specs/<capability>/spec.md` (one folder per capability) using `## ADDED|MODIFIED|REMOVED Requirements` with at least one `#### Scenario:` per requirement and cross-reference related capabilities when relevant.
6. Draft `tasks.md` as an ordered list of small, verifiable work items that deliver user-visible progress, include validation (tests, tooling), and highlight dependencies or parallelizable work.
7. Validate with `openspec validate <id> --strict` and resolve every issue before sharing the proposal.
**Reference**
- Use `openspec show <id> --json --deltas-only` or `openspec show <spec> --type spec` to inspect details when validation fails.
- Search existing requirements with `rg -n "Requirement:|Scenario:" openspec/specs` before writing new ones.
- Explore the codebase with `rg <keyword>`, `ls`, or direct file reads so proposals align with current implementation realities.
<!-- OPENSPEC:END -->

18
AGENTS.md Normal file
View File

@@ -0,0 +1,18 @@
<!-- OPENSPEC:START -->
# OpenSpec Instructions
These instructions are for AI assistants working in this project.
Always open `@/openspec/AGENTS.md` when the request:
- Mentions planning or proposals (words like proposal, spec, change, plan)
- Introduces new capabilities, breaking changes, architecture shifts, or big performance/security work
- Sounds ambiguous and you need the authoritative spec before coding
Use `@/openspec/AGENTS.md` to learn:
- How to create and apply change proposals
- Spec format and conventions
- Project structure and guidelines
Keep this managed block so 'openspec update' can refresh the instructions.
<!-- OPENSPEC:END -->

456
openspec/AGENTS.md Normal file
View File

@@ -0,0 +1,456 @@
# OpenSpec Instructions
Instructions for AI coding assistants using OpenSpec for spec-driven development.
## TL;DR Quick Checklist
- Search existing work: `openspec spec list --long`, `openspec list` (use `rg` only for full-text search)
- Decide scope: new capability vs modify existing capability
- Pick a unique `change-id`: kebab-case, verb-led (`add-`, `update-`, `remove-`, `refactor-`)
- Scaffold: `proposal.md`, `tasks.md`, `design.md` (only if needed), and delta specs per affected capability
- Write deltas: use `## ADDED|MODIFIED|REMOVED|RENAMED Requirements`; include at least one `#### Scenario:` per requirement
- Validate: `openspec validate [change-id] --strict` and fix issues
- Request approval: Do not start implementation until proposal is approved
## Three-Stage Workflow
### Stage 1: Creating Changes
Create proposal when you need to:
- Add features or functionality
- Make breaking changes (API, schema)
- Change architecture or patterns
- Optimize performance (changes behavior)
- Update security patterns
Triggers (examples):
- "Help me create a change proposal"
- "Help me plan a change"
- "Help me create a proposal"
- "I want to create a spec proposal"
- "I want to create a spec"
Loose matching guidance:
- Contains one of: `proposal`, `change`, `spec`
- With one of: `create`, `plan`, `make`, `start`, `help`
Skip proposal for:
- Bug fixes (restore intended behavior)
- Typos, formatting, comments
- Dependency updates (non-breaking)
- Configuration changes
- Tests for existing behavior
**Workflow**
1. Review `openspec/project.md`, `openspec list`, and `openspec list --specs` to understand current context.
2. Choose a unique verb-led `change-id` and scaffold `proposal.md`, `tasks.md`, optional `design.md`, and spec deltas under `openspec/changes/<id>/`.
3. Draft spec deltas using `## ADDED|MODIFIED|REMOVED Requirements` with at least one `#### Scenario:` per requirement.
4. Run `openspec validate <id> --strict` and resolve any issues before sharing the proposal.
### Stage 2: Implementing Changes
Track these steps as TODOs and complete them one by one.
1. **Read proposal.md** - Understand what's being built
2. **Read design.md** (if exists) - Review technical decisions
3. **Read tasks.md** - Get implementation checklist
4. **Implement tasks sequentially** - Complete in order
5. **Confirm completion** - Ensure every item in `tasks.md` is finished before updating statuses
6. **Update checklist** - After all work is done, set every task to `- [x]` so the list reflects reality
7. **Approval gate** - Do not start implementation until the proposal is reviewed and approved
### Stage 3: Archiving Changes
After deployment, create separate PR to:
- Move `changes/[name]/``changes/archive/YYYY-MM-DD-[name]/`
- Update `specs/` if capabilities changed
- Use `openspec archive <change-id> --skip-specs --yes` for tooling-only changes (always pass the change ID explicitly)
- Run `openspec validate --strict` to confirm the archived change passes checks
## Before Any Task
**Context Checklist:**
- [ ] Read relevant specs in `specs/[capability]/spec.md`
- [ ] Check pending changes in `changes/` for conflicts
- [ ] Read `openspec/project.md` for conventions
- [ ] Run `openspec list` to see active changes
- [ ] Run `openspec list --specs` to see existing capabilities
**Before Creating Specs:**
- Always check if capability already exists
- Prefer modifying existing specs over creating duplicates
- Use `openspec show [spec]` to review current state
- If request is ambiguous, ask 12 clarifying questions before scaffolding
### Search Guidance
- Enumerate specs: `openspec spec list --long` (or `--json` for scripts)
- Enumerate changes: `openspec list` (or `openspec change list --json` - deprecated but available)
- Show details:
- Spec: `openspec show <spec-id> --type spec` (use `--json` for filters)
- Change: `openspec show <change-id> --json --deltas-only`
- Full-text search (use ripgrep): `rg -n "Requirement:|Scenario:" openspec/specs`
## Quick Start
### CLI Commands
```bash
# Essential commands
openspec list # List active changes
openspec list --specs # List specifications
openspec show [item] # Display change or spec
openspec validate [item] # Validate changes or specs
openspec archive <change-id> [--yes|-y] # Archive after deployment (add --yes for non-interactive runs)
# Project management
openspec init [path] # Initialize OpenSpec
openspec update [path] # Update instruction files
# Interactive mode
openspec show # Prompts for selection
openspec validate # Bulk validation mode
# Debugging
openspec show [change] --json --deltas-only
openspec validate [change] --strict
```
### Command Flags
- `--json` - Machine-readable output
- `--type change|spec` - Disambiguate items
- `--strict` - Comprehensive validation
- `--no-interactive` - Disable prompts
- `--skip-specs` - Archive without spec updates
- `--yes`/`-y` - Skip confirmation prompts (non-interactive archive)
## Directory Structure
```
openspec/
├── project.md # Project conventions
├── specs/ # Current truth - what IS built
│ └── [capability]/ # Single focused capability
│ ├── spec.md # Requirements and scenarios
│ └── design.md # Technical patterns
├── changes/ # Proposals - what SHOULD change
│ ├── [change-name]/
│ │ ├── proposal.md # Why, what, impact
│ │ ├── tasks.md # Implementation checklist
│ │ ├── design.md # Technical decisions (optional; see criteria)
│ │ └── specs/ # Delta changes
│ │ └── [capability]/
│ │ └── spec.md # ADDED/MODIFIED/REMOVED
│ └── archive/ # Completed changes
```
## Creating Change Proposals
### Decision Tree
```
New request?
├─ Bug fix restoring spec behavior? → Fix directly
├─ Typo/format/comment? → Fix directly
├─ New feature/capability? → Create proposal
├─ Breaking change? → Create proposal
├─ Architecture change? → Create proposal
└─ Unclear? → Create proposal (safer)
```
### Proposal Structure
1. **Create directory:** `changes/[change-id]/` (kebab-case, verb-led, unique)
2. **Write proposal.md:**
```markdown
# Change: [Brief description of change]
## Why
[1-2 sentences on problem/opportunity]
## What Changes
- [Bullet list of changes]
- [Mark breaking changes with **BREAKING**]
## Impact
- Affected specs: [list capabilities]
- Affected code: [key files/systems]
```
3. **Create spec deltas:** `specs/[capability]/spec.md`
```markdown
## ADDED Requirements
### Requirement: New Feature
The system SHALL provide...
#### Scenario: Success case
- **WHEN** user performs action
- **THEN** expected result
## MODIFIED Requirements
### Requirement: Existing Feature
[Complete modified requirement]
## REMOVED Requirements
### Requirement: Old Feature
**Reason**: [Why removing]
**Migration**: [How to handle]
```
If multiple capabilities are affected, create multiple delta files under `changes/[change-id]/specs/<capability>/spec.md`—one per capability.
4. **Create tasks.md:**
```markdown
## 1. Implementation
- [ ] 1.1 Create database schema
- [ ] 1.2 Implement API endpoint
- [ ] 1.3 Add frontend component
- [ ] 1.4 Write tests
```
5. **Create design.md when needed:**
Create `design.md` if any of the following apply; otherwise omit it:
- Cross-cutting change (multiple services/modules) or a new architectural pattern
- New external dependency or significant data model changes
- Security, performance, or migration complexity
- Ambiguity that benefits from technical decisions before coding
Minimal `design.md` skeleton:
```markdown
## Context
[Background, constraints, stakeholders]
## Goals / Non-Goals
- Goals: [...]
- Non-Goals: [...]
## Decisions
- Decision: [What and why]
- Alternatives considered: [Options + rationale]
## Risks / Trade-offs
- [Risk] → Mitigation
## Migration Plan
[Steps, rollback]
## Open Questions
- [...]
```
## Spec File Format
### Critical: Scenario Formatting
**CORRECT** (use #### headers):
```markdown
#### Scenario: User login success
- **WHEN** valid credentials provided
- **THEN** return JWT token
```
**WRONG** (don't use bullets or bold):
```markdown
- **Scenario: User login** ❌
**Scenario**: User login ❌
### Scenario: User login ❌
```
Every requirement MUST have at least one scenario.
### Requirement Wording
- Use SHALL/MUST for normative requirements (avoid should/may unless intentionally non-normative)
### Delta Operations
- `## ADDED Requirements` - New capabilities
- `## MODIFIED Requirements` - Changed behavior
- `## REMOVED Requirements` - Deprecated features
- `## RENAMED Requirements` - Name changes
Headers matched with `trim(header)` - whitespace ignored.
#### When to use ADDED vs MODIFIED
- ADDED: Introduces a new capability or sub-capability that can stand alone as a requirement. Prefer ADDED when the change is orthogonal (e.g., adding "Slash Command Configuration") rather than altering the semantics of an existing requirement.
- MODIFIED: Changes the behavior, scope, or acceptance criteria of an existing requirement. Always paste the full, updated requirement content (header + all scenarios). The archiver will replace the entire requirement with what you provide here; partial deltas will drop previous details.
- RENAMED: Use when only the name changes. If you also change behavior, use RENAMED (name) plus MODIFIED (content) referencing the new name.
Common pitfall: Using MODIFIED to add a new concern without including the previous text. This causes loss of detail at archive time. If you arent explicitly changing the existing requirement, add a new requirement under ADDED instead.
Authoring a MODIFIED requirement correctly:
1) Locate the existing requirement in `openspec/specs/<capability>/spec.md`.
2) Copy the entire requirement block (from `### Requirement: ...` through its scenarios).
3) Paste it under `## MODIFIED Requirements` and edit to reflect the new behavior.
4) Ensure the header text matches exactly (whitespace-insensitive) and keep at least one `#### Scenario:`.
Example for RENAMED:
```markdown
## RENAMED Requirements
- FROM: `### Requirement: Login`
- TO: `### Requirement: User Authentication`
```
## Troubleshooting
### Common Errors
**"Change must have at least one delta"**
- Check `changes/[name]/specs/` exists with .md files
- Verify files have operation prefixes (## ADDED Requirements)
**"Requirement must have at least one scenario"**
- Check scenarios use `#### Scenario:` format (4 hashtags)
- Don't use bullet points or bold for scenario headers
**Silent scenario parsing failures**
- Exact format required: `#### Scenario: Name`
- Debug with: `openspec show [change] --json --deltas-only`
### Validation Tips
```bash
# Always use strict mode for comprehensive checks
openspec validate [change] --strict
# Debug delta parsing
openspec show [change] --json | jq '.deltas'
# Check specific requirement
openspec show [spec] --json -r 1
```
## Happy Path Script
```bash
# 1) Explore current state
openspec spec list --long
openspec list
# Optional full-text search:
# rg -n "Requirement:|Scenario:" openspec/specs
# rg -n "^#|Requirement:" openspec/changes
# 2) Choose change id and scaffold
CHANGE=add-two-factor-auth
mkdir -p openspec/changes/$CHANGE/{specs/auth}
printf "## Why\n...\n\n## What Changes\n- ...\n\n## Impact\n- ...\n" > openspec/changes/$CHANGE/proposal.md
printf "## 1. Implementation\n- [ ] 1.1 ...\n" > openspec/changes/$CHANGE/tasks.md
# 3) Add deltas (example)
cat > openspec/changes/$CHANGE/specs/auth/spec.md << 'EOF'
## ADDED Requirements
### Requirement: Two-Factor Authentication
Users MUST provide a second factor during login.
#### Scenario: OTP required
- **WHEN** valid credentials are provided
- **THEN** an OTP challenge is required
EOF
# 4) Validate
openspec validate $CHANGE --strict
```
## Multi-Capability Example
```
openspec/changes/add-2fa-notify/
├── proposal.md
├── tasks.md
└── specs/
├── auth/
│ └── spec.md # ADDED: Two-Factor Authentication
└── notifications/
└── spec.md # ADDED: OTP email notification
```
auth/spec.md
```markdown
## ADDED Requirements
### Requirement: Two-Factor Authentication
...
```
notifications/spec.md
```markdown
## ADDED Requirements
### Requirement: OTP Email Notification
...
```
## Best Practices
### Simplicity First
- Default to <100 lines of new code
- Single-file implementations until proven insufficient
- Avoid frameworks without clear justification
- Choose boring, proven patterns
### Complexity Triggers
Only add complexity with:
- Performance data showing current solution too slow
- Concrete scale requirements (>1000 users, >100MB data)
- Multiple proven use cases requiring abstraction
### Clear References
- Use `file.ts:42` format for code locations
- Reference specs as `specs/auth/spec.md`
- Link related changes and PRs
### Capability Naming
- Use verb-noun: `user-auth`, `payment-capture`
- Single purpose per capability
- 10-minute understandability rule
- Split if description needs "AND"
### Change ID Naming
- Use kebab-case, short and descriptive: `add-two-factor-auth`
- Prefer verb-led prefixes: `add-`, `update-`, `remove-`, `refactor-`
- Ensure uniqueness; if taken, append `-2`, `-3`, etc.
## Tool Selection Guide
| Task | Tool | Why |
|------|------|-----|
| Find files by pattern | Glob | Fast pattern matching |
| Search code content | Grep | Optimized regex search |
| Read specific files | Read | Direct file access |
| Explore unknown scope | Task | Multi-step investigation |
## Error Recovery
### Change Conflicts
1. Run `openspec list` to see active changes
2. Check for overlapping specs
3. Coordinate with change owners
4. Consider combining proposals
### Validation Failures
1. Run with `--strict` flag
2. Check JSON output for details
3. Verify spec file format
4. Ensure scenarios properly formatted
### Missing Context
1. Read project.md first
2. Check related specs
3. Review recent archives
4. Ask for clarification
## Quick Reference
### Stage Indicators
- `changes/` - Proposed, not yet built
- `specs/` - Built and deployed
- `archive/` - Completed changes
### File Purposes
- `proposal.md` - Why and what
- `tasks.md` - Implementation steps
- `design.md` - Technical decisions
- `spec.md` - Requirements and behavior
### CLI Essentials
```bash
openspec list # What's in progress?
openspec show [item] # View details
openspec validate --strict # Is it correct?
openspec archive <change-id> [--yes|-y] # Mark complete (add --yes for automation)
```
Remember: Specs are truth. Changes are proposals. Keep them in sync.

203
openspec/project.md Normal file
View File

@@ -0,0 +1,203 @@
# Project Context
## Purpose
牛只运输管理系统Cattle Transportation Management System是一个基于 Spring Boot 和 Vue 3 的现代化全栈应用,旨在为牛只运输过程提供全方位的管理和监控解决方案。
系统主要目标:
- 提供完整的牛只运输管理功能,包括运单管理、运输计划、路线规划等
- 实现检疫隔离管理,支持检疫记录、隔离状态监控、检疫证书管理
- 提供硬件设备监控能力,包括设备状态监控、数据采集、告警管理
- 构建预警系统,实现实时监控预警、异常情况报警、预警规则配置
- 支持用户权限管理、系统配置、日志管理等系统管理功能
## Tech Stack
### Backend (tradeCattle/)
- **基础框架**Spring Boot 2.6.0 + Java 1.8
- **ORM 框架**MyBatis-Plus 3.5.3.2
- **权限认证**Sa-Token 1.37.0 + JWT
- **数据库**MySQL 5.7+ + Redis 5.0+
- **连接池**Druid 1.2.20
- **任务调度**XXL-Job
- **API 文档**Swagger Fox 3.0.0
- **工具库**Hutool 5.8.25
- **云服务**:腾讯云 COS、短信服务
### Frontend (pc-cattle-transportation/)
- **核心框架**Vue 3.2.37 + TypeScript 4.6.4
- **构建工具**Vite 3.1.0
- **状态管理**Pinia 2.0.22
- **路由管理**Vue Router 4.1.5
- **UI 组件**Element Plus 2.2.17
- **HTTP 客户端**Axios 0.27.2
- **地图服务**百度地图vue-baidu-map-3x
- **图表库**ECharts 5.5.0
## Project Conventions
### Code Style
#### Backend (Java)
- **命名约定**
- 类名PascalCase`DeliveryController`
- 方法名camelCase`findUserById`
- 常量UPPER_CASE`MAX_RETRY_ATTEMPTS`
- 包名:小写,按功能模块划分
- **注释规范**
- 类注释:说明类的用途、作者、创建日期
- 方法注释JavaDoc 格式,说明参数、返回值、异常
- 复杂逻辑:添加行内注释解释
- **代码格式**:使用 4 个空格缩进,遵循 Java 官方代码风格
#### Frontend (TypeScript/Vue)
- **命名约定**
- 组件文件PascalCase 或 camelCase
- 变量和函数camelCase
- 常量UPPER_CASE
- 类型和接口PascalCase
- **组件规范**
- 使用 Vue 3 Composition API
- 组件保持单一职责
- Props 使用 TypeScript 类型定义
- **代码格式**:使用 4 个空格缩进,行宽 150 字符,语句末尾使用分号
### Architecture Patterns
#### Backend Architecture
- **分层架构**:严格遵守 Controller → Service → Mapper 分层
- Controller 层:处理 HTTP 请求,参数校验,返回响应
- Service 层:业务逻辑处理,事务管理
- Mapper 层:数据访问层,执行 SQL 操作
- **模块划分**
- `aiotagro-core`:核心模块,包含通用工具类和基础类
- `aiotagro-redis`Redis 操作模块
- `aiotagro-cattle-trade`:主业务模块,包含所有业务逻辑
- **数据传输对象**
- Entity数据库实体映射
- DTO接收前端请求参数
- VO返回给前端的数据视图
#### Frontend Architecture
- **组件化开发**:按功能模块组织组件
- `api/`API 接口定义,按业务模块划分
- `components/`:公共组件
- `views/`:页面组件
- `store/`Pinia 状态管理
- **路由管理**:使用 Vue Router 进行路由配置和权限控制
- **状态管理**:使用 Pinia 进行全局状态管理
### Testing Strategy
#### Backend Testing
- **单元测试**Service 层编写单元测试,使用 JUnit 5 + Mockito
- **集成测试**Controller 层编写集成测试,测试完整业务流程
- **测试覆盖率目标**60% 以上
- **关键业务逻辑**:必须有单元测试
#### Frontend Testing
- **工具函数**:编写单元测试
- **关键组件**:编写测试
- **测试工具**Jest + Vue Test Utils
### Git Workflow
- **分支策略**:使用 Git Flow 工作流
- `main/master`:主分支,生产环境代码
- `develop`:开发分支,集成最新功能
- `feature/*`:功能分支,开发新功能
- `hotfix/*`:热修复分支,紧急修复生产问题
- **提交规范**
- 编写有意义的提交信息
- 保持逻辑相关的更改在同一提交中
- 遵循约定式提交格式Conventional Commits
- **代码审查**:提交前进行代码自查,重要变更需要 Code Review
## Domain Context
### 核心业务领域
#### 1. 运输管理Delivery/Shipping
- **运单管理**:运单创建、编辑、查询、状态跟踪
- **运输计划**:运输计划制定、路线规划
- **车辆管理**:车辆信息管理、车辆备案码管理
- **司机管理**:司机信息管理、司机资质管理
- **装车管理**:装车记录、装车状态跟踪
#### 2. 检疫隔离管理Quarantine
- **检疫记录**:检疫记录创建、查询、管理
- **隔离状态监控**:隔离状态跟踪、隔离期限管理
- **检疫证书**:检疫证书生成、管理、验证
- **入境检疫**:入境检疫流程管理
- **出境管理**:出境检疫流程管理
#### 3. 硬件设备管理Hardware/Device
- **设备监控**:设备状态监控、设备数据采集
- **设备类型**
- 项圈设备Collar Device
- 耳标设备Ear Tag Device
- 主机设备Host Device
- **设备日志**:设备日志记录、设备告警管理
- **设备维护**:设备维护记录、设备故障处理
#### 4. 预警系统Early Warning
- **实时监控**:实时监控预警、异常情况报警
- **预警规则**:预警规则配置、预警阈值设置
- **预警处理**:预警处理跟踪、预警处理记录
#### 5. 系统管理System
- **用户管理**:系统用户管理、用户权限管理
- **角色管理**:角色定义、角色权限配置
- **菜单权限**:菜单权限管理、权限控制
- **日志管理**:系统操作日志、业务日志记录
- **数据录入**:数据录入、数据审核
### 核心实体
- **Delivery运单**:运单号、车牌号、司机、出发地、目的地、状态等
- **Vehicle车辆**:车牌号、备案码、车辆类型、车辆状态等
- **Driver司机**:司机姓名、手机号、驾驶证号、司机状态等
- **Order订单**:买方、卖方、结算方式、约定价格等
- **Quarantine检疫**:检疫记录、检疫证书、检疫状态等
- **Device设备**设备ID、设备类型、设备状态、设备位置等
- **User用户**:用户账号、密码、角色、权限等
## Important Constraints
### 技术约束
- **Java 版本**:必须使用 Java 1.8,不支持更高版本(除非明确升级)
- **数据库版本**MySQL 5.7+,不支持 MySQL 8.0 的某些特性
- **浏览器兼容性**前端需要支持现代浏览器Chrome、Firefox、Edge 最新版本)
- **性能要求**:接口响应时间 < 2 大数据量查询需要分页
### 业务约束
- **数据安全**敏感数据密码身份证等必须加密存储
- **权限控制**所有业务操作需要权限验证使用 Sa-Token 进行权限控制
- **数据完整性**关键业务数据需要事务保证避免数据不一致
- **审计要求**关键操作需要记录操作日志包括操作人操作时间操作内容
### 合规约束
- **数据隐私**遵守数据隐私保护相关法规
- **日志保留**操作日志需要保留一定期限建议 6 个月以上
- **数据备份**重要数据需要定期备份
## External Dependencies
### 云服务
- **腾讯云 COS对象存储**用于文件存储包括图片文档等
- **腾讯云短信服务**用于发送验证码通知短信等
### 第三方服务
- **百度地图 API**用于地图展示位置定位路线规划等
- **XXL-Job**分布式任务调度平台用于定时任务管理
### 数据库
- **MySQL**主数据库存储业务数据
- **Redis**缓存数据库用于会话存储缓存数据
### API 文档
- **Swagger Fox**API 文档生成和调试工具访问路径`/swagger-ui.html`
### 开发工具
- **MyBatis-Plus Generator**代码生成器用于生成 EntityMapperService
- **Lombok**简化 Java Bean 代码减少样板代码

View File

@@ -58,4 +58,43 @@ public class DeliveryEditDto {
*/ */
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Date estimatedArrivalTime; private Date estimatedArrivalTime;
/** 空车过磅重量 */
private String emptyWeight;
/** 装车过磅重量 */
private String entruckWeight;
/** 落地过磅重量 */
private String landingEntruckWeight;
/** 检疫票照片 */
private String quarantineTickeyUrl;
/** 传纸质磅单(双章) */
private String poundListImg;
/** 车辆空磅上磅车头照片 */
private String emptyVehicleFrontPhoto;
/** 车辆过重磅车头照片 */
private String loadedVehicleFrontPhoto;
/** 车辆重磅照片 */
private String loadedVehicleWeightPhoto;
/** 驾驶员手持身份证站车头照片 */
private String driverIdCardPhoto;
/** 到地纸质磅单(双章) */
private String destinationPoundListImg;
/** 到地车辆过重磅车头照片 */
private String destinationVehicleFrontPhoto;
/** 装车过磅视频 */
private String entruckWeightVideo;
/** 空车过磅视频 */
private String emptyWeightVideo;
/** 装牛视频 */
private String cattleLoadingVideo;
/** 控槽视频 */
private String controlSlotVideo;
/** 装完牛绕车一圈视频 */
private String cattleLoadingCircleVideo;
/** 卸牛视频 */
private String unloadCattleVideo;
/** 到地过磅视频 */
private String destinationWeightVideo;
} }

View File

@@ -689,7 +689,9 @@ public class DeliveryServiceImpl extends ServiceImpl<DeliveryMapper, Delivery> i
Integer userId = SecurityUtil.getCurrentUserId(); Integer userId = SecurityUtil.getCurrentUserId();
// 入参日志 // 入参日志
logger.info("更新运送清单: deliveryId={}, ratedQuantity={}", dto.getDeliveryId(), dto.getRatedQuantity()); logger.info("更新运送清单: deliveryId={}, ratedQuantity={}, quarantineTickeyUrl={}, poundListImg={}, cattleLoadingVideo={}",
dto.getDeliveryId(), dto.getRatedQuantity(), dto.getQuarantineTickeyUrl(),
dto.getPoundListImg(), dto.getCattleLoadingVideo());
// 查询运送清单是否存在 // 查询运送清单是否存在
Delivery delivery = this.getById(dto.getDeliveryId()); Delivery delivery = this.getById(dto.getDeliveryId());
@@ -765,6 +767,71 @@ public class DeliveryServiceImpl extends ServiceImpl<DeliveryMapper, Delivery> i
delivery.setFirmPrice(dto.getFirmPrice()); delivery.setFirmPrice(dto.getFirmPrice());
} }
// 更新重量字段将空字符串转换为null避免数据库DECIMAL字段类型错误
// 注意:前端可能传递空字符串"",需要处理这种情况
if (dto.getEmptyWeight() != null) {
delivery.setEmptyWeight(StringUtils.isNotEmpty(dto.getEmptyWeight()) ? dto.getEmptyWeight() : null);
}
if (dto.getEntruckWeight() != null) {
delivery.setEntruckWeight(StringUtils.isNotEmpty(dto.getEntruckWeight()) ? dto.getEntruckWeight() : null);
}
if (dto.getLandingEntruckWeight() != null) {
delivery.setLandingEntruckWeight(StringUtils.isNotEmpty(dto.getLandingEntruckWeight()) ? dto.getLandingEntruckWeight() : null);
}
// 更新照片字段将空字符串转换为null避免前端显示问题
// 注意前端总是传递字段即使是空字符串只有在传递了有效URL时才更新
if (dto.getQuarantineTickeyUrl() != null) {
delivery.setQuarantineTickeyUrl(StringUtils.isNotEmpty(dto.getQuarantineTickeyUrl()) ? dto.getQuarantineTickeyUrl() : null);
}
if (dto.getPoundListImg() != null) {
delivery.setPoundListImg(StringUtils.isNotEmpty(dto.getPoundListImg()) ? dto.getPoundListImg() : null);
}
if (dto.getEmptyVehicleFrontPhoto() != null) {
delivery.setEmptyVehicleFrontPhoto(StringUtils.isNotEmpty(dto.getEmptyVehicleFrontPhoto()) ? dto.getEmptyVehicleFrontPhoto() : null);
}
if (dto.getLoadedVehicleFrontPhoto() != null) {
delivery.setLoadedVehicleFrontPhoto(StringUtils.isNotEmpty(dto.getLoadedVehicleFrontPhoto()) ? dto.getLoadedVehicleFrontPhoto() : null);
}
if (dto.getLoadedVehicleWeightPhoto() != null) {
delivery.setLoadedVehicleWeightPhoto(StringUtils.isNotEmpty(dto.getLoadedVehicleWeightPhoto()) ? dto.getLoadedVehicleWeightPhoto() : null);
}
if (dto.getDriverIdCardPhoto() != null) {
delivery.setDriverIdCardPhoto(StringUtils.isNotEmpty(dto.getDriverIdCardPhoto()) ? dto.getDriverIdCardPhoto() : null);
}
if (dto.getDestinationPoundListImg() != null) {
delivery.setDestinationPoundListImg(StringUtils.isNotEmpty(dto.getDestinationPoundListImg()) ? dto.getDestinationPoundListImg() : null);
}
if (dto.getDestinationVehicleFrontPhoto() != null) {
delivery.setDestinationVehicleFrontPhoto(StringUtils.isNotEmpty(dto.getDestinationVehicleFrontPhoto()) ? dto.getDestinationVehicleFrontPhoto() : null);
}
// 更新视频字段将空字符串转换为null避免前端显示问题
if (dto.getEntruckWeightVideo() != null) {
delivery.setEntruckWeightVideo(StringUtils.isNotEmpty(dto.getEntruckWeightVideo()) ? dto.getEntruckWeightVideo() : null);
}
if (dto.getEmptyWeightVideo() != null) {
delivery.setEmptyWeightVideo(StringUtils.isNotEmpty(dto.getEmptyWeightVideo()) ? dto.getEmptyWeightVideo() : null);
}
if (dto.getCattleLoadingVideo() != null) {
delivery.setEntruckVideo(StringUtils.isNotEmpty(dto.getCattleLoadingVideo()) ? dto.getCattleLoadingVideo() : null);
}
if (dto.getControlSlotVideo() != null) {
delivery.setControlSlotVideo(StringUtils.isNotEmpty(dto.getControlSlotVideo()) ? dto.getControlSlotVideo() : null);
}
if (dto.getCattleLoadingCircleVideo() != null) {
delivery.setCattleLoadingCircleVideo(StringUtils.isNotEmpty(dto.getCattleLoadingCircleVideo()) ? dto.getCattleLoadingCircleVideo() : null);
}
if (dto.getUnloadCattleVideo() != null) {
delivery.setUnloadCattleVideo(StringUtils.isNotEmpty(dto.getUnloadCattleVideo()) ? dto.getUnloadCattleVideo() : null);
}
if (dto.getDestinationWeightVideo() != null) {
delivery.setDestinationWeightVideo(StringUtils.isNotEmpty(dto.getDestinationWeightVideo()) ? dto.getDestinationWeightVideo() : null);
}
logger.info("更新文件字段: quarantineTickeyUrl={}, poundListImg={}, cattleLoadingVideo={}",
delivery.getQuarantineTickeyUrl(), delivery.getPoundListImg(), delivery.getEntruckVideo());
// 根据车牌号从车辆表重新获取车辆照片(编辑时同步更新照片) // 根据车牌号从车辆表重新获取车辆照片(编辑时同步更新照片)
// 这样即使车辆表中的照片有更新,也能同步到运送清单 // 这样即使车辆表中的照片有更新,也能同步到运送清单
if (delivery.getLicensePlate() != null && StringUtils.isNotEmpty(delivery.getLicensePlate())) { if (delivery.getLicensePlate() != null && StringUtils.isNotEmpty(delivery.getLicensePlate())) {