diff --git a/.gitignore b/.gitignore index fb9d512..4c2539d 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ acs2/.idea/ +acs2/nladmin-system/logPath_IS_UNDEFINED/ diff --git a/acs2/nladmin-system/DEVELOPMENT_GUIDE.md b/acs2/nladmin-system/DEVELOPMENT_GUIDE.md new file mode 100644 index 0000000..9edcf14 --- /dev/null +++ b/acs2/nladmin-system/DEVELOPMENT_GUIDE.md @@ -0,0 +1,537 @@ +# NL Admin System 开发手册 + +## 1. 开发环境搭建 + +### 1.1 环境要求 + +| 软件 | 版本 | 说明 | +| :--- | :--- | :--- | +| JDK | 1.8 | 必须使用 Java 8 | +| Maven | 3.6+ | 依赖管理工具 | +| MySQL | 5.7+ | 主数据库 | +| Redis | 3.2+ | 缓存与会话管理 | + +### 1.2 JDK 配置 + +```bash +# 设置环境变量 +export JAVA_HOME=/path/to/jdk1.8 +export PATH=$JAVA_HOME/bin:$PATH +``` + +### 1.3 Maven 配置 + + `settings.xml` : + +```xml + + nexus-aliyun + central + Nexus aliyun + http://maven.aliyun.com/nexus/content/groups/public + +``` + +--- + +## 2. 项目结构详解 + +### 2.1 模块划分 + +``` +src/main/java/org/nl/ +├── AppRun.java # Spring Boot 启动类 +├── acs/ # 设备控制模块(核心业务) +│ ├── apiAdd/ # 地址管理 +│ ├── autoThread/ # 自动线程管理 +│ ├── device/ # 设备管理 +│ ├── iot/ # IoT数据处理 +│ ├── layout/ # 布局管理 +│ ├── log/ # 日志管理 +│ └── task/ # 任务调度 +├── common/ # 通用模块(工具、配置、异常等) +│ ├── annotation/ # 自定义注解 +│ ├── base/ # 基础类(DTO、Mapper等) +│ ├── db/ # 数据库工具 +│ ├── domain/ # 通用领域模型 +│ ├── enums/ # 枚举类 +│ ├── exception/ # 异常处理 +│ ├── logging/ # 日志切面 +│ ├── mnt/ # 监控与维护 +│ ├── security/ # 安全配置 +│ └── utils/ # 工具类 +├── config/ # 配置类 +│ ├── jackson/ # JSON序列化配置 +│ ├── language/ # 国际化配置 +│ ├── lucene/ # 全文搜索配置 +│ ├── mybatis/ # MyBatis配置 +│ ├── redis/ # Redis配置 +│ ├── saconfig/ # Sa-Token配置 +│ └── thread/ # 线程池配置 +├── extInterface/ # 外部接口 +│ ├── agvKit/ # AGV系统对接 +│ └── wms/ # WMS系统对接 +└── system/ # 系统管理 + └── controller/ # 系统控制器 +``` + +### 2.2 核心模块说明 + +| 模块 | 职责 | 说明 | +| :--- | :--- | :--- | +| `acs/` | 设备控制核心 | 包含设备管理、任务调度、IoT通信等核心业务 | +| `common/` | 通用工具 | 提供基础工具类、异常处理、日志等通用能力 | +| `config/` | 配置管理 | Spring配置类、第三方组件配置 | +| `extInterface/` | 外部对接 | AGV、WMS等外部系统接口 | +| `system/` | 系统管理 | 用户、角色、权限等系统管理功能 | + +--- + +## 3. 代码规范 + +### 3.1 命名规范 + +| 类型 | 规范 | 示例 | +| :--- | :--- | :--- | +| 包名 | 全小写,使用点分隔 | `org.nl.acs.device` | +| 类名 | 大驼峰命名法 | `DeviceController` | +| 方法名 | 小驼峰命名法 | `getDeviceById` | +| 变量名 | 小驼峰命名法 | `deviceName` | +| 常量名 | 全大写,下划线分隔 | `MAX_PAGE_SIZE` | +| 接口名 | 大驼峰,以 `I` 开头 | `IDeviceService` | + +### 3.2 格式规范 + +- 使用 4 个空格缩进(禁止使用 Tab) +- 每行代码不超过 120 字符 +- 方法之间空一行 +- 类成员变量与方法之间空两行 + +### 3.3 注释规范 + +```java +/** + * 获取设备详情 + * @param id 设备ID + * @return 设备信息 + */ +public DeviceDto getDeviceById(Long id) { + // 业务逻辑 +} +``` + +### 3.4 异常处理规范 + +```java +// 错误示例 +try { + // 业务逻辑 +} catch (Exception e) { + e.printStackTrace(); // 禁止直接打印堆栈 +} + +// 正确示例 +try { + // 业务逻辑 +} catch (Exception e) { + log.error("获取设备失败, id: {}", id, e); + throw new BadRequestException("获取设备失败"); +} +``` + +--- + +## 4. 开发流程 + +### 4.1 需求分析 + +1. 明确需求范围和业务场景 +2. 分析与现有模块的依赖关系 +3. 设计数据模型和接口 + +### 4.2 编码流程 + +``` +需求分析 → 设计文档 → 创建DTO → 创建Entity → 创建Mapper → 创建Service → 创建Controller → 单元测试 +``` + +### 4.3 Git 工作流 + +```bash +# 1. 从主分支拉取最新代码 +git checkout develop +git pull origin develop + +# 2. 创建特性分支 +git checkout -b feature/xxx-feature + +# 3. 开发并提交 +git add . +git commit -m "feat: 实现xxx功能" + +# 4. 推送到远程 +git push origin feature/xxx-feature + +# 5. 创建 Pull Request +``` + +--- + +## 5. 数据库设计规范 + +### 5.1 命名规范 + +| 对象 | 规范 | 示例 | +| :--- | :--- | :--- | +| 表名 | 小写,下划线分隔,前缀 `sys_` | `sys_device` | +| 字段名 | 小写,下划线分隔 | `device_name` | +| 主键 | 统一命名为 `id`,自增 | `id BIGINT PRIMARY KEY AUTO_INCREMENT` | + +### 5.2 字段类型选择 + +| 类型 | 用途 | 示例 | +| :--- | :--- | :--- | +| BIGINT | 主键、ID字段 | `id`, `user_id` | +| VARCHAR | 字符串,最大255 | `name`, `code` | +| TEXT | 长文本 | `description` | +| DATETIME | 日期时间 | `create_time`, `update_time` | +| INT | 整数 | `status`, `sort` | +| DECIMAL | 精确小数 | `price`, `quantity` | + +### 5.3 通用字段 + +```sql +CREATE TABLE example ( + id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '主键ID', + create_by VARCHAR(64) COMMENT '创建人', + create_time DATETIME COMMENT '创建时间', + update_by VARCHAR(64) COMMENT '更新人', + update_time DATETIME COMMENT '更新时间', + is_deleted TINYINT DEFAULT 0 COMMENT '删除标记(0-未删除,1-已删除)', + remark VARCHAR(255) COMMENT '备注' +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='示例表'; +``` + +--- + +## 6. API 开发规范 + +### 6.1 RESTful 风格 + +| 操作 | HTTP方法 | 路径 | +| :--- | :--- | :--- | +| 查询列表 | GET | `/api/device` | +| 查询单个 | GET | `/api/device/{id}` | +| 新增 | POST | `/api/device` | +| 更新 | PUT | `/api/device/{id}` | +| 删除 | DELETE | `/api/device/{id}` | + +### 6.2 统一响应格式 + +```java +// 成功响应 +{ + "code": 200, + "message": "操作成功", + "data": {...}, + "timestamp": 1620000000000 +} + +// 失败响应 +{ + "code": 400, + "message": "参数错误", + "data": null, + "timestamp": 1620000000000 +} +``` + +### 6.3 Controller 示例 + +```java +@RestController +@RequestMapping("/api/device") +public class DeviceController { + + @Autowired + private IDeviceService deviceService; + + @GetMapping + public ResponseEntity getAll(DeviceQuery query) { + List devices = deviceService.queryAll(query); + return ResponseEntity.ok(devices); + } + + @GetMapping("/{id}") + public ResponseEntity getById(@PathVariable Long id) { + DeviceDto device = deviceService.getById(id); + return ResponseEntity.ok(device); + } + + @PostMapping + public ResponseEntity create(@RequestBody DeviceDto dto) { + deviceService.create(dto); + return ResponseEntity.ok("创建成功"); + } + + @PutMapping("/{id}") + public ResponseEntity update(@PathVariable Long id, @RequestBody DeviceDto dto) { + deviceService.update(id, dto); + return ResponseEntity.ok("更新成功"); + } + + @DeleteMapping("/{id}") + public ResponseEntity delete(@PathVariable Long id) { + deviceService.delete(id); + return ResponseEntity.ok("删除成功"); + } +} +``` + +--- + +## 7. Service 层开发规范 + +### 7.1 接口定义 + +```java +public interface IDeviceService { + + /** + * 查询设备列表 + */ + List queryAll(DeviceQuery query); + + /** + * 根据ID查询设备 + */ + DeviceDto getById(Long id); + + /** + * 创建设备 + */ + void create(DeviceDto dto); + + /** + * 更新设备 + */ + void update(Long id, DeviceDto dto); + + /** + * 删除设备 + */ + void delete(Long id); +} +``` + +### 7.2 实现类规范 + +```java +@Service +public class DeviceServiceImpl implements IDeviceService { + + @Autowired + private DeviceMapper deviceMapper; + + @Autowired + private RedisUtils redisUtils; + + @Override + @Transactional(rollbackFor = Exception.class) + public void create(DeviceDto dto) { + // 参数校验 + if (StrUtil.isEmpty(dto.getName())) { + throw new BadRequestException("设备名称不能为空"); + } + + // 转换为实体 + Device entity = ConvertUtil.convert(dto, Device.class); + + // 保存到数据库 + deviceMapper.insert(entity); + + // 更新缓存 + redisUtils.set("device:" + entity.getId(), entity); + } +} +``` + +--- + +## 8. 安全规范 + +### 8.1 权限控制 + +使用 Sa-Token 进行权限控制: + +```java +@RestController +@RequestMapping("/api/admin") +public class AdminController { + + @GetMapping("/users") + public ResponseEntity getUsers() { + return ResponseEntity.ok(userService.getAll()); + } +} +``` + +### 8.2 参数校验 + +使用 `@Valid` 注解进行参数校验: + +```java +public class DeviceDto { + + @NotBlank(message = "设备名称不能为空") + @Size(max = 100, message = "设备名称不能超过100字符") + private String name; + + @Min(value = 0, message = "状态值不能为负数") + private Integer status; +} +``` + +### 8.3 SQL 注入防护 + +```java +// 正确:使用参数化查询 +QueryWrapper wrapper = new QueryWrapper<>(); +wrapper.eq("device_code", deviceCode); +deviceMapper.selectOne(wrapper); + +// 错误:拼接SQL +String sql = "SELECT * FROM device WHERE code = '" + deviceCode + "'"; +``` + +--- + +## 9. 日志规范 + +### 9.1 日志级别 + +| 级别 | 用途 | +| :--- | :--- | +| DEBUG | 详细的调试信息,生产环境关闭 | +| INFO | 重要的业务流程日志 | +| WARN | 警告信息,需要关注但不影响运行 | +| ERROR | 错误信息,需要立即处理 | + +### 9.2 日志格式 + +```java +// 错误示例 +log.info("设备ID: " + id + ", 名称: " + name); + +// 正确示例 +log.info("创建设备成功, id: {}, name: {}", id, name); +log.error("创建设备失败, id: {}, error: {}", id, e.getMessage(), e); +``` + +--- + +## 10. 测试规范 + +### 10.1 单元测试 + +```java +@SpringBootTest +class DeviceServiceTest { + + @Autowired + private IDeviceService deviceService; + + @Test + void testCreate() { + DeviceDto dto = new DeviceDto(); + dto.setName("测试设备"); + dto.setCode("TEST001"); + + deviceService.create(dto); + + DeviceDto result = deviceService.getById(1L); + assertEquals("测试设备", result.getName()); + } +} +``` + +### 10.2 测试覆盖率 + +- 核心业务逻辑覆盖率 ≥ 80% +- 工具类覆盖率 ≥ 90% +- 新增代码必须有对应的单元测试 + +--- + +## 11. 部署与发布 + +### 11.1 配置管理 + +```bash +# 开发环境 +java -jar nlsso-server.jar --spring.profiles.active=dev + +# 生产环境 +java -jar nlsso-server.jar --spring.profiles.active=prod +``` + +### 11.2 启动脚本 + +```bash +#!/bin/bash +APP_NAME=nlsso-server +APP_DIR=/opt/app + +cd $APP_DIR + +# 停止旧进程 +PID=$(ps -ef | grep $APP_NAME | grep -v grep | awk '{print $2}') +if [ -n "$PID" ]; then + kill -9 $PID + sleep 2 +fi + +# 启动新进程 +nohup java -jar $APP_DIR/$APP_NAME.jar --spring.profiles.active=prod > /dev/null 2>&1 & + +echo "服务已启动" +``` + +--- + +## 12. 常见问题 + +### 12.1 依赖冲突 + +问题:启动时报 `NoSuchMethodError` + +解决方案:检查 `pom.xml` 中的依赖版本,使用 `mvn dependency:tree` 分析依赖树,排除冲突的依赖。 + +### 12.2 数据库连接失败 + +问题:无法连接数据库 + +解决方案: +1. 检查数据库服务是否启动 +2. 检查 `application.yml` 中的数据库配置 +3. 检查数据库用户名和密码 + +### 12.3 Redis 连接失败 + +问题:缓存操作失败 + +解决方案: +1. 检查 Redis 服务是否启动 +2. 检查 Redis 配置(主机、端口、密码) +3. 检查防火墙规则 + +### 12.4 权限不足 + +问题:访问接口时返回 403 + +解决方案: +1. 检查用户是否登录 +2. 检查用户是否具有相应权限 +3. 检查权限配置是否正确 + +--- diff --git a/acs2/nladmin-system/README.md b/acs2/nladmin-system/README.md index 9532833..8bf8c2d 100644 --- a/acs2/nladmin-system/README.md +++ b/acs2/nladmin-system/README.md @@ -1,2 +1,202 @@ -# nladmin +# NL Admin System +诺力智能仓储管理ACS系统后端服务 + +## 项目简介 + +本项目是诺力智能仓储管理ACS系统后端服务标准版,基于 Spring Boot 框架构建,提供完整的用户认证、设备管理、任务调度、AGV调度、设备管理等核心功能。 + +## 技术栈 + +| 技术 | 版本 | 说明 | +| :--- | :--- | :--- | +| Java | 1.8 | 开发语言 | +| Spring Boot | 2.2.x | 应用框架 | +| Sa-Token | 1.31.0 | 权限认证框架 | +| MyBatis Plus | 3.4.0 | ORM框架 | +| Redis | - | 缓存与会话管理 | +| MySQL/Oracle | - | 数据库 | +| Lucene | 8.2.0 | 全文搜索 | +| WebSocket | - | 实时通信 | + +## 项目结构 + +``` +nladmin-system/ +├── nlsso-server/ # 后端服务模块 +│ ├── src/main/java/org/nl/ +│ │ ├── AppRun.java # 启动类 +│ │ ├── acs/ # 设备控制模块 +│ │ │ ├── apiAdd/ # 地址管理 +│ │ │ ├── autoThread/ # 自动线程管理 +│ │ │ ├── device/ # 设备管理 +│ │ │ ├── iot/ # IoT数据处理 +│ │ │ ├── layout/ # 布局管理 +│ │ │ ├── log/ # 日志管理 +│ │ │ └── task/ # 任务调度 +│ │ ├── common/ # 通用模块 +│ │ │ ├── annotation/ # 自定义注解 +│ │ │ ├── base/ # 基础类 +│ │ │ ├── db/ # 数据库工具 +│ │ │ ├── domain/ # 通用领域模型 +│ │ │ ├── enums/ # 枚举类 +│ │ │ ├── exception/ # 异常处理 +│ │ │ ├── logging/ # 日志切面 +│ │ │ ├── mnt/ # 监控与维护 +│ │ │ ├── security/ # 安全配置 +│ │ │ └── utils/ # 工具类 +│ │ ├── config/ # 配置类 +│ │ │ ├── jackson/ # JSON配置 +│ │ │ ├── language/ # 国际化配置 +│ │ │ ├── lucene/ # 搜索配置 +│ │ │ ├── mybatis/ # MyBatis配置 +│ │ │ ├── redis/ # Redis配置 +│ │ │ ├── saconfig/ # Sa-Token配置 +│ │ │ └── thread/ # 线程池配置 +│ │ ├── extInterface/ # 外部接口 +│ │ │ ├── agvKit/ # AGV接口 +│ │ │ └── wms/ # WMS接口 +│ │ └── system/ # 系统管理 +│ │ └── controller/ # 系统控制器 +│ └── pom.xml # Maven配置 +└── README.md # 项目说明 +``` + +## 快速开始 + +### 环境要求 + +- JDK 1.8+ +- Maven 3.6+ +- Redis 3.2+ +- MySQL 5.7+ / Oracle 11g+ + +### 配置说明 + +#### 1. 数据库配置 + +修改 `src/main/resources/application.yml`: + +```yaml +spring: + datasource: + url: jdbc:mysql://localhost:3306/nladmin?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai + username: admin + password: password + driver-class-name: com.mysql.cj.jdbc.Driver +``` + +#### 2. Redis 配置 + +```yaml +spring: + redis: + host: localhost + port: 6379 + password: + timeout: 10000ms +``` + +#### 3. Sa-Token 配置 + +```yaml +sa-token: + token-name: Authorization + timeout: 86400000 + active-timeout: -1 + is-concurrent: true + is-share: true + token-style: uuid + is-log: false +``` + +### 启动方式 + +```bash +java -jar target/nlsso-server.jar +``` + +## 核心功能 + +### 1. 用户认证 +- 登录/登出 +- 在线用户管理 +- Token 验证 +- 权限校验 + +### 2. 设备管理 +- 设备信息管理 +- 设备协议管理 +- 货位管理 +- 设备扩展信息 + +### 3. 任务调度 +- 任务创建与执行 +- 任务历史记录 +- 任务指令管理 +- 任务优先级调度 + +### 4. 外部接口 +- AGV 系统对接 +- WMS 系统对接 +- 数据交互协议 + +### 5. 日志管理 +- 操作日志记录 +- 设备执行日志 +- Lucene 全文检索 + +## API 接口 + +### 认证接口 + +| 接口 | 方法 | 描述 | +| :--- | :--- | :--- | +| `/api/auth/login` | POST | 用户登录 | +| `/api/auth/logout` | POST | 用户登出 | +| `/api/auth/online` | GET | 获取在线用户列表 | + +### 设备接口 + +| 接口 | 方法 | 描述 | +| :--- | :--- | :--- | +| `/api/device` | GET | 获取设备列表 | +| `/api/device/{id}` | GET | 获取设备详情 | +| `/api/device` | POST | 创建设备 | +| `/api/device/{id}` | PUT | 更新设备 | +| `/api/device/{id}` | DELETE | 删除设备 | + +### 任务接口 + +| 接口 | 方法 | 描述 | +| :--- | :--- | :--- | +| `/api/task` | GET | 获取任务列表 | +| `/api/task/{id}` | GET | 获取任务详情 | +| `/api/task` | POST | 创建任务 | +| `/api/task/{id}/execute` | POST | 执行任务 | + +## 安全说明 + +1. **密码加密**:采用 RSA 非对称加密传输,MD5 加盐存储 +2. **Token 管理**:基于 Sa-Token 的会话管理,支持多端登录控制 +3. **权限控制**:基于注解的细粒度权限控制 +4. **请求限流**:支持接口限流配置 +5. **SQL 注入防护**:使用 MyBatis Plus 参数化查询 + +## 开发规范 + +### 命名规范 +- 包名:小写字母,使用点分隔 +- 类名:大驼峰命名法(PascalCase) +- 方法名:小驼峰命名法(camelCase) +- 变量名:小驼峰命名法(camelCase) + +### 代码规范 +- 遵循阿里巴巴 Java 开发手册 +- 使用 Lombok 简化代码 +- 异常处理统一使用全局异常处理器 +- 日志记录使用 SLF4J + +## 许可证 + +Apache License 2.0 diff --git a/acs2/nladmin-system/nlsso-server/src/main/java/org/nl/acs/autoThread/autoRun/AbstractAutoRunnable.java b/acs2/nladmin-system/nlsso-server/src/main/java/org/nl/acs/autoThread/autoRun/AbstractAutoRunnable.java index aad7be0..d1baaeb 100644 --- a/acs2/nladmin-system/nlsso-server/src/main/java/org/nl/acs/autoThread/autoRun/AbstractAutoRunnable.java +++ b/acs2/nladmin-system/nlsso-server/src/main/java/org/nl/acs/autoThread/autoRun/AbstractAutoRunnable.java @@ -2,7 +2,7 @@ package org.nl.acs.autoThread.autoRun; import lombok.Data; import lombok.extern.slf4j.Slf4j; -import org.nl.acs.autoThread.autoRunThread.service.enums.ThreadUsedStatusEnum; +import org.nl.acs.autoThread.service.enums.ThreadUsedStatusEnum; import org.nl.acs.autoThread.service.enums.ThreadStatusEnum; import java.util.Date; diff --git a/acs2/nladmin-system/nlsso-server/src/main/java/org/nl/acs/autoThread/service/dto/AutoRunDto.java b/acs2/nladmin-system/nlsso-server/src/main/java/org/nl/acs/autoThread/service/dto/AutoRunDto.java index 0cf1b13..c7f83d3 100644 --- a/acs2/nladmin-system/nlsso-server/src/main/java/org/nl/acs/autoThread/service/dto/AutoRunDto.java +++ b/acs2/nladmin-system/nlsso-server/src/main/java/org/nl/acs/autoThread/service/dto/AutoRunDto.java @@ -1,7 +1,7 @@ package org.nl.acs.autoThread.service.dto; import lombok.Data; -import org.nl.acs.autoThread.autoRunThread.service.enums.ThreadUsedStatusEnum; +import org.nl.acs.autoThread.service.enums.ThreadUsedStatusEnum; import org.nl.acs.autoThread.service.enums.ThreadStatusEnum; import java.util.Date; diff --git a/acs2/nladmin-system/nlsso-server/src/main/java/org/nl/acs/autoThread/service/enums/ThreadUsedStatusEnum.java b/acs2/nladmin-system/nlsso-server/src/main/java/org/nl/acs/autoThread/service/enums/ThreadUsedStatusEnum.java index b4c444f..a09db2f 100644 --- a/acs2/nladmin-system/nlsso-server/src/main/java/org/nl/acs/autoThread/service/enums/ThreadUsedStatusEnum.java +++ b/acs2/nladmin-system/nlsso-server/src/main/java/org/nl/acs/autoThread/service/enums/ThreadUsedStatusEnum.java @@ -1,4 +1,4 @@ -package org.nl.acs.autoThread.autoRunThread.service.enums; +package org.nl.acs.autoThread.service.enums; /** * Demo class * diff --git a/acs2/nladmin-system/nlsso-server/src/main/java/org/nl/acs/autoThread/service/impl/AutoRunServiceImpl.java b/acs2/nladmin-system/nlsso-server/src/main/java/org/nl/acs/autoThread/service/impl/AutoRunServiceImpl.java index 76fdfc6..11d4afd 100644 --- a/acs2/nladmin-system/nlsso-server/src/main/java/org/nl/acs/autoThread/service/impl/AutoRunServiceImpl.java +++ b/acs2/nladmin-system/nlsso-server/src/main/java/org/nl/acs/autoThread/service/impl/AutoRunServiceImpl.java @@ -7,7 +7,7 @@ import org.apache.commons.lang3.StringUtils; import org.nl.ApplicationAutoInitial; import org.nl.acs.autoThread.autoRun.AbstractAutoRunnable; import org.nl.acs.autoThread.StartConfig; -import org.nl.acs.autoThread.autoRunThread.service.enums.ThreadUsedStatusEnum; +import org.nl.acs.autoThread.service.enums.ThreadUsedStatusEnum; import org.nl.acs.autoThread.service.AutoRunService; import org.nl.acs.autoThread.service.dto.AutoRunDto; import org.nl.common.exception.BadRequestException; diff --git a/acs2/nladmin-system/nlsso-server/src/main/java/org/nl/acs/autoThread/初始化自动线程.md b/acs2/nladmin-system/nlsso-server/src/main/java/org/nl/acs/autoThread/初始化自动线程.md index 9958b15..bbecc62 100644 --- a/acs2/nladmin-system/nlsso-server/src/main/java/org/nl/acs/autoThread/初始化自动线程.md +++ b/acs2/nladmin-system/nlsso-server/src/main/java/org/nl/acs/autoThread/初始化自动线程.md @@ -1,15 +1,415 @@ -### appInit -项目启动时候对项目进行初始化,初始化器包括6项目 -1.AutoRunService :根据配置判断哪些自动线程需要启动 -2.自动线程继承AbstractAutoRunnable,目前系统一共有4个自动线程 -#### DeviceExecuteAutoRun -设备自动线程,完成每个设备驱动的加载 - 设备驱动通过while循环判断信号状态 -#### NDCAutoRun -NDC自动线程,通过该线程与NDC创建socket通讯,做phace逻辑交互 -该版本废弃:统一改agvKit交互 -#### DeviceOpcSynchronizeAutoRun - 同步所有OpcService信号, -该版本固定使用KEP-SERVER 进行信号同步 -#### SocketListenerAutoRun - socket同步监听测试类,在startconfig中配置不用 +# 初始化自动线程文档 + +## 1. 自动线程系统概述 + +自动线程系统是 NL Admin System 的核心组件,负责管理系统启动时自动运行的后台任务。采用 **模板方法模式** + **服务发现机制**,实现线程的自动注册、启动和管理。 + +### 1.1 整体架构 + +``` +┌─────────────────────────────────────────────────────────────┐ +│ 应用启动阶段 │ +├─────────────────────────────────────────────────────────────┤ +│ ApplicationAutoInitial (Spring自动初始化接口) │ +│ │ │ +│ ▼ │ +│ AutoRunServiceImpl.autoInitial() │ +│ │ │ +│ ├──► 扫描所有 AbstractAutoRunnable Bean │ +│ ├──► 过滤检查 (code空/重复/禁止列表) │ +│ ├──► 注册到 code_autoRun_Map │ +│ └──► 创建线程并启动 │ +│ │ │ +│ ▼ │ +│ ┌─────────────────────────────────────────────────────┐ │ +│ │ AbstractAutoRunnable.run() │ │ +│ │ ┌─────────────────────────────────────────────┐ │ │ +│ │ │ before() → autoRun() → after() │ │ │ +│ │ │ │ │ │ │ │ │ +│ │ │ 前置处理 核心循环 后置清理 │ │ │ +│ │ └─────────────────────────────────────────────┘ │ │ +│ └─────────────────────────────────────────────────────┘ │ +└─────────────────────────────────────────────────────────────┘ +``` + +### 1.2 核心组件 + +| 组件 | 职责 | 说明 | +|------|------|------| +| `ApplicationAutoInitial` | 自动初始化接口 | Spring启动时自动调用 `autoInitial()` | +| `AutoRunServiceImpl` | 线程管理器 | 负责线程的注册、启动、停止、状态查询 | +| `AbstractAutoRunnable` | 抽象基类 | 定义线程生命周期模板 | +| `StartConfig` | 启动配置 | 控制全局开关和禁止列表 | + +--- + +## 2. 初始化流程 + +### 2.1 启动流程详解 + +```java +// AutoRunServiceImpl.autoInitial() +@Override +public void autoInitial() throws Exception { + // 1. 全局开关检查 + if (StartConfig.Need_Stop) { + System.out.println("---未开启自动线程,AutoRun初始化终止---"); + return; // 全局禁止启动 + } + + // 2. 遍历所有AbstractAutoRunnable Bean + for (AbstractAutoRunnable autoRunnable : abstractAutoRunnableList) { + // 检查1: code不能为空 + if (StringUtils.isEmpty(autoRunnable.getCode())) { + autoRunnable.setMessage("code为空未加载"); + continue; + } + + // 检查2: code不能重复 + if (partialJudg.contains(autoRunnable.getCode())) { + autoRunnable.setMessage("code重复而未加载"); + continue; + } + + // 检查3: 不在禁止列表中 + if (StartConfig.AutoRun_Ban.contains(autoRunnable.getCode())) { + autoRunnable.setMessage("配置了禁止启动"); + continue; + } + + // 通过所有检查,注册到Map + autoRunnable.setUsedStatus(ThreadUsedStatusEnum.used); + code_autoRun_Map.put(autoRunnable.getCode(), autoRunnable); + } + + // 3. 启动所有已注册的线程 + for (AbstractAutoRunnable autoRunnable : code_autoRun_Map.values()) { + final Thread thread = new Thread(autoRunnable); + code_Thread_Map.put(autoRunnable.getCode(), thread); + thread.start(); + } +} +``` + +### 2.2 过滤规则 + +| 检查项 | 条件 | 处理方式 | +|--------|------|----------| +| **code为空** | `StringUtils.isEmpty(code)` | 跳过,记录消息 | +| **code重复** | `partialJudg.contains(code)` | 跳过,记录消息 | +| **在禁止列表** | `StartConfig.AutoRun_Ban.contains(code)` | 跳过,记录消息 | + +--- + +## 3. AbstractAutoRunnable 抽象基类 + +### 3.1 类结构 + +```java +@Data +public abstract class AbstractAutoRunnable implements Runnable { + private ThreadStatusEnum status; // 线程执行状态 (run/stop) + private Date startTime; // 线程开始时间 + private Date stopTime; // 线程结束时间 + private String message; // 状态消息 + private ThreadUsedStatusEnum usedStatus; // 初始化状态 (used/unUsed) + + @Override + public void run() { + this.setStatus(ThreadStatusEnum.run); + this.setStartTime(new Date()); + try { + this.setMessage("自动线程初始化中"); + this.before(); // 前置处理 + this.autoRun(); // 核心业务(子类实现死循环) + this.setMessage("自动线程正常启动"); + } catch (Throwable throwable) { + log.error(this.getCode() + "自动线程执行异常", throwable); + } finally { + this.setStopTime(new Date()); + this.setMessage("自动线程关闭"); + this.setStatus(ThreadStatusEnum.stop); + this.after(); // 后置清理 + } + } + + // 子类必须实现的方法 + public abstract String getCode(); // 返回线程唯一标识 + public abstract String getName(); // 返回线程名称 + public abstract void autoRun() throws Exception; // 核心业务逻辑 + + // 可选覆盖方法 + public void before() throws Exception {} // 前置处理 + public void after() {} // 后置清理 + public void stop() { this.after(); } // 停止线程 +} +``` + +### 3.2 生命周期方法 + +| 方法 | 调用时机 | 用途 | 是否必须实现 | +|------|----------|------|--------------| +| `before()` | `run()` 开始时 | 初始化资源、建立连接 | 否 | +| `autoRun()` | `before()` 之后 | 核心业务逻辑(死循环) | **是** | +| `after()` | `run()` 结束时 | 释放资源、清理 | 否 | +| `stop()` | 手动停止时 | 触发停止流程 | 否 | + +--- + +## 4. 启动配置 + +### 4.1 StartConfig 配置类 + +```java +public class StartConfig { + /** + * 全局开关:true=禁止所有自动线程启动,false=允许启动 + */ + public static Boolean Need_Stop = Boolean.TRUE; + + /** + * 禁止启动的线程code列表 + */ + public static List AutoRun_Ban = new ArrayList<>(); + + static { + AutoRun_Ban.add("TaskFeedbackAutoRun"); // 任务反馈线程(测试用) + AutoRun_Ban.add("SocketListenerAutoRun"); // Socket监听线程(测试用) + } +} +``` + +### 4.2 配置说明 + +| 配置项 | 类型 | 默认值 | 说明 | +|--------|------|--------|------| +| `Need_Stop` | Boolean | `true` | 全局开关,设为`false`时启动所有自动线程 | +| `AutoRun_Ban` | List | 包含2个元素 | 精确匹配线程code,匹配到的线程不会启动 | + +--- + +## 5. 现有自动线程 + +### 5.1 线程列表 + +| 线程类 | Code | 职责 | 状态 | +|--------|------|------|------| +| `DeviceExecuteAutoRunable` | `DeviceExecuteAutoRun` | 设备执行线程,执行所有设备驱动 | **运行中** | +| `OpcSignalSyncAutoRunnable` | `OpcSignalSyncAutoRun` | OPC信号同步线程 | **运行中** | +| `SocketListenerAutoRun` | `SocketListenerAutoRun` | Socket监听测试线程 | **已禁止** | + +### 5.2 DeviceExecuteAutoRunable + +**职责**:定期执行所有设备驱动的自动任务 + +```java +public class DeviceExecuteAutoRunable extends AbstractAutoRunnable { + @Override + public void autoRun() throws Exception { + // 等待OPC同步完成(最多60秒) + for (int i = 0; !OpcConfig.OPC_START_TAG; ++i) { + Thread.sleep(1000L); + if (i > 60) break; + } + + // 死循环执行设备任务 + while (true) { + Thread.sleep(100); + List deviceDrivers = deviceAppService.findDeviceDriver(BaseDeviceDriver.class); + for (BaseDeviceDriver driver : deviceDrivers) { + executorService.submit(() -> driver.executeAuto()); + } + } + } +} +``` + +### 5.3 OpcSignalSyncAutoRunnable + +**职责**:同步所有OPC服务器的信号到系统内存 + +```java +public class OpcSignalSyncAutoRunnable extends AbstractAutoRunnable { + @Override + public void autoRun() throws Exception { + // 同步OPC信号逻辑 + while (true) { + // 读取OPC服务器数据 + // 更新到统一数据访问器(UDW) + Thread.sleep(50); // 控制同步频率 + } + } +} +``` + +--- + +## 6. 线程管理 API + +### 6.1 AutoRunService 接口 + +```java +public interface AutoRunService { + /** + * 启动指定线程 + * @param code 线程唯一标识 + */ + void startThread(String code); + + /** + * 停止指定线程 + * @param code 线程唯一标识 + */ + void stopThread(String code); + + /** + * 获取所有线程状态 + * @return 线程状态列表 + */ + List findAll(); +} +``` + +### 6.2 使用示例 + +```java +// 注入线程服务 +@Autowired +private AutoRunService autoRunService; + +// 启动线程 +autoRunService.startThread("DeviceExecuteAutoRun"); + +// 停止线程 +autoRunService.stopThread("DeviceExecuteAutoRun"); + +// 查询所有线程状态 +List threads = autoRunService.findAll(); +for (AutoRunDto dto : threads) { + System.out.println("线程: " + dto.getName() + ", 状态: " + dto.getStatus()); +} +``` + +### 6.3 AutoRunDto 数据结构 + +| 字段 | 类型 | 说明 | +|------|------|------| +| `code` | String | 线程唯一标识 | +| `name` | String | 线程名称 | +| `status` | String | 执行状态 (run/stop) | +| `startTime` | Date | 启动时间 | +| `stopTime` | Date | 停止时间 | +| `message` | String | 状态消息 | +| `thread_alive` | Boolean | 线程是否存活 | +| `thread_name` | String | JVM线程名称 | +| `thread_id` | String | JVM线程ID | +| `thread_state` | String | JVM线程状态 | + +--- + +## 7. 扩展开发指南 + +### 7.1 创建新的自动线程 + +1. **继承 AbstractAutoRunnable** + +```java +@Component +public class MyCustomAutoRun extends AbstractAutoRunnable { + + @Override + public String getCode() { + return "MyCustomAutoRun"; // 唯一标识 + } + + @Override + public String getName() { + return "我的自定义线程"; + } + + @Override + public void autoRun() throws Exception { + // 死循环执行业务逻辑 + while (true) { + // 执行任务... + Thread.sleep(1000); // 控制执行频率 + } + } + + @Override + public void before() throws Exception { + // 初始化资源(可选) + super.before(); + } + + @Override + public void after() { + // 清理资源(可选) + super.after(); + } +} +``` + +2. **配置控制** + +```java +// 在 StartConfig 中配置(如需禁止启动) +static { + AutoRun_Ban.add("TaskFeedbackAutoRun"); + AutoRun_Ban.add("SocketListenerAutoRun"); + // AutoRun_Ban.add("MyCustomAutoRun"); // 如需禁止,取消注释 +} +``` + +### 7.2 注意事项 + +- **线程安全**:`autoRun()` 中的共享资源需要加锁保护 +- **异常处理**:建议在 `autoRun()` 内部捕获异常,避免线程意外终止 +- **退出机制**:建议使用 `volatile` 标志位控制循环退出 + +--- + +## 8. 关键设计要点 + +### 8.1 服务发现机制 + +Spring 自动扫描所有 `AbstractAutoRunnable` 子类: + +```java +@Autowired(required = false) +private List abstractAutoRunnableList; +``` + +### 8.2 线程安全 + +```java +@Override +public synchronized void startThread(String code) { ... } + +@Override +public synchronized void stopThread(String threadCode) { ... } +``` + +### 8.3 状态管理 + +使用枚举类管理状态: + +```java +// ThreadStatusEnum - 执行状态 +public enum ThreadStatusEnum { + run, // 运行中 + stop // 已停止 +} + +// ThreadUsedStatusEnum - 初始化状态 +public enum ThreadUsedStatusEnum { + used, // 已使用(已注册) + unUsed // 未使用(未注册) +} +``` + +--- + +**文档版本**: v1.0 +**创建日期**: 2026年4月 +**适用模块**: NL Admin System - 自动线程模块 diff --git a/acs2/nladmin-system/nlsso-server/src/main/java/org/nl/acs/device/device/设备信息.md b/acs2/nladmin-system/nlsso-server/src/main/java/org/nl/acs/device/device/设备信息.md index 9958b15..cedf77c 100644 --- a/acs2/nladmin-system/nlsso-server/src/main/java/org/nl/acs/device/device/设备信息.md +++ b/acs2/nladmin-system/nlsso-server/src/main/java/org/nl/acs/device/device/设备信息.md @@ -1,15 +1,509 @@ -### appInit -项目启动时候对项目进行初始化,初始化器包括6项目 -1.AutoRunService :根据配置判断哪些自动线程需要启动 -2.自动线程继承AbstractAutoRunnable,目前系统一共有4个自动线程 -#### DeviceExecuteAutoRun -设备自动线程,完成每个设备驱动的加载 - 设备驱动通过while循环判断信号状态 -#### NDCAutoRun -NDC自动线程,通过该线程与NDC创建socket通讯,做phace逻辑交互 -该版本废弃:统一改agvKit交互 -#### DeviceOpcSynchronizeAutoRun - 同步所有OpcService信号, -该版本固定使用KEP-SERVER 进行信号同步 -#### SocketListenerAutoRun - socket同步监听测试类,在startconfig中配置不用 +# 设备信息管理文档 + +## 1. 设备信息加载流程 + +### 1.1 整体架构 + +设备信息加载采用 **服务发现 + 驱动绑定** 的设计模式: + +``` +┌─────────────────────────────────────────────────────────────────┐ +│ 应用启动阶段 │ +├─────────────────────────────────────────────────────────────────┤ +│ ApplicationAutoInitial (自动初始化接口) │ +│ │ │ +│ ▼ │ +│ DeviceAppServiceImpl.autoInitial() │ +│ │ │ +│ ▼ │ +│ ┌─────────────────────────────────────────────────────────┐ │ +│ │ reload() 方法 │ │ +│ │ 1. 从数据库查询设备基本信息 (DeviceMapper) │ │ +│ │ 2. 关联设备扩展信息 (DeviceExtraMapper) │ │ +│ │ 3. 关联OPC/PLC配置 (OpcMapper/OpcPlcMapper) │ │ +│ │ 4. 绑定设备驱动 (DeviceDriverDefination) │ │ +│ └─────────────────────────────────────────────────────────┘ │ +│ │ │ +│ ▼ │ +│ ┌─────────────────────────────────────────────────────────┐ │ +│ │ 内存缓存 (devices List) │ │ +│ │ - devices: 设备对象列表 │ │ +│ │ - code_indexs: 设备编码索引 │ │ +│ └─────────────────────────────────────────────────────────┘ │ +└─────────────────────────────────────────────────────────────────┘ +``` + +### 1.2 核心代码流程 + +#### ① 自动初始化触发 + +```java +// DeviceAppServiceImpl.java +@Override +public void autoInitial() throws Exception { + log.info("加载所有设备"); + this.reload(); // 核心加载方法 +} +``` + +#### ② 设备信息查询与组装 + +```java +@Override +public synchronized void reload() { + // 1. 查询所有设备(包含扩展信息) + List deviceManageDtos = this.queryAllWithExtra(); + + // 2. 初始化内存缓存 + this.devices = new ArrayList(); + this.code_indexs = new ArrayList(); + + // 3. 遍历设备,创建Device对象并绑定驱动 + for (DeviceManageDto deviceManage : deviceManageDtos) { + Device device = new Device(); + BeanUtil.copyProperties(deviceManage, device); + this.devices.add(device); + this.code_indexs.add(device.getDevice_code()); + + // 4. 绑定设备驱动(关键步骤) + if (!StrUtil.isEmpty(device.getDriver_code())) { + DeviceDriverDefination defination = + deviceDriverDefinationAppService.getDeviceDriverDefination(deviceManage.getDriver_code()); + if (defination != null) { + device.setDeviceDriverDefination(defination); + DeviceDriver driver = defination.getDriverInstance(device); // 创建驱动实例 + device.setDeviceDriver(driver); + } + } + } +} +``` + +#### ③ 数据库查询逻辑 + +`queryAllWithExtra()` 方法执行以下查询: + +| 数据表 | 查询内容 | 用途 | +|--------|----------|------| +| `acs_device` | 设备基本信息 | 设备编码、名称、类型、地址等 | +| `acs_device_extra` | 设备扩展属性 | 站点管理、自定义配置等 | +| `acs_opc` | OPC服务器配置 | 设备通信连接信息 | +| `acs_opc_plc` | PLC配置 | 设备PLC连接信息 | + +### 1.3 设备驱动绑定机制 + +设备驱动采用 **策略模式**,通过 `DeviceDriverDefination` 动态创建驱动实例: + +```java +// DeviceDriverDefination.getDriverInstance(device) +// 根据驱动定义,为每个设备创建对应的驱动实例 +DeviceDriver driver = defination.getDriverInstance(device); +device.setDeviceDriver(driver); +``` + +## 2. 设备信息数据结构 + +### 2.1 Device 实体 + +```java +public class Device { + private String device_code; // 设备编码(唯一标识) + private String device_name; // 设备名称 + private String device_type; // 设备类型 + private String address; // 设备地址 + private String driver_code; // 驱动编码 + private String opc_server_id; // OPC服务器ID + private String opc_plc_id; // PLC ID + private DeviceDriver deviceDriver; // 设备驱动实例 + private DeviceDriverDefination deviceDriverDefination; // 驱动定义 +} +``` + +### 2.2 DeviceDriver 抽象类 + +```java +public abstract class DeviceDriver { + private Device device; // 关联设备 + private String deviceCode; // 设备编码 + private DeviceDriverDefination deviceDriverDefination; // 驱动定义 + private boolean stop; // 停止标志 + + public DeviceDriver init(Device device, DeviceDriverDefination defination) { + this.device = device; + this.deviceCode = device.getDevice_code(); + this.deviceDriverDefination = defination; + return this; + } +} +``` + +## 3. 设备信息使用方式 + +### 3.1 基础查询接口 + +```java +// DeviceAppService 提供的查询方法 + +// 1. 查询所有设备 +List devices = deviceAppService.findAllDevice(); + +// 2. 根据设备编码查询 +Device device = deviceAppService.findDeviceByCode("DEVICE001"); + +// 3. 根据设备地址查询 +Device device = deviceAppService.findDeviceByAddress("A01-01"); + +// 4. 根据设备类型查询 +List devices = deviceAppService.findDeviceByType(DeviceType.STANDARD_INSPECT); + +// 5. 获取设备驱动列表(按类型筛选) +List drivers = deviceAppService.findDeviceDriver(BaseDeviceDriver.class); +``` + +### 3.2 设备驱动调用 + +通过设备驱动执行具体业务操作: + +```java +// 获取设备驱动 +Device device = deviceAppService.findDeviceByCode("DEVICE001"); +DeviceDriver driver = device.getDeviceDriver(); + +// 执行设备操作(具体方法由子类实现) +if (driver instanceof BaseDeviceDriver) { + BaseDeviceDriver baseDriver = (BaseDeviceDriver) driver; + baseDriver.executeAuto(); // 执行自动任务 +} +``` + +### 3.3 自动线程调用 + +`DeviceExecuteAutoRunable` 自动线程定期执行设备任务: + +```java +// DeviceExecuteAutoRunable.autoRun() +public void autoRun() throws Exception { + // 等待OPC同步完成(最多60秒) + for (int i = 0; !OpcConfig.OPC_START_TAG; ++i) { + Thread.sleep(1000L); + if (i > 60) break; + } + + // 循环执行设备任务 + while (true) { + Thread.sleep(100); + List deviceDrivers = deviceAppService.findDeviceDriver(BaseDeviceDriver.class); + + for (BaseDeviceDriver driver : deviceDrivers) { + // 提交到线程池执行 + executorService.submit(() -> driver.executeAuto()); + } + } +} +``` + +### 3.4 设备驱动插件体系 + +设备驱动采用 **插件模式**,实现两个核心接口: + +| 接口 | 职责 | 核心方法 | +|------|------|----------| +| `DriverExecutePlugin` | 设备执行逻辑 | `execute()` - 执行具体业务 | +| `DeviceStageMonitorPlugin` | 设备状态监控 | `getDeviceStatusName()` - 获取状态 | + +```java +// DriverExecutePlugin 接口 +public interface DriverExecutePlugin { + default void executeAuto() { + try { + this.execute(); // 调用子类实现 + } catch (Exception ex) { + ex.printStackTrace(); + } + } + void execute() throws Exception; // 子类必须实现 +} + +// DeviceStageMonitorPlugin 接口 +public interface DeviceStageMonitorPlugin { + JSONObject getDeviceStatusName() throws Exception; // 获取设备状态 + void setDeviceStatus(JSONObject data); // 设置设备状态 +} +``` + +### 3.5 OPC设备驱动使用 + +`AbstractOpcDeviceDriver` 是 OPC 设备驱动的基类,提供以下核心能力: + +#### ① OPC信号读取 + +```java +// 获取设备驱动 +Device device = deviceAppService.findDeviceByCode("DEVICE001"); +AbstractOpcDeviceDriver opcDriver = (AbstractOpcDeviceDriver) device.getDeviceDriver(); + +// 读取单个OPC信号值 +Integer value = opcDriver.getIntegeregerValue("heartbeat"); // 获取心跳值 +Object rawValue = opcDriver.getValue("running"); // 获取原始值 + +// 获取所有OPC点位配置 +List opcItems = opcDriver.getOpcItems(); +for (OpcItemDto item : opcItems) { + String itemCode = item.getItem_code(); + String deviceCode = item.getDevice_code(); + // 处理OPC点位 +} +``` + +#### ② OPC信号写入 + +```java +// 构建要写入的信号数据 +Map itemValues = new HashMap<>(); +itemValues.put("opc_server.plc.device001.start", 1); // 启动信号 +itemValues.put("opc_server.plc.device001.speed", 100); // 速度设置 + +// 调用控制方法(带重试机制) +opcDriver.checkcontrol(itemValues); + +// 或者直接调用control方法 +boolean result = opcDriver.control(itemValues); +if (!result) { + throw new RuntimeException("OPC写入失败"); +} +``` + +#### ③ 获取设备扩展信息 + +```java +// 获取设备扩展配置 +Map extraValues = opcDriver.getExtraValue(); +String stationManager = (String) extraValues.get("station_manager"); + +// 获取设备连接信息 +String opcServer = opcDriver.getOpcServer(); // OPC服务器编码 +String opcPlc = opcDriver.getOpcPlc(); // PLC编码 +String deviceCode = opcDriver.getDeviceCode(); // 设备编码 +``` + +### 3.6 驱动执行上下文 + +每个驱动实例持有设备信息和统一数据访问器: + +```java +// 获取关联设备 +Device device = driver.getDevice(); + +// 获取驱动定义 +DeviceDriverDefination defination = driver.getDriverDefination(); + +// 获取统一数据访问器(用于读写OPC数据) +UnifiedDataAccessor opcUdw = opcDriver.getOpcValueAccessor(); +Object value = opcUdw.getValue("opc_server.plc.device001.status"); +``` + +## 4. 业务场景示例 + +### 4.1 场景一:设备状态监控 + +```java +// 查询所有设备状态 +List devices = deviceAppService.findAllDevice(); +for (Device device : devices) { + DeviceDriver driver = device.getDeviceDriver(); + if (driver instanceof AbstractOpcDeviceDriver) { + AbstractOpcDeviceDriver opcDriver = (AbstractOpcDeviceDriver) driver; + List opcItems = opcDriver.getOpcItems(); + // 处理OPC信号数据 + } + + // 获取设备状态名称(故障、联机等) + if (driver instanceof DeviceStageMonitorPlugin) { + DeviceStageMonitorPlugin monitor = (DeviceStageMonitorPlugin) driver; + JSONObject status = monitor.getDeviceStatusName(); + String statusName = status.getString("statusName"); + System.out.println("设备 " + device.getDevice_code() + " 状态: " + statusName); + } +} +``` + +### 4.2 场景二:设备指令执行 + +```java +// 根据设备编码执行指令 +String deviceCode = "CONVEYOR001"; +Device device = deviceAppService.findDeviceByCode(deviceCode); + +if (device != null && device.getDeviceDriver() instanceof BaseDeviceDriver) { + BaseDeviceDriver driver = (BaseDeviceDriver) device.getDeviceDriver(); + driver.executeAuto(); // 执行设备自动任务 +} +``` + +### 4.3 场景三:设备动态管理 + +```java +// 运行时添加设备 +deviceAppService.addDevice("NEW_DEVICE_001"); + +// 运行时移除设备 +deviceAppService.removeDevice("DEVICE_TO_REMOVE"); + +// 重新加载所有设备 +deviceAppService.reload(); +``` + +### 4.4 场景四:OPC信号批量写入 + +```java +// 批量控制多个设备 +List deviceCodes = Arrays.asList("CONVEYOR001", "CONVEYOR002", "CONVEYOR003"); + +for (String deviceCode : deviceCodes) { + Device device = deviceAppService.findDeviceByCode(deviceCode); + if (device != null && device.getDeviceDriver() instanceof AbstractOpcDeviceDriver) { + AbstractOpcDeviceDriver opcDriver = (AbstractOpcDeviceDriver) device.getDeviceDriver(); + + // 构建控制信号 + Map signals = new HashMap<>(); + signals.put(opcDriver.getItem("start"), 1); + signals.put(opcDriver.getItem("speed"), 50); + + // 执行写入(带重试机制) + try { + opcDriver.checkcontrol(signals); + log.info("设备 {} 控制成功", deviceCode); + } catch (Exception e) { + log.error("设备 {} 控制失败: {}", deviceCode, e.getMessage()); + } + } +} +``` + +### 4.5 场景五:设备状态同步 + +```java +// 从OPC读取设备状态并更新到系统 +String deviceCode = "LIFT001"; +Device device = deviceAppService.findDeviceByCode(deviceCode); + +if (device != null && device.getDeviceDriver() instanceof AbstractOpcDeviceDriver) { + AbstractOpcDeviceDriver opcDriver = (AbstractOpcDeviceDriver) device.getDeviceDriver(); + + // 读取多个信号 + Integer heartbeat = opcDriver.getIntegeregerValue("heartbeat"); + Integer running = opcDriver.getIntegeregerValue("running"); + Integer fault = opcDriver.getIntegeregerValue("fault"); + + // 构建状态数据 + JSONObject statusData = new JSONObject(); + statusData.put("deviceCode", deviceCode); + statusData.put("heartbeat", heartbeat); + statusData.put("running", running == 1); + statusData.put("fault", fault == 1); + + // 更新设备状态 + DeviceStageMonitorPlugin monitor = (DeviceStageMonitorPlugin) opcDriver; + monitor.setDeviceStatus(statusData); + + // 存储到数据库或缓存 + deviceStatusService.updateStatus(deviceCode, statusData); +} +``` + +### 4.6 场景六:自定义驱动执行 + +```java +// 创建自定义任务执行器 +public class CustomDeviceTask implements Runnable { + private BaseDeviceDriver driver; + + public CustomDeviceTask(BaseDeviceDriver driver) { + this.driver = driver; + } + + @Override + public void run() { + try { + // 执行前置检查 + if (!checkDeviceReady(driver)) { + log.warn("设备 {} 未就绪", driver.getDeviceCode()); + return; + } + + // 执行驱动逻辑 + driver.execute(); + + // 记录执行日志 + log.info("设备 {} 任务执行完成", driver.getDeviceCode()); + } catch (Exception e) { + log.error("设备 {} 任务执行失败: {}", driver.getDeviceCode(), e.getMessage()); + } + } + + private boolean checkDeviceReady(BaseDeviceDriver driver) { + // 检查设备状态、连接状态等 + return true; + } +} + +// 使用示例 +BaseDeviceDriver driver = deviceAppService.findDeviceDriver("CONVEYOR001"); +if (driver != null) { + executorService.submit(new CustomDeviceTask(driver)); +} +``` + +## 5. 关键设计要点 + +### 5.1 内存缓存策略 + +| 设计点 | 说明 | +|--------|------| +| **缓存时机** | 应用启动时加载,运行时动态更新 | +| **缓存结构** | `devices` 列表 + `code_indexs` 索引 | +| **线程安全** | 使用 `synchronized` 和 `Collections.synchronizedList()` | +| **更新机制** | 提供 `reload()`、`addDevice()`、`removeDevice()` 方法 | + +### 5.2 驱动解耦设计 + +- **策略模式**:通过 `DeviceDriverDefination` 解耦驱动定义与实现 +- **依赖注入**:Spring 自动扫描驱动实现类 +- **动态创建**:根据 `driver_code` 动态创建对应驱动实例 + +### 5.3 配置校验 + +在加载设备时进行多重校验: + +```java +// queryAllWithExtra() 中的校验逻辑 +if (opc == null) { + log.info("设备:{},配置的OPC数据源不存在", device_code); + continue; // 跳过无效配置 +} +if (opcPlc == null) { + log.info("设备:{},配置的PLC数据源不存在", device_code); + continue; // 跳过无效配置 +} +``` + +## 6. 扩展开发指南 + +### 6.1 添加新设备类型 + +1. 在数据库中添加设备记录(`acs_device` 表) +2. 配置设备扩展信息(`acs_device_extra` 表) +3. 配置 OPC/PLC 连接信息(`acs_opc`、`acs_opc_plc` 表) +4. 确保对应的驱动已注册 + +### 6.2 添加新设备驱动 + +1. 继承 `DeviceDriver` 抽象类 +2. 实现具体的业务逻辑方法 +3. 在 `DeviceDriverDefination` 中注册驱动 + +--- + +**文档版本**: v1.0 +**创建日期**: 2026年4月 +**适用模块**: NL Admin System - 设备管理模块 diff --git a/acs2/nladmin-system/nlsso-server/src/main/java/org/nl/acs/device/deviceProtocol/设备协议.md b/acs2/nladmin-system/nlsso-server/src/main/java/org/nl/acs/device/deviceProtocol/设备协议.md index df08fbc..6e26a35 100644 --- a/acs2/nladmin-system/nlsso-server/src/main/java/org/nl/acs/device/deviceProtocol/设备协议.md +++ b/acs2/nladmin-system/nlsso-server/src/main/java/org/nl/acs/device/deviceProtocol/设备协议.md @@ -1,7 +1,334 @@ -### storageMgt -货位管理模块:主要是AGV取放货的点 -1.货位编码与上位系统保持一致 -2.AGV地址有2个,正常逻辑取放地址是同一个,如果取放货位不一样需要配合adress1共同使用 -3.offset:货位偏移量:主要用于ACSKit套件对接,AGV到达取放货位时会请求取放货,这时通过offset偏移量来下发给kit -4.has:有无货状态,AGV取放货完成后更新该状态 +# 设备协议管理文档 +## 1. 设备协议概述 + +设备协议模块负责管理设备与PLC之间的通信协议配置,主要功能包括: + +- 设备协议信息查询 +- 协议数据导出(支持多种PLC类型) +- 协议格式校验与转换 + +### 1.1 协议数据结构 + +设备协议数据存储在 `acs_device_extra` 表中,核心字段: + +| 字段名 | 类型 | 说明 | +|--------|------|------| +| `device_id` | VARCHAR | 关联设备ID | +| `extra_code` | VARCHAR | 协议编码(格式:OPC_SERVER.PLC.DEVICE.CODE) | +| `extra_name` | VARCHAR | 协议名称(PLC地址,如 DB100.B1) | +| `extra_value` | VARCHAR | 协议值 | +| `filed_type` | VARCHAR | 字段类型(02-输入点位,03-输出点位) | + +### 1.2 协议编码格式 + +协议编码采用 **四级分层结构**: + +``` +OPC_SERVER_CODE.PLC_CODE.DEVICE_CODE.POINT_CODE + ↓ ↓ ↓ ↓ + RD1 RD1 A1 mode + (OPC服务器) (PLC编码) (设备编码) (点位编码) +``` + +示例:`RD1.RD1.A1.mode` + +--- + +## 2. 核心服务接口 + +### 2.1 DeviceProtocolService + +```java +public interface DeviceProtocolService { + + /** + * 分页查询设备协议 + * @param whereJson 查询条件 + * @param page 分页参数 + * @return 分页结果 + */ + Map queryDeviceProtocol(Map whereJson, Pageable page); + + /** + * 查询设备协议列表(不分页) + * @param whereJson 查询条件 + * @return 协议列表 + */ + JSONArray queryDeviceProtocol(Map whereJson); + + /** + * 导出设备DB协议(Excel格式) + * @param jsonarr 协议数据 + * @param response HTTP响应 + */ + void downDeviceDBload(JSONArray jsonarr, HttpServletResponse response) throws IOException; + + /** + * 导出Smart设备协议CSV + * @param queryDeviceProtocol 协议数据 + * @param response HTTP响应 + */ + void downDeviceDBloadSmartCSV(JSONArray queryDeviceProtocol, HttpServletResponse response); + + /** + * 导出FX5U设备协议CSV + * @param queryDeviceProtocol 协议数据 + * @param response HTTP响应 + */ + void downDeviceDBloadFX5UCSV(JSONArray queryDeviceProtocol, HttpServletResponse response); + + /** + * 导出通用CSV(西门子1200/1500) + * @param queryDeviceProtocol 协议数据 + * @param response HTTP响应 + */ + void downDeviceDBloadCSV(JSONArray queryDeviceProtocol, HttpServletResponse response); +} +``` + +### 2.2 查询条件说明 + +| 参数名 | 类型 | 说明 | +|--------|------|------| +| `device_types` | String | 设备类型筛选 | +| `blurry` | String | 模糊搜索(设备编码或名称) | +| `opc_id` | String | OPC服务器ID筛选 | + +--- + +## 3. 协议导出功能 + +### 3.1 支持的PLC类型 + +| PLC类型 | 导出方法 | 数据类型映射 | +|---------|----------|--------------| +| **西门子200 Smart** | `downDeviceDBloadSmartCSV()` | W→Word, D→DWord, 含"."→Boolean | +| **三菱FX5U** | `downDeviceDBloadFX5UCSV()` | D→Long, 含"."→Boolean | +| **西门子1200/1500** | `downDeviceDBloadCSV()` | DBx.By→Byte/Boolean, W→Word, D→DWord | + +### 3.2 CSV导出格式 + +导出的CSV文件包含以下列: + +| 列名 | 说明 | 示例 | +|------|------|------| +| **Tag Name** | 点位编码 | `mode` | +| **Address** | PLC地址 | `DB100.B1.0` | +| **Data Type** | 数据类型 | `Boolean` / `Word` / `DWord` | +| **Respect Data Type** | 保留字段 | `1` | +| **Client Access** | 访问权限 | `R/W` | +| **Scan Rate** | 扫描速率(ms) | `100` | + +### 3.3 协议格式校验 + +在导出前会进行格式校验: + +```java +// 校验协议编码格式(必须包含3个点) +int num = countStr(extra_code, "."); +if (num != 3) { + throw new BadRequestException("协议编码格式不正确: " + extra_code); +} +``` + +--- + +## 4. 数据类型映射规则 + +### 4.1 西门子200 Smart 映射规则 + +```java +String datatype = jo.getString("extra_name"); +if (!datatype.contains(".")) { + String[] split = datatype.split(""); + if (split[1].equals("W")) { + datatype = "Word"; // 如 VW100 → Word + } else if (split[1].equals("D")) { + datatype = "DWord"; // 如 VD100 → DWord + } +} else { + datatype = "Boolean"; // 如 I0.0 → Boolean +} +``` + +### 4.2 三菱FX5U 映射规则 + +```java +if (!datatype.contains(".")) { + String[] split = datatype.split(""); + if (split[1].equals("D")) { + datatype = "Long"; // 如 D100 → Long + } +} else { + datatype = "Boolean"; // 如 X0.0 → Boolean +} +``` + +### 4.3 西门子1200/1500 映射规则 + +```java +if (datatype.startsWith("DB")) { + String str1 = datatype.substring(0, datatype.indexOf(".")); + String headtype = datatype.substring(str1.length() + 1); + + if (headtype.startsWith("B")) { + if (headtype.contains(".")) { + datatype = "Boolean"; // DB100.B1.0 → Boolean + } else { + datatype = "Byte"; // DB100.B1 → Byte + } + } else if (headtype.startsWith("W")) { + datatype = "Word"; // DB100.W1 → Word + } else if (headtype.startsWith("D")) { + datatype = "DWord"; // DB100.D1 → DWord + } else if (headtype.startsWith("S")) { + datatype = "String"; // DB100.S1 → String + } else if (headtype.startsWith("REAL")) { + datatype = "REAL"; // DB100.REAL1 → REAL + } +} +``` + +--- + +## 5. 使用示例 + +### 5.1 查询设备协议 + +```java +// 构建查询条件 +Map whereJson = new HashMap<>(); +whereJson.put("device_types", "CONVEYOR"); // 筛选设备类型 +whereJson.put("blurry", "A1"); // 模糊搜索 +whereJson.put("opc_id", "OPC_SERVER_01"); // 筛选OPC服务器 + +// 分页查询 +Pageable page = PageRequest.of(0, 10); +Map result = deviceProtocolService.queryDeviceProtocol(whereJson, page); + +// 不分页查询 +JSONArray protocols = deviceProtocolService.queryDeviceProtocol(whereJson); +``` + +### 5.2 导出协议CSV + +```java +// 查询协议数据 +JSONArray protocols = deviceProtocolService.queryDeviceProtocol(whereJson); + +// 导出西门子1200/1500格式 +deviceProtocolService.downDeviceDBloadCSV(protocols, response); + +// 或者导出西门子200 Smart格式 +deviceProtocolService.downDeviceDBloadSmartCSV(protocols, response); + +// 或者导出三菱FX5U格式 +deviceProtocolService.downDeviceDBloadFX5UCSV(protocols, response); +``` + +### 5.3 导出协议Excel + +```java +JSONArray protocols = deviceProtocolService.queryDeviceProtocol(whereJson); +deviceProtocolService.downDeviceDBload(protocols, response); +``` + +--- + +## 6. 业务场景 + +### 6.1 场景一:批量导出PLC配置 + +```java +// 查询所有设备协议 +Map whereJson = new HashMap<>(); +JSONArray protocols = deviceProtocolService.queryDeviceProtocol(whereJson); + +// 根据PLC类型选择导出方法 +String plcType = "SIEMENS_1200"; +switch (plcType) { + case "SIEMENS_200SMART": + deviceProtocolService.downDeviceDBloadSmartCSV(protocols, response); + break; + case "MITSUBISHI_FX5U": + deviceProtocolService.downDeviceDBloadFX5UCSV(protocols, response); + break; + case "SIEMENS_1200": + case "SIEMENS_1500": + default: + deviceProtocolService.downDeviceDBloadCSV(protocols, response); + break; +} +``` + +### 6.2 场景二:按设备类型导出 + +```java +// 只导出特定类型设备的协议 +Map whereJson = new HashMap<>(); +whereJson.put("device_types", "LIFT"); // 只导出提升机设备 + +JSONArray protocols = deviceProtocolService.queryDeviceProtocol(whereJson); +deviceProtocolService.downDeviceDBloadCSV(protocols, response); +``` + +### 6.3 场景三:前端API调用 + +```javascript +// 查询协议列表 +fetch('/api/deviceProtocol', { + method: 'GET', + params: { + device_types: 'CONVEYOR', + blurry: 'A1' + } +}).then(res => res.json()) + .then(data => console.log(data)); + +// 导出CSV +window.open('/api/deviceProtocol/export?plcType=SIEMENS_1200'); +``` + +--- + +## 7. 关键设计要点 + +### 7.1 协议编码规范 + +协议编码必须遵循以下格式: + +``` +OPC_SERVER.PLC.DEVICE.POINT +``` + +**示例**: +- `RD1.RD1.A1.mode` - A1设备的mode点位 +- `OPC_SRV01.PLC01.CONV001.start` - CONV001设备的start点位 + +### 7.2 数据类型自动识别 + +系统根据协议名称自动识别数据类型: + +| 协议名称特征 | 识别的数据类型 | +|--------------|----------------| +| 包含 `.` 且以数字结尾 | Boolean(如 DB100.B1.0) | +| 以 `W` 开头(不含`.`) | Word(如 VW100) | +| 以 `D` 开头(不含`.`) | DWord 或 Long | +| 以 `DB` 开头 | 根据后续字符判断 | + +### 7.3 协议分类 + +根据 `filed_type` 字段区分协议类型: + +| filed_type | 含义 | 用途 | +|------------|------|------| +| `01` | 设备属性 | 设备基础配置(不参与协议导出) | +| `02` | 输入点位 | PLC输入信号(如传感器状态) | +| `03` | 输出点位 | PLC输出信号(如控制指令) | + +--- + +**文档版本**: v1.0 +**创建日期**: 2026年4月 +**适用模块**: NL Admin System - 设备协议模块 diff --git a/acs2/nladmin-system/nlsso-server/src/main/java/org/nl/项目启动初始化autoInitial.md b/acs2/nladmin-system/nlsso-server/src/main/java/org/nl/项目启动初始化autoInitial.md index 2c59273..a757340 100644 --- a/acs2/nladmin-system/nlsso-server/src/main/java/org/nl/项目启动初始化autoInitial.md +++ b/acs2/nladmin-system/nlsso-server/src/main/java/org/nl/项目启动初始化autoInitial.md @@ -11,10 +11,10 @@ 加载所有设备信息devices至本地内存 #### 3.StorageCellServiceImpl -加载货位信息至内存 + 加载货位信息至内存 #### NDCAutoRun -NDC自动线程,通过该线程与NDC创建socket通讯,做phace逻辑交互 -该版本废弃:统一改agvKit交互 + NDC自动线程,通过该线程与NDC创建socket通讯,做phace逻辑交互 + 该版本废弃:统一改agvKit交互 #### DeviceOpcSynchronizeAutoRun 同步所有OpcService信号, 该版本固定使用KEP-SERVER 进行信号同步 diff --git a/acs2/nladmin-ui/FE_DEVELOPMENT_GUIDE.md b/acs2/nladmin-ui/FE_DEVELOPMENT_GUIDE.md new file mode 100644 index 0000000..65ec91c --- /dev/null +++ b/acs2/nladmin-ui/FE_DEVELOPMENT_GUIDE.md @@ -0,0 +1,470 @@ +# 前端开发手册 + +## 1. 项目概述 + +### 1.1 项目简介 +本项目是 **EL-ADMIN** 前端管理系统,基于 Vue 2.x 技术栈构建,是一个功能完善的企业级后台管理系统。 + +### 1.2 技术栈 + +| 技术 | 版本 | 说明 | +|------|------|------| +| Vue | 2.6.10 | 前端框架 | +| Vue Router | 3.0.2 | 路由管理 | +| Vuex | 3.1.0 | 状态管理 | +| Element UI | 2.15.8 | UI组件库 | +| Axios | 0.18.1 | HTTP客户端 | +| ECharts | 4.2.1 | 图表库 | +| LogicFlow | 1.2.1 | 流程图编辑器 | + +### 1.3 主要功能模块 + +- **系统管理**:用户管理、角色管理、菜单管理、部门管理、数据字典等 +- **ACS管理**:设备管理、任务管理、区域管理、路线规划等 +- **布局管理**:地图管理、设备布局等 +- **监控管理**:日志监控、在线监控、大屏展示等 +- **轨迹管理**:轨迹配置、轨迹后台管理 + +--- + +## 2. 开发环境配置 + +### 2.1 环境要求 + +| 工具 | 最低版本 | 推荐版本 | +|------|----------|----------| +| Node.js | >= 8.9 | >= 14.x | +| npm | >= 3.0.0 | >= 6.x | + +### 2.2 安装依赖 + +```bash +# 进入项目目录 +cd d:\诺力git\stand_acs\acs2\nladmin-ui + +# 安装依赖 +npm install +``` + +### 2.3 开发命令 + +```bash +# 启动开发服务器 +npm run dev + +# 生产环境构建 +npm run build:prod + +# 测试环境构建 +npm run build:stage + +# ESLint代码检查 +npm run lint + +# 单元测试 +npm run test:unit + +# 生成新页面(模板) +npm run new +``` + +--- + +## 3. 项目结构 + +``` +src/ +├── api/ # API接口定义 +│ ├── acs/ # ACS模块接口 +│ ├── layout/ # 布局模块接口 +│ ├── system/ # 系统模块接口 +│ ├── monitor/ # 监控模块接口 +│ └── ... +├── assets/ # 静态资源 +│ ├── icons/ # SVG图标 +│ ├── images/ # 图片资源 +│ ├── styles/ # 全局样式 +│ └── js/ # 第三方JS库 +├── components/ # 公共组件 +│ ├── Crud/ # CRUD组件 +│ ├── Dict/ # 数据字典组件 +│ ├── Permission/ # 权限组件 +│ └── IconSelect/ # 图标选择组件 +├── layout/ # 布局组件 +│ ├── components/ # 布局子组件 +│ └── mixin/ # 布局混入 +├── router/ # 路由配置 +│ ├── index.js # 路由权限控制 +│ └── routers.js # 路由定义 +├── store/ # Vuex状态管理 +│ ├── modules/ # 状态模块 +│ ├── getters.js # 计算属性 +│ └── index.js # 状态入口 +├── utils/ # 工具函数 +│ ├── request.js # Axios封装 +│ ├── auth.js # 认证工具 +│ └── nladmin.js # 通用工具 +├── views/ # 页面视图 +│ ├── system/ # 系统管理页面 +│ ├── acs/ # ACS管理页面 +│ ├── monitor/ # 监控页面 +│ └── dashboard/ # 仪表盘 +├── i18n/ # 国际化配置 +├── App.vue # 根组件 +├── main.js # 入口文件 +└── settings.js # 系统配置 +``` + +--- + +## 4. 核心模块说明 + +### 4.1 入口文件 (main.js) + +**职责**:项目初始化、全局配置、第三方库注册 + +**关键配置**: +- Element UI 全局配置(默认尺寸、表格边框等) +- 全局方法挂载(parseTime、resetForm、selectDictLabel等) +- 国际化配置 + +### 4.2 路由管理 + +#### 4.2.1 路由定义 (router/routers.js) + +路由采用异步路由加载方式,支持动态菜单加载: + +```javascript +{ + path: '/system/user', + component: Layout, + redirect: '/system/user/index', + name: 'User', + meta: { title: 'user', icon: 'user', roles: ['admin', 'user'] }, + children: [ + { + path: 'index', + component: () => import('@/views/system/user/index'), + name: 'UserIndex', + meta: { title: 'userList', icon: 'user', roles: ['admin', 'user'] } + } + ] +} +``` + +#### 4.2.2 权限控制 (router/index.js) + +实现了完整的路由守卫机制: +- 登录状态检查 +- 动态菜单加载 +- 权限验证 + +### 4.3 HTTP请求封装 (utils/request.js) + +**核心特性**: +- 请求拦截器:自动添加Token、语言标识 +- 响应拦截器:统一错误处理、401/403状态码处理 +- 支持blob下载错误处理 + +```javascript +// 请求拦截器 +service.interceptors.request.use( + config => { + if (getToken()) { + config.headers['Authorization'] = 'Bearer ' + getToken() + } + config.headers['Accept-Language'] = localStorage.getItem('lang') + return config + } +) +``` + +### 4.4 状态管理 (store/) + +| 模块 | 职责 | +|------|------| +| app.js | 应用基础状态(侧边栏、设备信息等) | +| permission.js | 权限路由状态 | +| settings.js | 系统设置(主题、布局等) | +| tagsView.js | 标签页状态 | +| user.js | 用户信息状态 | + +--- + +## 5. 开发规范 + +### 5.1 代码规范 + +#### 5.1.1 ESLint规则 + +项目使用 `eslint-plugin-vue` 进行代码检查,配置文件:`.eslintrc.js` + +**核心规则**: +- 使用 4 空格缩进 +- 语句结尾必须有分号 +- 单引号优先 +- 禁止未使用的变量 +- Vue组件必须有name属性 + +#### 5.1.2 Git钩子 + +通过 `husky` 和 `lint-staged` 实现提交前代码检查: + +```javascript +"husky": { + "hooks": { + "pre-commit": "lint-staged" + } +}, +"lint-staged": { + "src/**/*.{js,vue}": [ + "eslint --fix", + "git add" + ] +} +``` + +### 5.2 组件开发规范 + +#### 5.2.1 组件命名 + +- **组件名**:大驼峰命名(PascalCase),如 `UserList.vue` +- **组件文件**:放在对应模块目录下 + +#### 5.2.2 组件结构 + +```vue + + + + + +``` + +### 5.3 API接口规范 + +#### 5.3.1 接口定义 + +每个模块对应一个API文件,统一放在 `src/api/` 目录下: + +```javascript +import request from '@/utils/request' + +export function getList(params) { + return request({ + url: '/api/system/user', + method: 'get', + params + }) +} +``` + +#### 5.3.2 接口命名 + +| 操作 | 方法 | 命名示例 | +|------|------|----------| +| 查询列表 | GET | `getList` | +| 查询详情 | GET | `getInfo` | +| 新增 | POST | `add` | +| 更新 | PUT | `update` | +| 删除 | DELETE | `delete` | + +### 5.4 国际化规范 + +项目支持中文、英文、印尼语三种语言,配置文件位于 `src/i18n/langs/` + +**使用方式**: + +```vue + + + +``` + +--- + +## 6. 常用工具函数 + +### 6.1 时间处理 + +```javascript +// 格式化时间 +this.parseTime(new Date(), '{y}-{m}-{d} {h}:{i}:{s}') +``` + +### 6.2 数据字典 + +```javascript +// 获取字典标签 +this.selectDictLabel(dictList, value) + +// 获取多个字典标签 +this.selectDictLabels(dictList, values, ',') +``` + +### 6.3 表单操作 + +```javascript +// 重置表单 +this.resetForm('formName') + +// 添加日期范围 +this.addDateRange(params, startDate, endDate) +``` + +### 6.4 树结构处理 + +```javascript +// 处理树形数据 +const treeData = this.handleTree(data, 'id', 'parentId') +``` + +--- + +## 7. CRUD开发指南 + +### 7.1 使用Crud组件 + +项目提供了通用的Crud组件,简化增删改查操作: + +```vue + + + +``` + +### 7.2 自定义表单 + +```vue + +``` + +--- + +## 8. 权限管理 + +### 8.1 按钮权限 + +使用 `v-permission` 指令控制按钮显示: + +```vue +编辑 +``` + +### 8.2 角色权限 + +路由配置中通过 `meta.roles` 控制访问权限: + +```javascript +{ + path: 'index', + component: () => import('@/views/system/user/index'), + meta: { roles: ['admin', 'user'] } +} +``` + +--- + +## 9. 构建部署 + +### 9.1 环境变量 + +| 文件 | 环境 | 说明 | +|------|------|------| +| .env.development | 开发环境 | 本地开发配置 | +| .env.production | 生产环境 | 生产部署配置 | + +### 9.2 构建命令 + +```bash +# 生产构建 +npm run build:prod + +# 构建产物目录:dist/ +``` + +### 9.3 部署方式 + +1. 将 `dist/` 目录部署到静态服务器 +2. 配置Nginx反向代理: + +```nginx +server { + listen 80; + server_name your-domain.com; + + location / { + root /path/to/dist; + index index.html; + try_files $uri $uri/ /index.html; + } + + location /api { + proxy_pass http://backend-server:8080; + proxy_set_header Host $host; + } +} +``` + +--- \ No newline at end of file