修正部分业务逻辑
This commit is contained in:
parent
deafe6dc63
commit
7815cac8a5
6
pom.xml
6
pom.xml
@ -306,12 +306,6 @@
|
||||
<version>${hutool-all.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<version>${springboottest.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>javax.servlet</groupId>
|
||||
<artifactId>javax.servlet-api</artifactId>
|
||||
|
133
sql/Device.sql
133
sql/Device.sql
@ -1,17 +1,20 @@
|
||||
-- 创建数据库
|
||||
CREATE DATABASE IF NOT EXISTS `storm-device`;
|
||||
CREATE DATABASE IF NOT EXISTS `storm-device`
|
||||
DEFAULT CHARACTER SET utf8mb4
|
||||
COLLATE utf8mb4_unicode_ci;
|
||||
|
||||
USE `storm-device`;
|
||||
SET NAMES utf8mb4;
|
||||
|
||||
-- 设备表(主表)
|
||||
-- 设备表修改
|
||||
CREATE TABLE device (
|
||||
id BIGINT(20) NOT NULL AUTO_INCREMENT COMMENT '主键ID',
|
||||
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '主键ID',
|
||||
device_id VARCHAR(255) NOT NULL COMMENT '设备ID',
|
||||
device_name VARCHAR(255) DEFAULT NULL COMMENT '设备名称',
|
||||
product_id VARCHAR(255) DEFAULT NULL COMMENT '产品ID',
|
||||
product_name VARCHAR(255) DEFAULT NULL COMMENT '产品名称',
|
||||
device_type INT DEFAULT NULL COMMENT '设备类型(1:RS-LL-X1, 2:RS-LL-X2)',
|
||||
STATUS VARCHAR(50) DEFAULT 'OFFLINE' COMMENT '设备在线状态(ONLINE/OFFLINE)',
|
||||
status VARCHAR(50) DEFAULT 'OFFLINE' COMMENT '设备在线状态(ONLINE/OFFLINE)',
|
||||
|
||||
-- 位置信息
|
||||
location VARCHAR(255) DEFAULT NULL COMMENT '安装位置',
|
||||
@ -27,13 +30,13 @@ CREATE TABLE device (
|
||||
activation_code VARCHAR(255) DEFAULT NULL COMMENT '激活码',
|
||||
serial_number VARCHAR(255) DEFAULT NULL COMMENT '序列号',
|
||||
license_status VARCHAR(50) DEFAULT NULL COMMENT '许可证状态',
|
||||
license_expire_time DATETIME COMMENT '许可证过期时间',
|
||||
last_massage_time DATETIME COMMENT '最后消息时间',
|
||||
license_expire_time DATETIME DEFAULT NULL COMMENT '许可证过期时间',
|
||||
last_massage_time DATETIME DEFAULT NULL COMMENT '最后按摩时间',
|
||||
|
||||
-- 在线状态时间记录
|
||||
last_online_time DATETIME COMMENT '最后上线时间',
|
||||
last_offline_time DATETIME COMMENT '最后下线时间',
|
||||
activation_time DATETIME COMMENT '激活时间',
|
||||
last_online_time DATETIME DEFAULT NULL COMMENT '最后上线时间',
|
||||
last_offline_time DATETIME DEFAULT NULL COMMENT '最后下线时间',
|
||||
activation_time DATETIME DEFAULT NULL COMMENT '激活时间',
|
||||
|
||||
-- 运行时长统计(秒)
|
||||
total_online_duration BIGINT DEFAULT 0 COMMENT '累计在线时长(秒)',
|
||||
@ -46,29 +49,28 @@ CREATE TABLE device (
|
||||
remark TEXT COMMENT '备注',
|
||||
|
||||
-- 系统字段
|
||||
is_deleted TINYINT(1) DEFAULT '0' COMMENT '是否删除(0-未删除, 1-已删除)',
|
||||
is_deleted TINYINT(1) DEFAULT 0 COMMENT '是否删除(0-未删除, 1-已删除)',
|
||||
create_by VARCHAR(255) DEFAULT NULL COMMENT '创建人',
|
||||
update_by VARCHAR(255) DEFAULT NULL COMMENT '更新人',
|
||||
create_time DATETIME DEFAULT NULL COMMENT '创建时间',
|
||||
update_time DATETIME DEFAULT NULL COMMENT '更新时间',
|
||||
create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE KEY uk_device_id (device_id),
|
||||
KEY idx_device_id (device_id),
|
||||
KEY idx_product_id (product_id),
|
||||
KEY idx_shop_id (shop_id),
|
||||
KEY idx_status (STATUS),
|
||||
KEY idx_create_time (create_time),
|
||||
KEY idx_status (status),
|
||||
KEY idx_license_status (license_status),
|
||||
KEY idx_last_online_time (last_online_time)
|
||||
KEY idx_last_online_time (last_online_time),
|
||||
KEY idx_create_time (create_time)
|
||||
) ENGINE=INNODB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='设备表';
|
||||
|
||||
-- 设备状态日志表
|
||||
-- 设备状态日志表修改
|
||||
CREATE TABLE device_status_log (
|
||||
id BIGINT(20) NOT NULL AUTO_INCREMENT COMMENT '主键ID',
|
||||
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '主键ID',
|
||||
device_id VARCHAR(255) NOT NULL COMMENT '设备ID',
|
||||
STATUS VARCHAR(255) DEFAULT NULL COMMENT '状态',
|
||||
event_type VARCHAR(255) DEFAULT NULL COMMENT '事件类型',
|
||||
status VARCHAR(50) DEFAULT NULL COMMENT '状态',
|
||||
event_type VARCHAR(100) DEFAULT NULL COMMENT '事件类型',
|
||||
|
||||
-- 毫秒时间戳字段
|
||||
massage_start_time BIGINT DEFAULT NULL COMMENT '按摩开始时间(毫秒时间戳)',
|
||||
@ -76,39 +78,44 @@ CREATE TABLE device_status_log (
|
||||
massage_duration BIGINT DEFAULT NULL COMMENT '按摩持续时间(毫秒)',
|
||||
|
||||
-- 按摩相关信息
|
||||
head_type VARCHAR(255) DEFAULT NULL COMMENT '按摩头类型',
|
||||
body_part VARCHAR(255) DEFAULT NULL COMMENT '身体部位',
|
||||
head_type VARCHAR(100) DEFAULT NULL COMMENT '按摩头类型',
|
||||
body_part VARCHAR(100) DEFAULT NULL COMMENT '身体部位',
|
||||
massage_plan VARCHAR(255) DEFAULT NULL COMMENT '按摩方案',
|
||||
|
||||
-- 时间字段
|
||||
event_time DATETIME DEFAULT NULL COMMENT '事件时间',
|
||||
last_online_time DATETIME DEFAULT NULL COMMENT '最后在线时间',
|
||||
duration INT(11) DEFAULT NULL COMMENT '持续时间(秒)',
|
||||
duration INT DEFAULT NULL COMMENT '持续时间(秒)',
|
||||
|
||||
-- 描述和备注
|
||||
description TEXT COMMENT '描述',
|
||||
remark TEXT COMMENT '备注',
|
||||
|
||||
-- 系统字段
|
||||
is_deleted TINYINT(1) DEFAULT '0' COMMENT '是否删除(0-未删除, 1-已删除)',
|
||||
is_deleted TINYINT(1) DEFAULT 0 COMMENT '是否删除(0-未删除, 1-已删除)',
|
||||
create_by VARCHAR(255) DEFAULT NULL COMMENT '创建人',
|
||||
update_by VARCHAR(255) DEFAULT NULL COMMENT '更新人',
|
||||
create_time DATETIME DEFAULT NULL COMMENT '创建时间',
|
||||
update_time DATETIME DEFAULT NULL COMMENT '更新时间',
|
||||
create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE KEY uk_device_event (device_id, event_time, STATUS),
|
||||
UNIQUE KEY uk_device_event_time (device_id, event_time, status, event_type),
|
||||
KEY idx_device_id (device_id),
|
||||
KEY idx_event_time (event_time),
|
||||
KEY idx_status (STATUS),
|
||||
KEY idx_status (status),
|
||||
KEY idx_head_type (head_type),
|
||||
KEY idx_event_type (event_type)
|
||||
KEY idx_event_type (event_type),
|
||||
KEY idx_create_time (create_time)
|
||||
) ENGINE=INNODB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='设备状态日志表';
|
||||
|
||||
-- 按摩任务表
|
||||
-- 按摩任务表修改
|
||||
CREATE TABLE massage_task (
|
||||
id BIGINT(20) NOT NULL AUTO_INCREMENT COMMENT '主键ID',
|
||||
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '主键ID',
|
||||
device_id VARCHAR(255) NOT NULL COMMENT '设备ID',
|
||||
device_name VARCHAR(255) DEFAULT NULL COMMENT '设备名称',
|
||||
product_name VARCHAR(255) DEFAULT NULL COMMENT '产品名称',
|
||||
head_type VARCHAR(255) DEFAULT NULL COMMENT '按摩头类型',
|
||||
body_part VARCHAR(255) DEFAULT NULL COMMENT '身体部位',
|
||||
head_type VARCHAR(100) DEFAULT NULL COMMENT '按摩头类型',
|
||||
body_part VARCHAR(100) DEFAULT NULL COMMENT '身体部位',
|
||||
massage_plan VARCHAR(255) DEFAULT NULL COMMENT '按摩方案',
|
||||
|
||||
-- 创建人
|
||||
@ -120,16 +127,20 @@ CREATE TABLE massage_task (
|
||||
task_time BIGINT DEFAULT NULL COMMENT '任务时长(毫秒)',
|
||||
create_timestamp BIGINT DEFAULT NULL COMMENT '创建时间戳(毫秒)',
|
||||
|
||||
-- 描述和备注
|
||||
description TEXT COMMENT '描述',
|
||||
remark TEXT COMMENT '备注',
|
||||
|
||||
-- 时间字段
|
||||
create_time DATETIME DEFAULT NULL COMMENT '创建时间',
|
||||
create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
|
||||
-- 系统字段
|
||||
is_deleted TINYINT(1) DEFAULT '0' COMMENT '是否删除(0-未删除, 1-已删除)',
|
||||
is_deleted TINYINT(1) DEFAULT 0 COMMENT '是否删除(0-未删除, 1-已删除)',
|
||||
update_by VARCHAR(255) DEFAULT NULL COMMENT '更新人',
|
||||
update_time DATETIME DEFAULT NULL COMMENT '更新时间',
|
||||
update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE KEY uk_device_task (device_id, start_time, head_type, body_part),
|
||||
UNIQUE KEY uk_device_task_time (device_id, start_time, head_type, body_part),
|
||||
KEY idx_device_id (device_id),
|
||||
KEY idx_start_time (start_time),
|
||||
KEY idx_head_type (head_type),
|
||||
@ -137,9 +148,9 @@ CREATE TABLE massage_task (
|
||||
KEY idx_create_time (create_time)
|
||||
) ENGINE=INNODB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='按摩任务表';
|
||||
|
||||
-- 设备运行时长统计表
|
||||
-- 设备运行时长统计表修改
|
||||
CREATE TABLE device_runtime_stats (
|
||||
id BIGINT AUTO_INCREMENT PRIMARY KEY COMMENT '主键ID',
|
||||
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '主键ID',
|
||||
device_id VARCHAR(255) NOT NULL COMMENT '设备唯一标识',
|
||||
stat_date DATE NOT NULL COMMENT '统计日期',
|
||||
daily_duration BIGINT DEFAULT 0 COMMENT '日运行时长(秒)',
|
||||
@ -147,21 +158,27 @@ CREATE TABLE device_runtime_stats (
|
||||
monthly_duration BIGINT DEFAULT 0 COMMENT '月运行时长(秒)',
|
||||
online_count INT DEFAULT 0 COMMENT '上线次数',
|
||||
|
||||
-- 描述和备注
|
||||
description TEXT COMMENT '描述',
|
||||
remark TEXT COMMENT '备注',
|
||||
|
||||
-- 系统字段
|
||||
is_deleted TINYINT(1) DEFAULT '0' COMMENT '是否删除(0-未删除, 1-已删除)',
|
||||
is_deleted TINYINT(1) DEFAULT 0 COMMENT '是否删除(0-未删除, 1-已删除)',
|
||||
create_by VARCHAR(255) DEFAULT NULL COMMENT '创建人',
|
||||
update_by VARCHAR(255) DEFAULT NULL COMMENT '更新人',
|
||||
create_time DATETIME DEFAULT NULL COMMENT '创建时间',
|
||||
update_time DATETIME DEFAULT NULL COMMENT '更新时间',
|
||||
create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
|
||||
UNIQUE KEY uk_device_date (device_id, stat_date),
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE KEY uk_device_stat_date (device_id, stat_date),
|
||||
KEY idx_stat_date (stat_date),
|
||||
KEY idx_device_id (device_id)
|
||||
KEY idx_device_id (device_id),
|
||||
KEY idx_create_time (create_time)
|
||||
) ENGINE=INNODB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='设备运行时长统计表';
|
||||
|
||||
-- 设备按头使用统计表
|
||||
-- 设备按头使用统计表修改
|
||||
CREATE TABLE device_head_usage (
|
||||
id BIGINT AUTO_INCREMENT PRIMARY KEY COMMENT '主键ID',
|
||||
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '主键ID',
|
||||
device_id VARCHAR(255) NOT NULL COMMENT '设备唯一标识',
|
||||
head_type VARCHAR(100) NOT NULL COMMENT '按头类型',
|
||||
usage_count INT DEFAULT 0 COMMENT '使用次数',
|
||||
@ -169,23 +186,23 @@ CREATE TABLE device_head_usage (
|
||||
daily_duration BIGINT DEFAULT 0 COMMENT '今日使用时长(秒)',
|
||||
weekly_duration BIGINT DEFAULT 0 COMMENT '本周使用时长(秒)',
|
||||
monthly_duration BIGINT DEFAULT 0 COMMENT '本月使用时长(秒)',
|
||||
last_used_time DATETIME COMMENT '最后使用时间',
|
||||
last_used_time DATETIME DEFAULT NULL COMMENT '最后使用时间',
|
||||
|
||||
-- 描述和备注
|
||||
description TEXT COMMENT '描述',
|
||||
remark TEXT COMMENT '备注',
|
||||
|
||||
-- 系统字段
|
||||
is_deleted TINYINT(1) DEFAULT '0' COMMENT '是否删除(0-未删除, 1-已删除)',
|
||||
is_deleted TINYINT(1) DEFAULT 0 COMMENT '是否删除(0-未删除, 1-已删除)',
|
||||
create_by VARCHAR(255) DEFAULT NULL COMMENT '创建人',
|
||||
update_by VARCHAR(255) DEFAULT NULL COMMENT '更新人',
|
||||
create_time DATETIME DEFAULT NULL COMMENT '创建时间',
|
||||
update_time DATETIME DEFAULT NULL COMMENT '更新时间',
|
||||
create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
|
||||
UNIQUE KEY uk_device_head (device_id, head_type),
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE KEY uk_device_head_type (device_id, head_type),
|
||||
KEY idx_head_type (head_type),
|
||||
KEY idx_last_used_time (last_used_time),
|
||||
KEY idx_device_id (device_id)
|
||||
KEY idx_device_id (device_id),
|
||||
KEY idx_create_time (create_time)
|
||||
) ENGINE=INNODB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='设备按头使用统计表';
|
||||
|
||||
-- 如果业务允许,可以考虑调整唯一键约束,增加时间容差
|
||||
-- 比如允许同一设备在同一分钟内只有一条相同状态的记录
|
||||
ALTER TABLE device_status_log
|
||||
DROP INDEX uk_device_event,
|
||||
ADD UNIQUE KEY uk_device_event_minute (device_id, DATE_FORMAT(event_time, '%Y-%m-%d %H:%i'), status);
|
140
sql/storm-AfterSales.sql
Normal file
140
sql/storm-AfterSales.sql
Normal file
@ -0,0 +1,140 @@
|
||||
CREATE DATABASE `storm-AfterSales`
|
||||
|
||||
USE `storm-AfterSales`
|
||||
|
||||
CREATE TABLE sys_work_order_type (
|
||||
type_id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '类型ID',
|
||||
type_name VARCHAR(50) NOT NULL COMMENT '类型名称(操作习惯、部件维修、设备巡厂维修、产品建议、投诉)',
|
||||
type_key VARCHAR(50) NOT NULL COMMENT '类型键值',
|
||||
STATUS CHAR(1) DEFAULT '0' COMMENT '状态(0正常 1停用)',
|
||||
order_num INT DEFAULT 0 COMMENT '显示顺序',
|
||||
create_by VARCHAR(64) DEFAULT '' COMMENT '创建者',
|
||||
create_time DATETIME COMMENT '创建时间',
|
||||
update_by VARCHAR(64) DEFAULT '' COMMENT '更新者',
|
||||
update_time DATETIME COMMENT '更新时间',
|
||||
remark VARCHAR(500) DEFAULT NULL COMMENT '备注',
|
||||
UNIQUE KEY uk_type_key (type_key)
|
||||
) ENGINE=INNODB COMMENT='工单类型表';
|
||||
|
||||
CREATE TABLE sys_work_order (
|
||||
order_id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '工单ID',
|
||||
order_no VARCHAR(50) UNIQUE NOT NULL COMMENT '工单编号',
|
||||
order_title VARCHAR(200) NOT NULL COMMENT '工单标题',
|
||||
order_type VARCHAR(50) COMMENT '工单类型(对应type_key)',
|
||||
order_status VARCHAR(20) DEFAULT 'pending' COMMENT '工单状态(pending, processing, resolved, closed, cancelled)',
|
||||
priority VARCHAR(10) DEFAULT 'medium' COMMENT '优先级(low, medium, high, urgent)',
|
||||
|
||||
-- 客户信息
|
||||
customer_id BIGINT COMMENT '客户ID',
|
||||
customer_name VARCHAR(100) COMMENT '客户姓名',
|
||||
customer_contact VARCHAR(50) COMMENT '客户联系方式',
|
||||
store_address VARCHAR(200) COMMENT '门店地址',
|
||||
|
||||
-- 设备信息
|
||||
device_name VARCHAR(100) COMMENT '设备名称',
|
||||
device_serial_no VARCHAR(100) COMMENT '设备序列号',
|
||||
device_model VARCHAR(100) COMMENT '设备型号',
|
||||
production_date DATE COMMENT '生产日期',
|
||||
|
||||
-- 问题描述(多媒体支持)
|
||||
problem_description TEXT COMMENT '问题描述',
|
||||
attachment_urls JSON COMMENT '附件URL(图片/视频) - 支持多个',
|
||||
problem_category VARCHAR(50) COMMENT '问题分类(数量问题、质量问题、软件故障等)',
|
||||
|
||||
-- 处理信息
|
||||
assignee_id BIGINT COMMENT '处理人ID',
|
||||
assignee_name VARCHAR(100) COMMENT '处理人姓名',
|
||||
assign_time DATETIME COMMENT '分配时间',
|
||||
current_handler_id BIGINT COMMENT '当前处理人ID',
|
||||
current_handler_name VARCHAR(100) COMMENT '当前处理人姓名',
|
||||
|
||||
-- 进度管理(结构化)
|
||||
root_cause TEXT COMMENT '原因定位',
|
||||
solution_plan TEXT COMMENT '处理方案',
|
||||
final_solution TEXT COMMENT '最终解决方案',
|
||||
|
||||
-- 物流信息(图片1明确需求)
|
||||
logistics_number VARCHAR(100) COMMENT '物流单号',
|
||||
logistics_progress VARCHAR(500) COMMENT '物流进度',
|
||||
|
||||
-- 客户反馈
|
||||
customer_feedback TEXT COMMENT '客户反馈',
|
||||
satisfaction_level INT COMMENT '满意度(1-5)',
|
||||
feedback_type VARCHAR(20) COMMENT '反馈类型(praise, complaint, suggestion)',
|
||||
|
||||
-- 时间管理
|
||||
plan_finish_time DATETIME COMMENT '计划完成时间',
|
||||
actual_finish_time DATETIME COMMENT '实际完成时间',
|
||||
response_deadline DATETIME COMMENT '响应截止时间(30分钟规则)',
|
||||
|
||||
-- 来源渠道
|
||||
source_channel VARCHAR(20) DEFAULT 'feishu' COMMENT '来源渠道(feishu, phone, wechat, app, web)',
|
||||
feishu_chat_id VARCHAR(100) COMMENT '飞书会话ID',
|
||||
|
||||
-- 若依标准字段
|
||||
create_by VARCHAR(64) DEFAULT '' COMMENT '创建者',
|
||||
create_time DATETIME COMMENT '创建时间',
|
||||
update_by VARCHAR(64) DEFAULT '' COMMENT '更新者',
|
||||
update_time DATETIME COMMENT '更新时间',
|
||||
remark VARCHAR(500) DEFAULT NULL COMMENT '备注',
|
||||
|
||||
-- 索引优化
|
||||
INDEX idx_order_no (order_no),
|
||||
INDEX idx_status (order_status),
|
||||
INDEX idx_assignee (assignee_id),
|
||||
INDEX idx_customer (customer_id),
|
||||
INDEX idx_create_time (create_time),
|
||||
INDEX idx_device_serial (device_serial_no),
|
||||
INDEX idx_logistics (logistics_number)
|
||||
) ENGINE=INNODB COMMENT='工单主表';
|
||||
|
||||
-- 工单处理记录表(增强进度跟踪)
|
||||
CREATE TABLE sys_work_order_record (
|
||||
record_id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '记录ID',
|
||||
order_id BIGINT NOT NULL COMMENT '工单ID',
|
||||
process_type VARCHAR(50) COMMENT '处理类型(create, assign, process, feedback, close, visit等)',
|
||||
process_content TEXT COMMENT '处理内容',
|
||||
|
||||
-- 结构化进度信息
|
||||
progress_type VARCHAR(50) COMMENT '进度类型(logistics, repair, replacement, optimization, complaint)',
|
||||
progress_value VARCHAR(200) COMMENT '进度值',
|
||||
progress_percent INT COMMENT '进度百分比',
|
||||
|
||||
process_user_id BIGINT COMMENT '处理人ID',
|
||||
process_user_name VARCHAR(100) COMMENT '处理人姓名',
|
||||
process_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '处理时间',
|
||||
attachment_url VARCHAR(500) COMMENT '进度附件',
|
||||
|
||||
INDEX idx_order_id (order_id),
|
||||
INDEX idx_process_time (process_time),
|
||||
INDEX idx_progress_type (progress_type)
|
||||
) ENGINE=INNODB COMMENT='工单处理记录表';
|
||||
|
||||
-- 工单聊天关联表(支持多渠道)
|
||||
CREATE TABLE sys_work_order_chat_rel (
|
||||
id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '主键',
|
||||
order_id BIGINT NOT NULL COMMENT '工单ID',
|
||||
chat_session_id VARCHAR(100) COMMENT '聊天会话ID',
|
||||
chat_platform VARCHAR(20) DEFAULT 'feishu' COMMENT '平台(feishu, wechat, phone)',
|
||||
message_id VARCHAR(100) COMMENT '消息ID',
|
||||
chat_content TEXT COMMENT '聊天内容摘要',
|
||||
chat_duration INT COMMENT '通话时长(秒)',
|
||||
create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
|
||||
INDEX idx_order_id (order_id),
|
||||
INDEX idx_chat_session (chat_session_id),
|
||||
INDEX idx_platform (chat_platform)
|
||||
) ENGINE=INNODB COMMENT='工单聊天关联表';
|
||||
|
||||
-- 新增:客服知识库关联表
|
||||
CREATE TABLE sys_knowledge_rel (
|
||||
id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '主键',
|
||||
order_id BIGINT NOT NULL COMMENT '工单ID',
|
||||
knowledge_id BIGINT COMMENT '知识库文章ID',
|
||||
knowledge_title VARCHAR(200) COMMENT '知识标题',
|
||||
used_count INT DEFAULT 1 COMMENT '使用次数',
|
||||
create_time DATETIME COMMENT '关联时间',
|
||||
|
||||
INDEX idx_order_id (order_id),
|
||||
INDEX idx_knowledge (knowledge_id)
|
||||
) ENGINE=INNODB COMMENT='工单知识库关联表';
|
111
storm-afterSales/pom.xml
Normal file
111
storm-afterSales/pom.xml
Normal file
@ -0,0 +1,111 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>com.storm</groupId>
|
||||
<artifactId>storm</artifactId>
|
||||
<version>3.6.6</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>storm-afterSales</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<name>storm-afterSales</name>
|
||||
<description>storm-afterSales工单管理</description>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>17</maven.compiler.source>
|
||||
<maven.compiler.target>17</maven.compiler.target>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<!-- SpringCloud Alibaba Nacos -->
|
||||
<dependency>
|
||||
<groupId>com.alibaba.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
|
||||
<version>2.2.6.RELEASE</version>
|
||||
</dependency>
|
||||
|
||||
<!-- SpringCloud Alibaba Nacos Config -->
|
||||
<dependency>
|
||||
<groupId>com.alibaba.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
|
||||
<version>2.2.6.RELEASE</version>
|
||||
</dependency>
|
||||
|
||||
<!-- SpringCloud Alibaba Sentinel -->
|
||||
<dependency>
|
||||
<groupId>com.alibaba.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- storm Common -->
|
||||
<dependency>
|
||||
<groupId>com.storm</groupId>
|
||||
<artifactId>storm-common-security</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.storm</groupId>
|
||||
<artifactId>storm-common-redis</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.storm</groupId>
|
||||
<artifactId>storm-common-datascope</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.storm</groupId>
|
||||
<artifactId>storm-api-system</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Mysql Connector -->
|
||||
<dependency>
|
||||
<groupId>com.mysql</groupId>
|
||||
<artifactId>mysql-connector-j</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- storm Common Log -->
|
||||
<dependency>
|
||||
<groupId>com.storm</groupId>
|
||||
<artifactId>storm-common-log</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- storm Common Swagger -->
|
||||
<dependency>
|
||||
<groupId>com.storm</groupId>
|
||||
<artifactId>storm-common-swagger</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.storm</groupId>
|
||||
<artifactId>storm-common-oss</artifactId>
|
||||
<version>3.6.6</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<finalName>${project.artifactId}</finalName>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>repackage</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
@ -0,0 +1,13 @@
|
||||
package com.storm.afterSales;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
@SpringBootApplication
|
||||
public class AfterSalesApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(AfterSalesApplication.class, args);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,117 @@
|
||||
package com.storm.afterSales.controller;
|
||||
|
||||
import java.util.List;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import com.storm.afterSales.domain.SysKnowledgeRel;
|
||||
import com.storm.afterSales.service.ISysKnowledgeRelService;
|
||||
import com.storm.common.core.domain.R;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.PutMapping;
|
||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import com.storm.common.log.annotation.Log;
|
||||
import com.storm.common.log.enums.BusinessType;
|
||||
import com.storm.common.security.annotation.RequiresPermissions;
|
||||
import com.storm.common.core.web.controller.BaseController;
|
||||
import com.storm.common.core.web.domain.AjaxResult;
|
||||
import com.storm.common.core.utils.poi.ExcelUtil;
|
||||
import com.storm.common.core.web.page.TableDataInfo;
|
||||
|
||||
/**
|
||||
* 工单知识库关联Controller
|
||||
*
|
||||
* @author storm
|
||||
* @date 2025-10-02
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/afterSales")
|
||||
@Tag(name = "工单知识库关联相关接口")
|
||||
public class SysKnowledgeRelController extends BaseController
|
||||
{
|
||||
@Autowired
|
||||
private ISysKnowledgeRelService sysKnowledgeRelService;
|
||||
|
||||
/**
|
||||
* 查询工单知识库关联列表
|
||||
*/
|
||||
@RequiresPermissions("afterSales:afterSales:list")
|
||||
@GetMapping("/list")
|
||||
@Operation(summary = "查询工单知识库关联列表")
|
||||
public TableDataInfo<List<SysKnowledgeRel>> list(@Parameter(description = "工单知识库关联查询条件") SysKnowledgeRel sysKnowledgeRel)
|
||||
{
|
||||
startPage();
|
||||
List<SysKnowledgeRel> list = sysKnowledgeRelService.selectSysKnowledgeRelList(sysKnowledgeRel);
|
||||
return getDataTable(list);
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出工单知识库关联列表
|
||||
*/
|
||||
@RequiresPermissions("afterSales:afterSales:export")
|
||||
@Log(title = "工单知识库关联", businessType = BusinessType.EXPORT)
|
||||
@PostMapping("/export")
|
||||
@Operation(summary = "导出工单知识库关联列表")
|
||||
public void export(HttpServletResponse response, @Parameter(description = "工单知识库关联查询条件") SysKnowledgeRel sysKnowledgeRel)
|
||||
{
|
||||
List<SysKnowledgeRel> list = sysKnowledgeRelService.selectSysKnowledgeRelList(sysKnowledgeRel);
|
||||
ExcelUtil<SysKnowledgeRel> util = new ExcelUtil<SysKnowledgeRel>(SysKnowledgeRel.class);
|
||||
util.exportExcel(response, list, "工单知识库关联数据");
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取工单知识库关联详细信息
|
||||
*/
|
||||
@RequiresPermissions("afterSales:afterSales:query")
|
||||
@GetMapping(value = "/{id}")
|
||||
@Operation(summary = "获取工单知识库关联详细信息")
|
||||
public R<SysKnowledgeRel> getInfo(@Parameter(description = "工单知识库关联ID", required = true)
|
||||
@PathVariable("id") Long id)
|
||||
{
|
||||
return R.ok(sysKnowledgeRelService.selectSysKnowledgeRelById(id));
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增工单知识库关联
|
||||
*/
|
||||
@RequiresPermissions("afterSales:afterSales:add")
|
||||
@Log(title = "工单知识库关联", businessType = BusinessType.INSERT)
|
||||
@PostMapping
|
||||
@Operation(summary = "新增工单知识库关联")
|
||||
public AjaxResult add(@Parameter(description = "工单知识库关联实体", required = true) @RequestBody SysKnowledgeRel sysKnowledgeRel)
|
||||
{
|
||||
return toAjax(sysKnowledgeRelService.insertSysKnowledgeRel(sysKnowledgeRel));
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改工单知识库关联
|
||||
*/
|
||||
@RequiresPermissions("afterSales:afterSales:edit")
|
||||
@Log(title = "工单知识库关联", businessType = BusinessType.UPDATE)
|
||||
@PutMapping
|
||||
@Operation(summary = "修改工单知识库关联")
|
||||
public AjaxResult edit(@Parameter(description = "工单知识库关联实体", required = true) @RequestBody SysKnowledgeRel sysKnowledgeRel)
|
||||
{
|
||||
return toAjax(sysKnowledgeRelService.updateSysKnowledgeRel(sysKnowledgeRel));
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除工单知识库关联
|
||||
*/
|
||||
@RequiresPermissions("afterSales:afterSales:remove")
|
||||
@Log(title = "工单知识库关联", businessType = BusinessType.DELETE)
|
||||
@DeleteMapping("/{ids}")
|
||||
@Operation(summary = "删除工单知识库关联")
|
||||
public AjaxResult remove(@Parameter(description = "工单知识库关联ID数组", required = true) @PathVariable Long[] ids)
|
||||
{
|
||||
return toAjax(sysKnowledgeRelService.deleteSysKnowledgeRelByIds(ids));
|
||||
}
|
||||
}
|
@ -0,0 +1,117 @@
|
||||
package com.storm.afterSales.controller;
|
||||
|
||||
import java.util.List;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import com.storm.common.core.domain.R;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.PutMapping;
|
||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import com.storm.common.log.annotation.Log;
|
||||
import com.storm.common.log.enums.BusinessType;
|
||||
import com.storm.common.security.annotation.RequiresPermissions;
|
||||
import com.storm.afterSales.domain.SysWorkOrderChatRel;
|
||||
import com.storm.afterSales.service.ISysWorkOrderChatRelService;
|
||||
import com.storm.common.core.web.controller.BaseController;
|
||||
import com.storm.common.core.web.domain.AjaxResult;
|
||||
import com.storm.common.core.utils.poi.ExcelUtil;
|
||||
import com.storm.common.core.web.page.TableDataInfo;
|
||||
|
||||
/**
|
||||
* 工单聊天关联Controller
|
||||
*
|
||||
* @author storm
|
||||
* @date 2025-10-02
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/afterSales")
|
||||
@Tag(name = "工单聊天关联相关接口")
|
||||
public class SysWorkOrderChatRelController extends BaseController
|
||||
{
|
||||
@Autowired
|
||||
private ISysWorkOrderChatRelService sysWorkOrderChatRelService;
|
||||
|
||||
/**
|
||||
* 查询工单聊天关联列表
|
||||
*/
|
||||
@RequiresPermissions("afterSales:afterSales:list")
|
||||
@GetMapping("/list")
|
||||
@Operation(summary = "查询工单聊天关联列表")
|
||||
public TableDataInfo<List<SysWorkOrderChatRel>> list(@Parameter(description = "工单聊天关联查询条件") SysWorkOrderChatRel sysWorkOrderChatRel)
|
||||
{
|
||||
startPage();
|
||||
List<SysWorkOrderChatRel> list = sysWorkOrderChatRelService.selectSysWorkOrderChatRelList(sysWorkOrderChatRel);
|
||||
return getDataTable(list);
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出工单聊天关联列表
|
||||
*/
|
||||
@RequiresPermissions("afterSales:afterSales:export")
|
||||
@Log(title = "工单聊天关联", businessType = BusinessType.EXPORT)
|
||||
@PostMapping("/export")
|
||||
@Operation(summary = "导出工单聊天关联列表")
|
||||
public void export(HttpServletResponse response, @Parameter(description = "工单聊天关联查询条件") SysWorkOrderChatRel sysWorkOrderChatRel)
|
||||
{
|
||||
List<SysWorkOrderChatRel> list = sysWorkOrderChatRelService.selectSysWorkOrderChatRelList(sysWorkOrderChatRel);
|
||||
ExcelUtil<SysWorkOrderChatRel> util = new ExcelUtil<SysWorkOrderChatRel>(SysWorkOrderChatRel.class);
|
||||
util.exportExcel(response, list, "工单聊天关联数据");
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取工单聊天关联详细信息
|
||||
*/
|
||||
@RequiresPermissions("afterSales:afterSales:query")
|
||||
@GetMapping(value = "/{id}")
|
||||
@Operation(summary = "获取工单聊天关联详细信息")
|
||||
public R<SysWorkOrderChatRel> getInfo(@Parameter(description = "工单聊天关联ID", required = true)
|
||||
@PathVariable("id") Long id)
|
||||
{
|
||||
return R.ok(sysWorkOrderChatRelService.selectSysWorkOrderChatRelById(id));
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增工单聊天关联
|
||||
*/
|
||||
@RequiresPermissions("afterSales:afterSales:add")
|
||||
@Log(title = "工单聊天关联", businessType = BusinessType.INSERT)
|
||||
@PostMapping
|
||||
@Operation(summary = "新增工单聊天关联")
|
||||
public AjaxResult add(@Parameter(description = "工单聊天关联实体", required = true) @RequestBody SysWorkOrderChatRel sysWorkOrderChatRel)
|
||||
{
|
||||
return toAjax(sysWorkOrderChatRelService.insertSysWorkOrderChatRel(sysWorkOrderChatRel));
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改工单聊天关联
|
||||
*/
|
||||
@RequiresPermissions("afterSales:afterSales:edit")
|
||||
@Log(title = "工单聊天关联", businessType = BusinessType.UPDATE)
|
||||
@PutMapping
|
||||
@Operation(summary = "修改工单聊天关联")
|
||||
public AjaxResult edit(@Parameter(description = "工单聊天关联实体", required = true) @RequestBody SysWorkOrderChatRel sysWorkOrderChatRel)
|
||||
{
|
||||
return toAjax(sysWorkOrderChatRelService.updateSysWorkOrderChatRel(sysWorkOrderChatRel));
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除工单聊天关联
|
||||
*/
|
||||
@RequiresPermissions("afterSales:afterSales:remove")
|
||||
@Log(title = "工单聊天关联", businessType = BusinessType.DELETE)
|
||||
@DeleteMapping("/{ids}")
|
||||
@Operation(summary = "删除工单聊天关联")
|
||||
public AjaxResult remove(@Parameter(description = "工单聊天关联ID数组", required = true) @PathVariable Long[] ids)
|
||||
{
|
||||
return toAjax(sysWorkOrderChatRelService.deleteSysWorkOrderChatRelByIds(ids));
|
||||
}
|
||||
}
|
@ -0,0 +1,117 @@
|
||||
package com.storm.afterSales.controller;
|
||||
|
||||
import java.util.List;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import com.storm.common.core.domain.R;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.PutMapping;
|
||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import com.storm.common.log.annotation.Log;
|
||||
import com.storm.common.log.enums.BusinessType;
|
||||
import com.storm.common.security.annotation.RequiresPermissions;
|
||||
import com.storm.afterSales.domain.SysWorkOrder;
|
||||
import com.storm.afterSales.service.ISysWorkOrderService;
|
||||
import com.storm.common.core.web.controller.BaseController;
|
||||
import com.storm.common.core.web.domain.AjaxResult;
|
||||
import com.storm.common.core.utils.poi.ExcelUtil;
|
||||
import com.storm.common.core.web.page.TableDataInfo;
|
||||
|
||||
/**
|
||||
* 工单主Controller
|
||||
*
|
||||
* @author storm
|
||||
* @date 2025-10-02
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/afterSales")
|
||||
@Tag(name = "工单主相关接口")
|
||||
public class SysWorkOrderController extends BaseController
|
||||
{
|
||||
@Autowired
|
||||
private ISysWorkOrderService sysWorkOrderService;
|
||||
|
||||
/**
|
||||
* 查询工单主列表
|
||||
*/
|
||||
@RequiresPermissions("afterSales:afterSales:list")
|
||||
@GetMapping("/list")
|
||||
@Operation(summary = "查询工单主列表")
|
||||
public TableDataInfo<List<SysWorkOrder>> list(@Parameter(description = "工单主查询条件") SysWorkOrder sysWorkOrder)
|
||||
{
|
||||
startPage();
|
||||
List<SysWorkOrder> list = sysWorkOrderService.selectSysWorkOrderList(sysWorkOrder);
|
||||
return getDataTable(list);
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出工单主列表
|
||||
*/
|
||||
@RequiresPermissions("afterSales:afterSales:export")
|
||||
@Log(title = "工单主", businessType = BusinessType.EXPORT)
|
||||
@PostMapping("/export")
|
||||
@Operation(summary = "导出工单主列表")
|
||||
public void export(HttpServletResponse response, @Parameter(description = "工单主查询条件") SysWorkOrder sysWorkOrder)
|
||||
{
|
||||
List<SysWorkOrder> list = sysWorkOrderService.selectSysWorkOrderList(sysWorkOrder);
|
||||
ExcelUtil<SysWorkOrder> util = new ExcelUtil<SysWorkOrder>(SysWorkOrder.class);
|
||||
util.exportExcel(response, list, "工单主数据");
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取工单主详细信息
|
||||
*/
|
||||
@RequiresPermissions("afterSales:afterSales:query")
|
||||
@GetMapping(value = "/{orderId}")
|
||||
@Operation(summary = "获取工单主详细信息")
|
||||
public R<SysWorkOrder> getInfo(@Parameter(description = "工单主ID", required = true)
|
||||
@PathVariable("orderId") Long orderId)
|
||||
{
|
||||
return R.ok(sysWorkOrderService.selectSysWorkOrderByOrderId(orderId));
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增工单主
|
||||
*/
|
||||
@RequiresPermissions("afterSales:afterSales:add")
|
||||
@Log(title = "工单主", businessType = BusinessType.INSERT)
|
||||
@PostMapping
|
||||
@Operation(summary = "新增工单主")
|
||||
public AjaxResult add(@Parameter(description = "工单主实体", required = true) @RequestBody SysWorkOrder sysWorkOrder)
|
||||
{
|
||||
return toAjax(sysWorkOrderService.insertSysWorkOrder(sysWorkOrder));
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改工单主
|
||||
*/
|
||||
@RequiresPermissions("afterSales:afterSales:edit")
|
||||
@Log(title = "工单主", businessType = BusinessType.UPDATE)
|
||||
@PutMapping
|
||||
@Operation(summary = "修改工单主")
|
||||
public AjaxResult edit(@Parameter(description = "工单主实体", required = true) @RequestBody SysWorkOrder sysWorkOrder)
|
||||
{
|
||||
return toAjax(sysWorkOrderService.updateSysWorkOrder(sysWorkOrder));
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除工单主
|
||||
*/
|
||||
@RequiresPermissions("afterSales:afterSales:remove")
|
||||
@Log(title = "工单主", businessType = BusinessType.DELETE)
|
||||
@DeleteMapping("/{orderIds}")
|
||||
@Operation(summary = "删除工单主")
|
||||
public AjaxResult remove(@Parameter(description = "工单主ID数组", required = true) @PathVariable Long[] orderIds)
|
||||
{
|
||||
return toAjax(sysWorkOrderService.deleteSysWorkOrderByOrderIds(orderIds));
|
||||
}
|
||||
}
|
@ -0,0 +1,117 @@
|
||||
package com.storm.afterSales.controller;
|
||||
|
||||
import java.util.List;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import com.storm.common.core.domain.R;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.PutMapping;
|
||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import com.storm.common.log.annotation.Log;
|
||||
import com.storm.common.log.enums.BusinessType;
|
||||
import com.storm.common.security.annotation.RequiresPermissions;
|
||||
import com.storm.afterSales.domain.SysWorkOrderRecord;
|
||||
import com.storm.afterSales.service.ISysWorkOrderRecordService;
|
||||
import com.storm.common.core.web.controller.BaseController;
|
||||
import com.storm.common.core.web.domain.AjaxResult;
|
||||
import com.storm.common.core.utils.poi.ExcelUtil;
|
||||
import com.storm.common.core.web.page.TableDataInfo;
|
||||
|
||||
/**
|
||||
* 工单处理记录Controller
|
||||
*
|
||||
* @author storm
|
||||
* @date 2025-10-02
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/afterSales")
|
||||
@Tag(name = "工单处理记录相关接口")
|
||||
public class SysWorkOrderRecordController extends BaseController
|
||||
{
|
||||
@Autowired
|
||||
private ISysWorkOrderRecordService sysWorkOrderRecordService;
|
||||
|
||||
/**
|
||||
* 查询工单处理记录列表
|
||||
*/
|
||||
@RequiresPermissions("afterSales:afterSales:list")
|
||||
@GetMapping("/list")
|
||||
@Operation(summary = "查询工单处理记录列表")
|
||||
public TableDataInfo<List<SysWorkOrderRecord>> list(@Parameter(description = "工单处理记录查询条件") SysWorkOrderRecord sysWorkOrderRecord)
|
||||
{
|
||||
startPage();
|
||||
List<SysWorkOrderRecord> list = sysWorkOrderRecordService.selectSysWorkOrderRecordList(sysWorkOrderRecord);
|
||||
return getDataTable(list);
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出工单处理记录列表
|
||||
*/
|
||||
@RequiresPermissions("afterSales:afterSales:export")
|
||||
@Log(title = "工单处理记录", businessType = BusinessType.EXPORT)
|
||||
@PostMapping("/export")
|
||||
@Operation(summary = "导出工单处理记录列表")
|
||||
public void export(HttpServletResponse response, @Parameter(description = "工单处理记录查询条件") SysWorkOrderRecord sysWorkOrderRecord)
|
||||
{
|
||||
List<SysWorkOrderRecord> list = sysWorkOrderRecordService.selectSysWorkOrderRecordList(sysWorkOrderRecord);
|
||||
ExcelUtil<SysWorkOrderRecord> util = new ExcelUtil<SysWorkOrderRecord>(SysWorkOrderRecord.class);
|
||||
util.exportExcel(response, list, "工单处理记录数据");
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取工单处理记录详细信息
|
||||
*/
|
||||
@RequiresPermissions("afterSales:afterSales:query")
|
||||
@GetMapping(value = "/{recordId}")
|
||||
@Operation(summary = "获取工单处理记录详细信息")
|
||||
public R<SysWorkOrderRecord> getInfo(@Parameter(description = "工单处理记录ID", required = true)
|
||||
@PathVariable("recordId") Long recordId)
|
||||
{
|
||||
return R.ok(sysWorkOrderRecordService.selectSysWorkOrderRecordByRecordId(recordId));
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增工单处理记录
|
||||
*/
|
||||
@RequiresPermissions("afterSales:afterSales:add")
|
||||
@Log(title = "工单处理记录", businessType = BusinessType.INSERT)
|
||||
@PostMapping
|
||||
@Operation(summary = "新增工单处理记录")
|
||||
public AjaxResult add(@Parameter(description = "工单处理记录实体", required = true) @RequestBody SysWorkOrderRecord sysWorkOrderRecord)
|
||||
{
|
||||
return toAjax(sysWorkOrderRecordService.insertSysWorkOrderRecord(sysWorkOrderRecord));
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改工单处理记录
|
||||
*/
|
||||
@RequiresPermissions("afterSales:afterSales:edit")
|
||||
@Log(title = "工单处理记录", businessType = BusinessType.UPDATE)
|
||||
@PutMapping
|
||||
@Operation(summary = "修改工单处理记录")
|
||||
public AjaxResult edit(@Parameter(description = "工单处理记录实体", required = true) @RequestBody SysWorkOrderRecord sysWorkOrderRecord)
|
||||
{
|
||||
return toAjax(sysWorkOrderRecordService.updateSysWorkOrderRecord(sysWorkOrderRecord));
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除工单处理记录
|
||||
*/
|
||||
@RequiresPermissions("afterSales:afterSales:remove")
|
||||
@Log(title = "工单处理记录", businessType = BusinessType.DELETE)
|
||||
@DeleteMapping("/{recordIds}")
|
||||
@Operation(summary = "删除工单处理记录")
|
||||
public AjaxResult remove(@Parameter(description = "工单处理记录ID数组", required = true) @PathVariable Long[] recordIds)
|
||||
{
|
||||
return toAjax(sysWorkOrderRecordService.deleteSysWorkOrderRecordByRecordIds(recordIds));
|
||||
}
|
||||
}
|
@ -0,0 +1,117 @@
|
||||
package com.storm.afterSales.controller;
|
||||
|
||||
import java.util.List;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import com.storm.common.core.domain.R;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.PutMapping;
|
||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import com.storm.common.log.annotation.Log;
|
||||
import com.storm.common.log.enums.BusinessType;
|
||||
import com.storm.common.security.annotation.RequiresPermissions;
|
||||
import com.storm.afterSales.domain.SysWorkOrderType;
|
||||
import com.storm.afterSales.service.ISysWorkOrderTypeService;
|
||||
import com.storm.common.core.web.controller.BaseController;
|
||||
import com.storm.common.core.web.domain.AjaxResult;
|
||||
import com.storm.common.core.utils.poi.ExcelUtil;
|
||||
import com.storm.common.core.web.page.TableDataInfo;
|
||||
|
||||
/**
|
||||
* 工单类型Controller
|
||||
*
|
||||
* @author storm
|
||||
* @date 2025-10-02
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/type")
|
||||
@Tag(name = "工单类型相关接口")
|
||||
public class SysWorkOrderTypeController extends BaseController
|
||||
{
|
||||
@Autowired
|
||||
private ISysWorkOrderTypeService sysWorkOrderTypeService;
|
||||
|
||||
/**
|
||||
* 查询工单类型列表
|
||||
*/
|
||||
@RequiresPermissions("afterSales:type:list")
|
||||
@GetMapping("/list")
|
||||
@Operation(summary = "查询工单类型列表")
|
||||
public TableDataInfo<List<SysWorkOrderType>> list(@Parameter(description = "工单类型查询条件") SysWorkOrderType sysWorkOrderType)
|
||||
{
|
||||
startPage();
|
||||
List<SysWorkOrderType> list = sysWorkOrderTypeService.selectSysWorkOrderTypeList(sysWorkOrderType);
|
||||
return getDataTable(list);
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出工单类型列表
|
||||
*/
|
||||
@RequiresPermissions("afterSales:type:export")
|
||||
@Log(title = "工单类型", businessType = BusinessType.EXPORT)
|
||||
@PostMapping("/export")
|
||||
@Operation(summary = "导出工单类型列表")
|
||||
public void export(HttpServletResponse response, @Parameter(description = "工单类型查询条件") SysWorkOrderType sysWorkOrderType)
|
||||
{
|
||||
List<SysWorkOrderType> list = sysWorkOrderTypeService.selectSysWorkOrderTypeList(sysWorkOrderType);
|
||||
ExcelUtil<SysWorkOrderType> util = new ExcelUtil<SysWorkOrderType>(SysWorkOrderType.class);
|
||||
util.exportExcel(response, list, "工单类型数据");
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取工单类型详细信息
|
||||
*/
|
||||
@RequiresPermissions("afterSales:type:query")
|
||||
@GetMapping(value = "/{typeId}")
|
||||
@Operation(summary = "获取工单类型详细信息")
|
||||
public R<SysWorkOrderType> getInfo(@Parameter(description = "工单类型ID", required = true)
|
||||
@PathVariable("typeId") Long typeId)
|
||||
{
|
||||
return R.ok(sysWorkOrderTypeService.selectSysWorkOrderTypeByTypeId(typeId));
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增工单类型
|
||||
*/
|
||||
@RequiresPermissions("afterSales:type:add")
|
||||
@Log(title = "工单类型", businessType = BusinessType.INSERT)
|
||||
@PostMapping
|
||||
@Operation(summary = "新增工单类型")
|
||||
public AjaxResult add(@Parameter(description = "工单类型实体", required = true) @RequestBody SysWorkOrderType sysWorkOrderType)
|
||||
{
|
||||
return toAjax(sysWorkOrderTypeService.insertSysWorkOrderType(sysWorkOrderType));
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改工单类型
|
||||
*/
|
||||
@RequiresPermissions("afterSales:type:edit")
|
||||
@Log(title = "工单类型", businessType = BusinessType.UPDATE)
|
||||
@PutMapping
|
||||
@Operation(summary = "修改工单类型")
|
||||
public AjaxResult edit(@Parameter(description = "工单类型实体", required = true) @RequestBody SysWorkOrderType sysWorkOrderType)
|
||||
{
|
||||
return toAjax(sysWorkOrderTypeService.updateSysWorkOrderType(sysWorkOrderType));
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除工单类型
|
||||
*/
|
||||
@RequiresPermissions("afterSales:type:remove")
|
||||
@Log(title = "工单类型", businessType = BusinessType.DELETE)
|
||||
@DeleteMapping("/{typeIds}")
|
||||
@Operation(summary = "删除工单类型")
|
||||
public AjaxResult remove(@Parameter(description = "工单类型ID数组", required = true) @PathVariable Long[] typeIds)
|
||||
{
|
||||
return toAjax(sysWorkOrderTypeService.deleteSysWorkOrderTypeByTypeIds(typeIds));
|
||||
}
|
||||
}
|
@ -0,0 +1,49 @@
|
||||
package com.storm.afterSales.domain;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import com.storm.common.core.annotation.Excel;
|
||||
import com.storm.common.core.web.domain.BaseEntity;
|
||||
import lombok.Data;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* 工单知识库关联对象 sys_knowledge_rel
|
||||
*
|
||||
* @author storm
|
||||
* @date 2025-10-02
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Schema(description = "工单知识库关联实体")
|
||||
public class SysKnowledgeRel extends BaseEntity
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/** 主键 */
|
||||
@Schema(description = "主键")
|
||||
private Long id;
|
||||
|
||||
/** 工单ID */
|
||||
@Excel(name = "工单ID")
|
||||
@Schema(description = "工单ID")
|
||||
private Long orderId;
|
||||
|
||||
/** 知识库文章ID */
|
||||
@Excel(name = "知识库文章ID")
|
||||
@Schema(description = "知识库文章ID")
|
||||
private Long knowledgeId;
|
||||
|
||||
/** 知识标题 */
|
||||
@Excel(name = "知识标题")
|
||||
@Schema(description = "知识标题")
|
||||
private String knowledgeTitle;
|
||||
|
||||
/** 使用次数 */
|
||||
@Excel(name = "使用次数")
|
||||
@Schema(description = "使用次数")
|
||||
private Integer usedCount;
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,205 @@
|
||||
package com.storm.afterSales.domain;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import com.storm.common.core.annotation.Excel;
|
||||
import com.storm.common.core.web.domain.BaseEntity;
|
||||
import lombok.Data;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* 工单主对象 sys_work_order
|
||||
*
|
||||
* @author storm
|
||||
* @date 2025-10-02
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Schema(description = "工单主实体")
|
||||
public class SysWorkOrder extends BaseEntity
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/** 工单ID */
|
||||
@Schema(description = "工单ID")
|
||||
private Long orderId;
|
||||
|
||||
/** 工单编号 */
|
||||
@Excel(name = "工单编号")
|
||||
@Schema(description = "工单编号")
|
||||
private String orderNo;
|
||||
|
||||
/** 工单标题 */
|
||||
@Excel(name = "工单标题")
|
||||
@Schema(description = "工单标题")
|
||||
private String orderTitle;
|
||||
|
||||
/** 工单类型(对应type_key) */
|
||||
@Excel(name = "工单类型", readConverterExp = "对=应type_key")
|
||||
@Schema(description = "工单类型(对应type_key)")
|
||||
private String orderType;
|
||||
|
||||
/** 工单状态(pending, processing, resolved, closed, cancelled) */
|
||||
@Excel(name = "工单状态", readConverterExp = "p=ending,,p=rocessing,,r=esolved,,c=losed,,c=ancelled")
|
||||
@Schema(description = "工单状态(pending, processing, resolved, closed, cancelled)")
|
||||
private String orderStatus;
|
||||
|
||||
/** 优先级(low, medium, high, urgent) */
|
||||
@Excel(name = "优先级", readConverterExp = "l=ow,,m=edium,,h=igh,,u=rgent")
|
||||
@Schema(description = "优先级(low, medium, high, urgent)")
|
||||
private String priority;
|
||||
|
||||
/** 客户ID */
|
||||
@Excel(name = "客户ID")
|
||||
@Schema(description = "客户ID")
|
||||
private Long customerId;
|
||||
|
||||
/** 客户姓名 */
|
||||
@Excel(name = "客户姓名")
|
||||
@Schema(description = "客户姓名")
|
||||
private String customerName;
|
||||
|
||||
/** 客户联系方式 */
|
||||
@Excel(name = "客户联系方式")
|
||||
@Schema(description = "客户联系方式")
|
||||
private String customerContact;
|
||||
|
||||
/** 门店地址 */
|
||||
@Excel(name = "门店地址")
|
||||
@Schema(description = "门店地址")
|
||||
private String storeAddress;
|
||||
|
||||
/** 设备名称 */
|
||||
@Excel(name = "设备名称")
|
||||
@Schema(description = "设备名称")
|
||||
private String deviceName;
|
||||
|
||||
/** 设备序列号 */
|
||||
@Excel(name = "设备序列号")
|
||||
@Schema(description = "设备序列号")
|
||||
private String deviceSerialNo;
|
||||
|
||||
/** 设备型号 */
|
||||
@Excel(name = "设备型号")
|
||||
@Schema(description = "设备型号")
|
||||
private String deviceModel;
|
||||
|
||||
/** 生产日期 */
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
@Excel(name = "生产日期", width = 30, dateFormat = "yyyy-MM-dd")
|
||||
@Schema(description = "生产日期")
|
||||
private LocalDateTime productionDate;
|
||||
|
||||
/** 问题描述 */
|
||||
@Excel(name = "问题描述")
|
||||
@Schema(description = "问题描述")
|
||||
private String problemDescription;
|
||||
|
||||
/** 附件URL(图片/视频) - 支持多个 */
|
||||
@Excel(name = "附件URL", readConverterExp = "图=片/视频")
|
||||
@Schema(description = "附件URL(图片/视频) - 支持多个")
|
||||
private String attachmentUrls;
|
||||
|
||||
/** 问题分类(数量问题、质量问题、软件故障等) */
|
||||
@Excel(name = "问题分类", readConverterExp = "数=量问题、质量问题、软件故障等")
|
||||
@Schema(description = "问题分类(数量问题、质量问题、软件故障等)")
|
||||
private String problemCategory;
|
||||
|
||||
/** 处理人ID */
|
||||
@Excel(name = "处理人ID")
|
||||
@Schema(description = "处理人ID")
|
||||
private Long assigneeId;
|
||||
|
||||
/** 处理人姓名 */
|
||||
@Excel(name = "处理人姓名")
|
||||
@Schema(description = "处理人姓名")
|
||||
private String assigneeName;
|
||||
|
||||
/** 分配时间 */
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
@Excel(name = "分配时间", width = 30, dateFormat = "yyyy-MM-dd")
|
||||
@Schema(description = "分配时间")
|
||||
private LocalDateTime assignTime;
|
||||
|
||||
/** 当前处理人ID */
|
||||
@Excel(name = "当前处理人ID")
|
||||
@Schema(description = "当前处理人ID")
|
||||
private Long currentHandlerId;
|
||||
|
||||
/** 当前处理人姓名 */
|
||||
@Excel(name = "当前处理人姓名")
|
||||
@Schema(description = "当前处理人姓名")
|
||||
private String currentHandlerName;
|
||||
|
||||
/** 原因定位 */
|
||||
@Excel(name = "原因定位")
|
||||
@Schema(description = "原因定位")
|
||||
private String rootCause;
|
||||
|
||||
/** 处理方案 */
|
||||
@Excel(name = "处理方案")
|
||||
@Schema(description = "处理方案")
|
||||
private String solutionPlan;
|
||||
|
||||
/** 最终解决方案 */
|
||||
@Excel(name = "最终解决方案")
|
||||
@Schema(description = "最终解决方案")
|
||||
private String finalSolution;
|
||||
|
||||
/** 物流单号 */
|
||||
@Excel(name = "物流单号")
|
||||
@Schema(description = "物流单号")
|
||||
private String logisticsNumber;
|
||||
|
||||
/** 物流进度 */
|
||||
@Excel(name = "物流进度")
|
||||
@Schema(description = "物流进度")
|
||||
private String logisticsProgress;
|
||||
|
||||
/** 客户反馈 */
|
||||
@Excel(name = "客户反馈")
|
||||
@Schema(description = "客户反馈")
|
||||
private String customerFeedback;
|
||||
|
||||
/** 满意度(1-5) */
|
||||
@Excel(name = "满意度", readConverterExp = "1=-5")
|
||||
@Schema(description = "满意度(1-5)")
|
||||
private Integer satisfactionLevel;
|
||||
|
||||
/** 反馈类型(praise, complaint, suggestion) */
|
||||
@Excel(name = "反馈类型", readConverterExp = "p=raise,,c=omplaint,,s=uggestion")
|
||||
@Schema(description = "反馈类型(praise, complaint, suggestion)")
|
||||
private String feedbackType;
|
||||
|
||||
/** 计划完成时间 */
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
@Excel(name = "计划完成时间", width = 30, dateFormat = "yyyy-MM-dd")
|
||||
@Schema(description = "计划完成时间")
|
||||
private LocalDateTime planFinishTime;
|
||||
|
||||
/** 实际完成时间 */
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
@Excel(name = "实际完成时间", width = 30, dateFormat = "yyyy-MM-dd")
|
||||
@Schema(description = "实际完成时间")
|
||||
private LocalDateTime actualFinishTime;
|
||||
|
||||
/** 响应截止时间(30分钟规则) */
|
||||
@Excel(name = "响应截止时间", readConverterExp = "3=0分钟规则")
|
||||
@Schema(description = "响应截止时间(30分钟规则)")
|
||||
private LocalDateTime responseDeadline;
|
||||
|
||||
/** 来源渠道(feishu, phone, wechat, app, web) */
|
||||
@Excel(name = "来源渠道", readConverterExp = "f=eishu,,p=hone,,w=echat,,a=pp,,w=eb")
|
||||
@Schema(description = "来源渠道(feishu, phone, wechat, app, web)")
|
||||
private String sourceChannel;
|
||||
|
||||
/** 飞书会话ID */
|
||||
@Excel(name = "飞书会话ID")
|
||||
@Schema(description = "飞书会话ID")
|
||||
private String feishuChatId;
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,59 @@
|
||||
package com.storm.afterSales.domain;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import com.storm.common.core.annotation.Excel;
|
||||
import com.storm.common.core.web.domain.BaseEntity;
|
||||
import lombok.Data;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* 工单聊天关联对象 sys_work_order_chat_rel
|
||||
*
|
||||
* @author storm
|
||||
* @date 2025-10-02
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Schema(description = "工单聊天关联实体")
|
||||
public class SysWorkOrderChatRel extends BaseEntity
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/** 主键 */
|
||||
@Schema(description = "主键")
|
||||
private Long id;
|
||||
|
||||
/** 工单ID */
|
||||
@Excel(name = "工单ID")
|
||||
@Schema(description = "工单ID")
|
||||
private Long orderId;
|
||||
|
||||
/** 聊天会话ID */
|
||||
@Excel(name = "聊天会话ID")
|
||||
@Schema(description = "聊天会话ID")
|
||||
private String chatSessionId;
|
||||
|
||||
/** 平台(feishu, wechat, phone) */
|
||||
@Excel(name = "平台", readConverterExp = "f=eishu,,w=echat,,p=hone")
|
||||
@Schema(description = "平台(feishu, wechat, phone)")
|
||||
private String chatPlatform;
|
||||
|
||||
/** 消息ID */
|
||||
@Excel(name = "消息ID")
|
||||
@Schema(description = "消息ID")
|
||||
private String messageId;
|
||||
|
||||
/** 聊天内容摘要 */
|
||||
@Excel(name = "聊天内容摘要")
|
||||
@Schema(description = "聊天内容摘要")
|
||||
private String chatContent;
|
||||
|
||||
/** 通话时长(秒) */
|
||||
@Excel(name = "通话时长", readConverterExp = "秒=")
|
||||
@Schema(description = "通话时长(秒)")
|
||||
private Integer chatDuration;
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,82 @@
|
||||
package com.storm.afterSales.domain;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import com.storm.common.core.annotation.Excel;
|
||||
import com.storm.common.core.web.domain.BaseEntity;
|
||||
import lombok.Data;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* 工单处理记录对象 sys_work_order_record
|
||||
*
|
||||
* @author storm
|
||||
* @date 2025-10-02
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Schema(description = "工单处理记录实体")
|
||||
public class SysWorkOrderRecord extends BaseEntity
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/** 记录ID */
|
||||
@Schema(description = "记录ID")
|
||||
private Long recordId;
|
||||
|
||||
/** 工单ID */
|
||||
@Excel(name = "工单ID")
|
||||
@Schema(description = "工单ID")
|
||||
private Long orderId;
|
||||
|
||||
/** 处理类型(create, assign, process, feedback, close, visit等) */
|
||||
@Excel(name = "处理类型", readConverterExp = "c=reate,,a=ssign,,p=rocess,,f=eedback,,c=lose,,v=isit等")
|
||||
@Schema(description = "处理类型(create, assign, process, feedback, close, visit等)")
|
||||
private String processType;
|
||||
|
||||
/** 处理内容 */
|
||||
@Excel(name = "处理内容")
|
||||
@Schema(description = "处理内容")
|
||||
private String processContent;
|
||||
|
||||
/** 进度类型(logistics, repair, replacement, optimization, complaint) */
|
||||
@Excel(name = "进度类型", readConverterExp = "l=ogistics,,r=epair,,r=eplacement,,o=ptimization,,c=omplaint")
|
||||
@Schema(description = "进度类型(logistics, repair, replacement, optimization, complaint)")
|
||||
private String progressType;
|
||||
|
||||
/** 进度值 */
|
||||
@Excel(name = "进度值")
|
||||
@Schema(description = "进度值")
|
||||
private String progressValue;
|
||||
|
||||
/** 进度百分比 */
|
||||
@Excel(name = "进度百分比")
|
||||
@Schema(description = "进度百分比")
|
||||
private Integer progressPercent;
|
||||
|
||||
/** 处理人ID */
|
||||
@Excel(name = "处理人ID")
|
||||
@Schema(description = "处理人ID")
|
||||
private Long processUserId;
|
||||
|
||||
/** 处理人姓名 */
|
||||
@Excel(name = "处理人姓名")
|
||||
@Schema(description = "处理人姓名")
|
||||
private String processUserName;
|
||||
|
||||
/** 处理时间 */
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
@Excel(name = "处理时间", width = 30, dateFormat = "yyyy-MM-dd")
|
||||
@Schema(description = "处理时间")
|
||||
private LocalDateTime processTime;
|
||||
|
||||
/** 进度附件 */
|
||||
@Excel(name = "进度附件")
|
||||
@Schema(description = "进度附件")
|
||||
private String attachmentUrl;
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,49 @@
|
||||
package com.storm.afterSales.domain;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import com.storm.common.core.annotation.Excel;
|
||||
import com.storm.common.core.web.domain.BaseEntity;
|
||||
import lombok.Data;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* 工单类型对象 sys_work_order_type
|
||||
*
|
||||
* @author storm
|
||||
* @date 2025-10-02
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Schema(description = "工单类型实体")
|
||||
public class SysWorkOrderType extends BaseEntity
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/** 类型ID */
|
||||
@Schema(description = "类型ID")
|
||||
private Long typeId;
|
||||
|
||||
/** 类型名称(操作习惯、部件维修、设备巡厂维修、产品建议、投诉) */
|
||||
@Excel(name = "类型名称", readConverterExp = "操=作习惯、部件维修、设备巡厂维修、产品建议、投诉")
|
||||
@Schema(description = "类型名称(操作习惯、部件维修、设备巡厂维修、产品建议、投诉)")
|
||||
private String typeName;
|
||||
|
||||
/** 类型键值 */
|
||||
@Excel(name = "类型键值")
|
||||
@Schema(description = "类型键值")
|
||||
private String typeKey;
|
||||
|
||||
/** 状态(0正常 1停用) */
|
||||
@Excel(name = "状态", readConverterExp = "0=正常,1=停用")
|
||||
@Schema(description = "状态(0正常 1停用)")
|
||||
private String STATUS;
|
||||
|
||||
/** 显示顺序 */
|
||||
@Excel(name = "显示顺序")
|
||||
@Schema(description = "显示顺序")
|
||||
private Integer orderNum;
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,64 @@
|
||||
package com.storm.afterSales.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import java.util.List;
|
||||
import com.storm.afterSales.domain.SysKnowledgeRel;
|
||||
|
||||
/**
|
||||
* 工单知识库关联Mapper接口
|
||||
*
|
||||
* @author storm
|
||||
* @date 2025-10-02
|
||||
*/
|
||||
@Mapper
|
||||
public interface SysKnowledgeRelMapper extends BaseMapper<SysKnowledgeRel>
|
||||
{
|
||||
/**
|
||||
* 查询工单知识库关联
|
||||
*
|
||||
* @param id 工单知识库关联主键
|
||||
* @return 工单知识库关联
|
||||
*/
|
||||
public SysKnowledgeRel selectSysKnowledgeRelById(Long id);
|
||||
|
||||
/**
|
||||
* 查询工单知识库关联列表
|
||||
*
|
||||
* @param sysKnowledgeRel 工单知识库关联
|
||||
* @return 工单知识库关联集合
|
||||
*/
|
||||
public List<SysKnowledgeRel> selectSysKnowledgeRelList(SysKnowledgeRel sysKnowledgeRel);
|
||||
|
||||
/**
|
||||
* 新增工单知识库关联
|
||||
*
|
||||
* @param sysKnowledgeRel 工单知识库关联
|
||||
* @return 结果
|
||||
*/
|
||||
public int insertSysKnowledgeRel(SysKnowledgeRel sysKnowledgeRel);
|
||||
|
||||
/**
|
||||
* 修改工单知识库关联
|
||||
*
|
||||
* @param sysKnowledgeRel 工单知识库关联
|
||||
* @return 结果
|
||||
*/
|
||||
public int updateSysKnowledgeRel(SysKnowledgeRel sysKnowledgeRel);
|
||||
|
||||
/**
|
||||
* 删除工单知识库关联
|
||||
*
|
||||
* @param id 工单知识库关联主键
|
||||
* @return 结果
|
||||
*/
|
||||
public int deleteSysKnowledgeRelById(Long id);
|
||||
|
||||
/**
|
||||
* 批量删除工单知识库关联
|
||||
*
|
||||
* @param ids 需要删除的数据主键集合
|
||||
* @return 结果
|
||||
*/
|
||||
public int deleteSysKnowledgeRelByIds(Long[] ids);
|
||||
}
|
@ -0,0 +1,64 @@
|
||||
package com.storm.afterSales.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import java.util.List;
|
||||
import com.storm.afterSales.domain.SysWorkOrderChatRel;
|
||||
|
||||
/**
|
||||
* 工单聊天关联Mapper接口
|
||||
*
|
||||
* @author storm
|
||||
* @date 2025-10-02
|
||||
*/
|
||||
@Mapper
|
||||
public interface SysWorkOrderChatRelMapper extends BaseMapper<SysWorkOrderChatRel>
|
||||
{
|
||||
/**
|
||||
* 查询工单聊天关联
|
||||
*
|
||||
* @param id 工单聊天关联主键
|
||||
* @return 工单聊天关联
|
||||
*/
|
||||
public SysWorkOrderChatRel selectSysWorkOrderChatRelById(Long id);
|
||||
|
||||
/**
|
||||
* 查询工单聊天关联列表
|
||||
*
|
||||
* @param sysWorkOrderChatRel 工单聊天关联
|
||||
* @return 工单聊天关联集合
|
||||
*/
|
||||
public List<SysWorkOrderChatRel> selectSysWorkOrderChatRelList(SysWorkOrderChatRel sysWorkOrderChatRel);
|
||||
|
||||
/**
|
||||
* 新增工单聊天关联
|
||||
*
|
||||
* @param sysWorkOrderChatRel 工单聊天关联
|
||||
* @return 结果
|
||||
*/
|
||||
public int insertSysWorkOrderChatRel(SysWorkOrderChatRel sysWorkOrderChatRel);
|
||||
|
||||
/**
|
||||
* 修改工单聊天关联
|
||||
*
|
||||
* @param sysWorkOrderChatRel 工单聊天关联
|
||||
* @return 结果
|
||||
*/
|
||||
public int updateSysWorkOrderChatRel(SysWorkOrderChatRel sysWorkOrderChatRel);
|
||||
|
||||
/**
|
||||
* 删除工单聊天关联
|
||||
*
|
||||
* @param id 工单聊天关联主键
|
||||
* @return 结果
|
||||
*/
|
||||
public int deleteSysWorkOrderChatRelById(Long id);
|
||||
|
||||
/**
|
||||
* 批量删除工单聊天关联
|
||||
*
|
||||
* @param ids 需要删除的数据主键集合
|
||||
* @return 结果
|
||||
*/
|
||||
public int deleteSysWorkOrderChatRelByIds(Long[] ids);
|
||||
}
|
@ -0,0 +1,64 @@
|
||||
package com.storm.afterSales.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import java.util.List;
|
||||
import com.storm.afterSales.domain.SysWorkOrder;
|
||||
|
||||
/**
|
||||
* 工单主Mapper接口
|
||||
*
|
||||
* @author storm
|
||||
* @date 2025-10-02
|
||||
*/
|
||||
@Mapper
|
||||
public interface SysWorkOrderMapper extends BaseMapper<SysWorkOrder>
|
||||
{
|
||||
/**
|
||||
* 查询工单主
|
||||
*
|
||||
* @param orderId 工单主主键
|
||||
* @return 工单主
|
||||
*/
|
||||
public SysWorkOrder selectSysWorkOrderByOrderId(Long orderId);
|
||||
|
||||
/**
|
||||
* 查询工单主列表
|
||||
*
|
||||
* @param sysWorkOrder 工单主
|
||||
* @return 工单主集合
|
||||
*/
|
||||
public List<SysWorkOrder> selectSysWorkOrderList(SysWorkOrder sysWorkOrder);
|
||||
|
||||
/**
|
||||
* 新增工单主
|
||||
*
|
||||
* @param sysWorkOrder 工单主
|
||||
* @return 结果
|
||||
*/
|
||||
public int insertSysWorkOrder(SysWorkOrder sysWorkOrder);
|
||||
|
||||
/**
|
||||
* 修改工单主
|
||||
*
|
||||
* @param sysWorkOrder 工单主
|
||||
* @return 结果
|
||||
*/
|
||||
public int updateSysWorkOrder(SysWorkOrder sysWorkOrder);
|
||||
|
||||
/**
|
||||
* 删除工单主
|
||||
*
|
||||
* @param orderId 工单主主键
|
||||
* @return 结果
|
||||
*/
|
||||
public int deleteSysWorkOrderByOrderId(Long orderId);
|
||||
|
||||
/**
|
||||
* 批量删除工单主
|
||||
*
|
||||
* @param orderIds 需要删除的数据主键集合
|
||||
* @return 结果
|
||||
*/
|
||||
public int deleteSysWorkOrderByOrderIds(Long[] orderIds);
|
||||
}
|
@ -0,0 +1,64 @@
|
||||
package com.storm.afterSales.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import java.util.List;
|
||||
import com.storm.afterSales.domain.SysWorkOrderRecord;
|
||||
|
||||
/**
|
||||
* 工单处理记录Mapper接口
|
||||
*
|
||||
* @author storm
|
||||
* @date 2025-10-02
|
||||
*/
|
||||
@Mapper
|
||||
public interface SysWorkOrderRecordMapper extends BaseMapper<SysWorkOrderRecord>
|
||||
{
|
||||
/**
|
||||
* 查询工单处理记录
|
||||
*
|
||||
* @param recordId 工单处理记录主键
|
||||
* @return 工单处理记录
|
||||
*/
|
||||
public SysWorkOrderRecord selectSysWorkOrderRecordByRecordId(Long recordId);
|
||||
|
||||
/**
|
||||
* 查询工单处理记录列表
|
||||
*
|
||||
* @param sysWorkOrderRecord 工单处理记录
|
||||
* @return 工单处理记录集合
|
||||
*/
|
||||
public List<SysWorkOrderRecord> selectSysWorkOrderRecordList(SysWorkOrderRecord sysWorkOrderRecord);
|
||||
|
||||
/**
|
||||
* 新增工单处理记录
|
||||
*
|
||||
* @param sysWorkOrderRecord 工单处理记录
|
||||
* @return 结果
|
||||
*/
|
||||
public int insertSysWorkOrderRecord(SysWorkOrderRecord sysWorkOrderRecord);
|
||||
|
||||
/**
|
||||
* 修改工单处理记录
|
||||
*
|
||||
* @param sysWorkOrderRecord 工单处理记录
|
||||
* @return 结果
|
||||
*/
|
||||
public int updateSysWorkOrderRecord(SysWorkOrderRecord sysWorkOrderRecord);
|
||||
|
||||
/**
|
||||
* 删除工单处理记录
|
||||
*
|
||||
* @param recordId 工单处理记录主键
|
||||
* @return 结果
|
||||
*/
|
||||
public int deleteSysWorkOrderRecordByRecordId(Long recordId);
|
||||
|
||||
/**
|
||||
* 批量删除工单处理记录
|
||||
*
|
||||
* @param recordIds 需要删除的数据主键集合
|
||||
* @return 结果
|
||||
*/
|
||||
public int deleteSysWorkOrderRecordByRecordIds(Long[] recordIds);
|
||||
}
|
@ -0,0 +1,64 @@
|
||||
package com.storm.afterSales.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import java.util.List;
|
||||
import com.storm.afterSales.domain.SysWorkOrderType;
|
||||
|
||||
/**
|
||||
* 工单类型Mapper接口
|
||||
*
|
||||
* @author storm
|
||||
* @date 2025-10-02
|
||||
*/
|
||||
@Mapper
|
||||
public interface SysWorkOrderTypeMapper extends BaseMapper<SysWorkOrderType>
|
||||
{
|
||||
/**
|
||||
* 查询工单类型
|
||||
*
|
||||
* @param typeId 工单类型主键
|
||||
* @return 工单类型
|
||||
*/
|
||||
public SysWorkOrderType selectSysWorkOrderTypeByTypeId(Long typeId);
|
||||
|
||||
/**
|
||||
* 查询工单类型列表
|
||||
*
|
||||
* @param sysWorkOrderType 工单类型
|
||||
* @return 工单类型集合
|
||||
*/
|
||||
public List<SysWorkOrderType> selectSysWorkOrderTypeList(SysWorkOrderType sysWorkOrderType);
|
||||
|
||||
/**
|
||||
* 新增工单类型
|
||||
*
|
||||
* @param sysWorkOrderType 工单类型
|
||||
* @return 结果
|
||||
*/
|
||||
public int insertSysWorkOrderType(SysWorkOrderType sysWorkOrderType);
|
||||
|
||||
/**
|
||||
* 修改工单类型
|
||||
*
|
||||
* @param sysWorkOrderType 工单类型
|
||||
* @return 结果
|
||||
*/
|
||||
public int updateSysWorkOrderType(SysWorkOrderType sysWorkOrderType);
|
||||
|
||||
/**
|
||||
* 删除工单类型
|
||||
*
|
||||
* @param typeId 工单类型主键
|
||||
* @return 结果
|
||||
*/
|
||||
public int deleteSysWorkOrderTypeByTypeId(Long typeId);
|
||||
|
||||
/**
|
||||
* 批量删除工单类型
|
||||
*
|
||||
* @param typeIds 需要删除的数据主键集合
|
||||
* @return 结果
|
||||
*/
|
||||
public int deleteSysWorkOrderTypeByTypeIds(Long[] typeIds);
|
||||
}
|
@ -0,0 +1,63 @@
|
||||
package com.storm.afterSales.service;
|
||||
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import java.util.List;
|
||||
import com.storm.afterSales.domain.SysKnowledgeRel;
|
||||
|
||||
/**
|
||||
* 工单知识库关联Service接口
|
||||
*
|
||||
* @author storm
|
||||
* @date 2025-10-02
|
||||
*/
|
||||
public interface ISysKnowledgeRelService extends IService<SysKnowledgeRel>
|
||||
{
|
||||
/**
|
||||
* 查询工单知识库关联
|
||||
*
|
||||
* @param id 工单知识库关联主键
|
||||
* @return 工单知识库关联
|
||||
*/
|
||||
public SysKnowledgeRel selectSysKnowledgeRelById(Long id);
|
||||
|
||||
/**
|
||||
* 查询工单知识库关联列表
|
||||
*
|
||||
* @param sysKnowledgeRel 工单知识库关联
|
||||
* @return 工单知识库关联集合
|
||||
*/
|
||||
public List<SysKnowledgeRel> selectSysKnowledgeRelList(SysKnowledgeRel sysKnowledgeRel);
|
||||
|
||||
/**
|
||||
* 新增工单知识库关联
|
||||
*
|
||||
* @param sysKnowledgeRel 工单知识库关联
|
||||
* @return 结果
|
||||
*/
|
||||
public int insertSysKnowledgeRel(SysKnowledgeRel sysKnowledgeRel);
|
||||
|
||||
/**
|
||||
* 修改工单知识库关联
|
||||
*
|
||||
* @param sysKnowledgeRel 工单知识库关联
|
||||
* @return 结果
|
||||
*/
|
||||
public int updateSysKnowledgeRel(SysKnowledgeRel sysKnowledgeRel);
|
||||
|
||||
/**
|
||||
* 批量删除工单知识库关联
|
||||
*
|
||||
* @param ids 需要删除的工单知识库关联主键集合
|
||||
* @return 结果
|
||||
*/
|
||||
public int deleteSysKnowledgeRelByIds(Long[] ids);
|
||||
|
||||
/**
|
||||
* 删除工单知识库关联信息
|
||||
*
|
||||
* @param id 工单知识库关联主键
|
||||
* @return 结果
|
||||
*/
|
||||
public int deleteSysKnowledgeRelById(Long id);
|
||||
}
|
@ -0,0 +1,63 @@
|
||||
package com.storm.afterSales.service;
|
||||
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import java.util.List;
|
||||
import com.storm.afterSales.domain.SysWorkOrderChatRel;
|
||||
|
||||
/**
|
||||
* 工单聊天关联Service接口
|
||||
*
|
||||
* @author storm
|
||||
* @date 2025-10-02
|
||||
*/
|
||||
public interface ISysWorkOrderChatRelService extends IService<SysWorkOrderChatRel>
|
||||
{
|
||||
/**
|
||||
* 查询工单聊天关联
|
||||
*
|
||||
* @param id 工单聊天关联主键
|
||||
* @return 工单聊天关联
|
||||
*/
|
||||
public SysWorkOrderChatRel selectSysWorkOrderChatRelById(Long id);
|
||||
|
||||
/**
|
||||
* 查询工单聊天关联列表
|
||||
*
|
||||
* @param sysWorkOrderChatRel 工单聊天关联
|
||||
* @return 工单聊天关联集合
|
||||
*/
|
||||
public List<SysWorkOrderChatRel> selectSysWorkOrderChatRelList(SysWorkOrderChatRel sysWorkOrderChatRel);
|
||||
|
||||
/**
|
||||
* 新增工单聊天关联
|
||||
*
|
||||
* @param sysWorkOrderChatRel 工单聊天关联
|
||||
* @return 结果
|
||||
*/
|
||||
public int insertSysWorkOrderChatRel(SysWorkOrderChatRel sysWorkOrderChatRel);
|
||||
|
||||
/**
|
||||
* 修改工单聊天关联
|
||||
*
|
||||
* @param sysWorkOrderChatRel 工单聊天关联
|
||||
* @return 结果
|
||||
*/
|
||||
public int updateSysWorkOrderChatRel(SysWorkOrderChatRel sysWorkOrderChatRel);
|
||||
|
||||
/**
|
||||
* 批量删除工单聊天关联
|
||||
*
|
||||
* @param ids 需要删除的工单聊天关联主键集合
|
||||
* @return 结果
|
||||
*/
|
||||
public int deleteSysWorkOrderChatRelByIds(Long[] ids);
|
||||
|
||||
/**
|
||||
* 删除工单聊天关联信息
|
||||
*
|
||||
* @param id 工单聊天关联主键
|
||||
* @return 结果
|
||||
*/
|
||||
public int deleteSysWorkOrderChatRelById(Long id);
|
||||
}
|
@ -0,0 +1,63 @@
|
||||
package com.storm.afterSales.service;
|
||||
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import java.util.List;
|
||||
import com.storm.afterSales.domain.SysWorkOrderRecord;
|
||||
|
||||
/**
|
||||
* 工单处理记录Service接口
|
||||
*
|
||||
* @author storm
|
||||
* @date 2025-10-02
|
||||
*/
|
||||
public interface ISysWorkOrderRecordService extends IService<SysWorkOrderRecord>
|
||||
{
|
||||
/**
|
||||
* 查询工单处理记录
|
||||
*
|
||||
* @param recordId 工单处理记录主键
|
||||
* @return 工单处理记录
|
||||
*/
|
||||
public SysWorkOrderRecord selectSysWorkOrderRecordByRecordId(Long recordId);
|
||||
|
||||
/**
|
||||
* 查询工单处理记录列表
|
||||
*
|
||||
* @param sysWorkOrderRecord 工单处理记录
|
||||
* @return 工单处理记录集合
|
||||
*/
|
||||
public List<SysWorkOrderRecord> selectSysWorkOrderRecordList(SysWorkOrderRecord sysWorkOrderRecord);
|
||||
|
||||
/**
|
||||
* 新增工单处理记录
|
||||
*
|
||||
* @param sysWorkOrderRecord 工单处理记录
|
||||
* @return 结果
|
||||
*/
|
||||
public int insertSysWorkOrderRecord(SysWorkOrderRecord sysWorkOrderRecord);
|
||||
|
||||
/**
|
||||
* 修改工单处理记录
|
||||
*
|
||||
* @param sysWorkOrderRecord 工单处理记录
|
||||
* @return 结果
|
||||
*/
|
||||
public int updateSysWorkOrderRecord(SysWorkOrderRecord sysWorkOrderRecord);
|
||||
|
||||
/**
|
||||
* 批量删除工单处理记录
|
||||
*
|
||||
* @param recordIds 需要删除的工单处理记录主键集合
|
||||
* @return 结果
|
||||
*/
|
||||
public int deleteSysWorkOrderRecordByRecordIds(Long[] recordIds);
|
||||
|
||||
/**
|
||||
* 删除工单处理记录信息
|
||||
*
|
||||
* @param recordId 工单处理记录主键
|
||||
* @return 结果
|
||||
*/
|
||||
public int deleteSysWorkOrderRecordByRecordId(Long recordId);
|
||||
}
|
@ -0,0 +1,63 @@
|
||||
package com.storm.afterSales.service;
|
||||
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import java.util.List;
|
||||
import com.storm.afterSales.domain.SysWorkOrder;
|
||||
|
||||
/**
|
||||
* 工单主Service接口
|
||||
*
|
||||
* @author storm
|
||||
* @date 2025-10-02
|
||||
*/
|
||||
public interface ISysWorkOrderService extends IService<SysWorkOrder>
|
||||
{
|
||||
/**
|
||||
* 查询工单主
|
||||
*
|
||||
* @param orderId 工单主主键
|
||||
* @return 工单主
|
||||
*/
|
||||
public SysWorkOrder selectSysWorkOrderByOrderId(Long orderId);
|
||||
|
||||
/**
|
||||
* 查询工单主列表
|
||||
*
|
||||
* @param sysWorkOrder 工单主
|
||||
* @return 工单主集合
|
||||
*/
|
||||
public List<SysWorkOrder> selectSysWorkOrderList(SysWorkOrder sysWorkOrder);
|
||||
|
||||
/**
|
||||
* 新增工单主
|
||||
*
|
||||
* @param sysWorkOrder 工单主
|
||||
* @return 结果
|
||||
*/
|
||||
public int insertSysWorkOrder(SysWorkOrder sysWorkOrder);
|
||||
|
||||
/**
|
||||
* 修改工单主
|
||||
*
|
||||
* @param sysWorkOrder 工单主
|
||||
* @return 结果
|
||||
*/
|
||||
public int updateSysWorkOrder(SysWorkOrder sysWorkOrder);
|
||||
|
||||
/**
|
||||
* 批量删除工单主
|
||||
*
|
||||
* @param orderIds 需要删除的工单主主键集合
|
||||
* @return 结果
|
||||
*/
|
||||
public int deleteSysWorkOrderByOrderIds(Long[] orderIds);
|
||||
|
||||
/**
|
||||
* 删除工单主信息
|
||||
*
|
||||
* @param orderId 工单主主键
|
||||
* @return 结果
|
||||
*/
|
||||
public int deleteSysWorkOrderByOrderId(Long orderId);
|
||||
}
|
@ -0,0 +1,63 @@
|
||||
package com.storm.afterSales.service;
|
||||
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import java.util.List;
|
||||
import com.storm.afterSales.domain.SysWorkOrderType;
|
||||
|
||||
/**
|
||||
* 工单类型Service接口
|
||||
*
|
||||
* @author storm
|
||||
* @date 2025-10-02
|
||||
*/
|
||||
public interface ISysWorkOrderTypeService extends IService<SysWorkOrderType>
|
||||
{
|
||||
/**
|
||||
* 查询工单类型
|
||||
*
|
||||
* @param typeId 工单类型主键
|
||||
* @return 工单类型
|
||||
*/
|
||||
public SysWorkOrderType selectSysWorkOrderTypeByTypeId(Long typeId);
|
||||
|
||||
/**
|
||||
* 查询工单类型列表
|
||||
*
|
||||
* @param sysWorkOrderType 工单类型
|
||||
* @return 工单类型集合
|
||||
*/
|
||||
public List<SysWorkOrderType> selectSysWorkOrderTypeList(SysWorkOrderType sysWorkOrderType);
|
||||
|
||||
/**
|
||||
* 新增工单类型
|
||||
*
|
||||
* @param sysWorkOrderType 工单类型
|
||||
* @return 结果
|
||||
*/
|
||||
public int insertSysWorkOrderType(SysWorkOrderType sysWorkOrderType);
|
||||
|
||||
/**
|
||||
* 修改工单类型
|
||||
*
|
||||
* @param sysWorkOrderType 工单类型
|
||||
* @return 结果
|
||||
*/
|
||||
public int updateSysWorkOrderType(SysWorkOrderType sysWorkOrderType);
|
||||
|
||||
/**
|
||||
* 批量删除工单类型
|
||||
*
|
||||
* @param typeIds 需要删除的工单类型主键集合
|
||||
* @return 结果
|
||||
*/
|
||||
public int deleteSysWorkOrderTypeByTypeIds(Long[] typeIds);
|
||||
|
||||
/**
|
||||
* 删除工单类型信息
|
||||
*
|
||||
* @param typeId 工单类型主键
|
||||
* @return 结果
|
||||
*/
|
||||
public int deleteSysWorkOrderTypeByTypeId(Long typeId);
|
||||
}
|
@ -0,0 +1,96 @@
|
||||
package com.storm.afterSales.service.impl;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import com.storm.afterSales.mapper.SysKnowledgeRelMapper;
|
||||
import com.storm.afterSales.domain.SysKnowledgeRel;
|
||||
import com.storm.afterSales.service.ISysKnowledgeRelService;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* 工单知识库关联Service业务层处理
|
||||
*
|
||||
* @author storm
|
||||
* @date 2025-10-02
|
||||
*/
|
||||
@Service
|
||||
public class SysKnowledgeRelServiceImpl extends ServiceImpl<SysKnowledgeRelMapper, SysKnowledgeRel> implements ISysKnowledgeRelService
|
||||
{
|
||||
@Autowired
|
||||
private SysKnowledgeRelMapper sysKnowledgeRelMapper;
|
||||
|
||||
/**
|
||||
* 查询工单知识库关联
|
||||
*
|
||||
* @param id 工单知识库关联主键
|
||||
* @return 工单知识库关联
|
||||
*/
|
||||
@Override
|
||||
public SysKnowledgeRel selectSysKnowledgeRelById(Long id)
|
||||
{
|
||||
return getById(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询工单知识库关联列表
|
||||
*
|
||||
* @param sysKnowledgeRel 工单知识库关联
|
||||
* @return 工单知识库关联
|
||||
*/
|
||||
@Override
|
||||
public List<SysKnowledgeRel> selectSysKnowledgeRelList(SysKnowledgeRel sysKnowledgeRel)
|
||||
{
|
||||
return sysKnowledgeRelMapper.selectSysKnowledgeRelList(sysKnowledgeRel);
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增工单知识库关联
|
||||
*
|
||||
* @param sysKnowledgeRel 工单知识库关联
|
||||
* @return 结果
|
||||
*/
|
||||
@Override
|
||||
public int insertSysKnowledgeRel(SysKnowledgeRel sysKnowledgeRel)
|
||||
{
|
||||
return save(sysKnowledgeRel) ? 1 : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改工单知识库关联
|
||||
*
|
||||
* @param sysKnowledgeRel 工单知识库关联
|
||||
* @return 结果
|
||||
*/
|
||||
@Override
|
||||
public int updateSysKnowledgeRel(SysKnowledgeRel sysKnowledgeRel)
|
||||
{
|
||||
return updateById(sysKnowledgeRel) ? 1 : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量删除工单知识库关联
|
||||
*
|
||||
* @param ids 需要删除的工单知识库关联主键
|
||||
* @return 结果
|
||||
*/
|
||||
@Override
|
||||
public int deleteSysKnowledgeRelByIds(Long[] ids)
|
||||
{
|
||||
return removeByIds(Arrays.asList(ids)) ? 1 : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除工单知识库关联信息
|
||||
*
|
||||
* @param id 工单知识库关联主键
|
||||
* @return 结果
|
||||
*/
|
||||
@Override
|
||||
public int deleteSysKnowledgeRelById(Long id)
|
||||
{
|
||||
return removeById(id) ? 1 : 0;
|
||||
}
|
||||
}
|
@ -0,0 +1,96 @@
|
||||
package com.storm.afterSales.service.impl;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import com.storm.afterSales.mapper.SysWorkOrderChatRelMapper;
|
||||
import com.storm.afterSales.domain.SysWorkOrderChatRel;
|
||||
import com.storm.afterSales.service.ISysWorkOrderChatRelService;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* 工单聊天关联Service业务层处理
|
||||
*
|
||||
* @author storm
|
||||
* @date 2025-10-02
|
||||
*/
|
||||
@Service
|
||||
public class SysWorkOrderChatRelServiceImpl extends ServiceImpl<SysWorkOrderChatRelMapper, SysWorkOrderChatRel> implements ISysWorkOrderChatRelService
|
||||
{
|
||||
@Autowired
|
||||
private SysWorkOrderChatRelMapper sysWorkOrderChatRelMapper;
|
||||
|
||||
/**
|
||||
* 查询工单聊天关联
|
||||
*
|
||||
* @param id 工单聊天关联主键
|
||||
* @return 工单聊天关联
|
||||
*/
|
||||
@Override
|
||||
public SysWorkOrderChatRel selectSysWorkOrderChatRelById(Long id)
|
||||
{
|
||||
return getById(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询工单聊天关联列表
|
||||
*
|
||||
* @param sysWorkOrderChatRel 工单聊天关联
|
||||
* @return 工单聊天关联
|
||||
*/
|
||||
@Override
|
||||
public List<SysWorkOrderChatRel> selectSysWorkOrderChatRelList(SysWorkOrderChatRel sysWorkOrderChatRel)
|
||||
{
|
||||
return sysWorkOrderChatRelMapper.selectSysWorkOrderChatRelList(sysWorkOrderChatRel);
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增工单聊天关联
|
||||
*
|
||||
* @param sysWorkOrderChatRel 工单聊天关联
|
||||
* @return 结果
|
||||
*/
|
||||
@Override
|
||||
public int insertSysWorkOrderChatRel(SysWorkOrderChatRel sysWorkOrderChatRel)
|
||||
{
|
||||
return save(sysWorkOrderChatRel) ? 1 : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改工单聊天关联
|
||||
*
|
||||
* @param sysWorkOrderChatRel 工单聊天关联
|
||||
* @return 结果
|
||||
*/
|
||||
@Override
|
||||
public int updateSysWorkOrderChatRel(SysWorkOrderChatRel sysWorkOrderChatRel)
|
||||
{
|
||||
return updateById(sysWorkOrderChatRel) ? 1 : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量删除工单聊天关联
|
||||
*
|
||||
* @param ids 需要删除的工单聊天关联主键
|
||||
* @return 结果
|
||||
*/
|
||||
@Override
|
||||
public int deleteSysWorkOrderChatRelByIds(Long[] ids)
|
||||
{
|
||||
return removeByIds(Arrays.asList(ids)) ? 1 : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除工单聊天关联信息
|
||||
*
|
||||
* @param id 工单聊天关联主键
|
||||
* @return 结果
|
||||
*/
|
||||
@Override
|
||||
public int deleteSysWorkOrderChatRelById(Long id)
|
||||
{
|
||||
return removeById(id) ? 1 : 0;
|
||||
}
|
||||
}
|
@ -0,0 +1,95 @@
|
||||
package com.storm.afterSales.service.impl;
|
||||
|
||||
import java.util.List;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import com.storm.afterSales.mapper.SysWorkOrderRecordMapper;
|
||||
import com.storm.afterSales.domain.SysWorkOrderRecord;
|
||||
import com.storm.afterSales.service.ISysWorkOrderRecordService;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* 工单处理记录Service业务层处理
|
||||
*
|
||||
* @author storm
|
||||
* @date 2025-10-02
|
||||
*/
|
||||
@Service
|
||||
public class SysWorkOrderRecordServiceImpl extends ServiceImpl<SysWorkOrderRecordMapper, SysWorkOrderRecord> implements ISysWorkOrderRecordService
|
||||
{
|
||||
@Autowired
|
||||
private SysWorkOrderRecordMapper sysWorkOrderRecordMapper;
|
||||
|
||||
/**
|
||||
* 查询工单处理记录
|
||||
*
|
||||
* @param recordId 工单处理记录主键
|
||||
* @return 工单处理记录
|
||||
*/
|
||||
@Override
|
||||
public SysWorkOrderRecord selectSysWorkOrderRecordByRecordId(Long recordId)
|
||||
{
|
||||
return getById(recordId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询工单处理记录列表
|
||||
*
|
||||
* @param sysWorkOrderRecord 工单处理记录
|
||||
* @return 工单处理记录
|
||||
*/
|
||||
@Override
|
||||
public List<SysWorkOrderRecord> selectSysWorkOrderRecordList(SysWorkOrderRecord sysWorkOrderRecord)
|
||||
{
|
||||
return sysWorkOrderRecordMapper.selectSysWorkOrderRecordList(sysWorkOrderRecord);
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增工单处理记录
|
||||
*
|
||||
* @param sysWorkOrderRecord 工单处理记录
|
||||
* @return 结果
|
||||
*/
|
||||
@Override
|
||||
public int insertSysWorkOrderRecord(SysWorkOrderRecord sysWorkOrderRecord)
|
||||
{
|
||||
return save(sysWorkOrderRecord) ? 1 : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改工单处理记录
|
||||
*
|
||||
* @param sysWorkOrderRecord 工单处理记录
|
||||
* @return 结果
|
||||
*/
|
||||
@Override
|
||||
public int updateSysWorkOrderRecord(SysWorkOrderRecord sysWorkOrderRecord)
|
||||
{
|
||||
return updateById(sysWorkOrderRecord) ? 1 : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量删除工单处理记录
|
||||
*
|
||||
* @param recordIds 需要删除的工单处理记录主键
|
||||
* @return 结果
|
||||
*/
|
||||
@Override
|
||||
public int deleteSysWorkOrderRecordByRecordIds(Long[] recordIds)
|
||||
{
|
||||
return removeByIds(Arrays.asList(recordIds)) ? 1 : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除工单处理记录信息
|
||||
*
|
||||
* @param recordId 工单处理记录主键
|
||||
* @return 结果
|
||||
*/
|
||||
@Override
|
||||
public int deleteSysWorkOrderRecordByRecordId(Long recordId)
|
||||
{
|
||||
return removeById(recordId) ? 1 : 0;
|
||||
}
|
||||
}
|
@ -0,0 +1,96 @@
|
||||
package com.storm.afterSales.service.impl;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import com.storm.afterSales.mapper.SysWorkOrderMapper;
|
||||
import com.storm.afterSales.domain.SysWorkOrder;
|
||||
import com.storm.afterSales.service.ISysWorkOrderService;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* 工单主Service业务层处理
|
||||
*
|
||||
* @author storm
|
||||
* @date 2025-10-02
|
||||
*/
|
||||
@Service
|
||||
public class SysWorkOrderServiceImpl extends ServiceImpl<SysWorkOrderMapper, SysWorkOrder> implements ISysWorkOrderService
|
||||
{
|
||||
@Autowired
|
||||
private SysWorkOrderMapper sysWorkOrderMapper;
|
||||
|
||||
/**
|
||||
* 查询工单主
|
||||
*
|
||||
* @param orderId 工单主主键
|
||||
* @return 工单主
|
||||
*/
|
||||
@Override
|
||||
public SysWorkOrder selectSysWorkOrderByOrderId(Long orderId)
|
||||
{
|
||||
return getById(orderId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询工单主列表
|
||||
*
|
||||
* @param sysWorkOrder 工单主
|
||||
* @return 工单主
|
||||
*/
|
||||
@Override
|
||||
public List<SysWorkOrder> selectSysWorkOrderList(SysWorkOrder sysWorkOrder)
|
||||
{
|
||||
return sysWorkOrderMapper.selectSysWorkOrderList(sysWorkOrder);
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增工单主
|
||||
*
|
||||
* @param sysWorkOrder 工单主
|
||||
* @return 结果
|
||||
*/
|
||||
@Override
|
||||
public int insertSysWorkOrder(SysWorkOrder sysWorkOrder)
|
||||
{
|
||||
return save(sysWorkOrder) ? 1 : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改工单主
|
||||
*
|
||||
* @param sysWorkOrder 工单主
|
||||
* @return 结果
|
||||
*/
|
||||
@Override
|
||||
public int updateSysWorkOrder(SysWorkOrder sysWorkOrder)
|
||||
{
|
||||
return updateById(sysWorkOrder) ? 1 : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量删除工单主
|
||||
*
|
||||
* @param orderIds 需要删除的工单主主键
|
||||
* @return 结果
|
||||
*/
|
||||
@Override
|
||||
public int deleteSysWorkOrderByOrderIds(Long[] orderIds)
|
||||
{
|
||||
return removeByIds(Arrays.asList(orderIds)) ? 1 : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除工单主信息
|
||||
*
|
||||
* @param orderId 工单主主键
|
||||
* @return 结果
|
||||
*/
|
||||
@Override
|
||||
public int deleteSysWorkOrderByOrderId(Long orderId)
|
||||
{
|
||||
return removeById(orderId) ? 1 : 0;
|
||||
}
|
||||
}
|
@ -0,0 +1,96 @@
|
||||
package com.storm.afterSales.service.impl;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import com.storm.afterSales.mapper.SysWorkOrderTypeMapper;
|
||||
import com.storm.afterSales.domain.SysWorkOrderType;
|
||||
import com.storm.afterSales.service.ISysWorkOrderTypeService;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* 工单类型Service业务层处理
|
||||
*
|
||||
* @author storm
|
||||
* @date 2025-10-02
|
||||
*/
|
||||
@Service
|
||||
public class SysWorkOrderTypeServiceImpl extends ServiceImpl<SysWorkOrderTypeMapper, SysWorkOrderType> implements ISysWorkOrderTypeService
|
||||
{
|
||||
@Autowired
|
||||
private SysWorkOrderTypeMapper sysWorkOrderTypeMapper;
|
||||
|
||||
/**
|
||||
* 查询工单类型
|
||||
*
|
||||
* @param typeId 工单类型主键
|
||||
* @return 工单类型
|
||||
*/
|
||||
@Override
|
||||
public SysWorkOrderType selectSysWorkOrderTypeByTypeId(Long typeId)
|
||||
{
|
||||
return getById(typeId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询工单类型列表
|
||||
*
|
||||
* @param sysWorkOrderType 工单类型
|
||||
* @return 工单类型
|
||||
*/
|
||||
@Override
|
||||
public List<SysWorkOrderType> selectSysWorkOrderTypeList(SysWorkOrderType sysWorkOrderType)
|
||||
{
|
||||
return sysWorkOrderTypeMapper.selectSysWorkOrderTypeList(sysWorkOrderType);
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增工单类型
|
||||
*
|
||||
* @param sysWorkOrderType 工单类型
|
||||
* @return 结果
|
||||
*/
|
||||
@Override
|
||||
public int insertSysWorkOrderType(SysWorkOrderType sysWorkOrderType)
|
||||
{
|
||||
return save(sysWorkOrderType) ? 1 : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改工单类型
|
||||
*
|
||||
* @param sysWorkOrderType 工单类型
|
||||
* @return 结果
|
||||
*/
|
||||
@Override
|
||||
public int updateSysWorkOrderType(SysWorkOrderType sysWorkOrderType)
|
||||
{
|
||||
return updateById(sysWorkOrderType) ? 1 : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量删除工单类型
|
||||
*
|
||||
* @param typeIds 需要删除的工单类型主键
|
||||
* @return 结果
|
||||
*/
|
||||
@Override
|
||||
public int deleteSysWorkOrderTypeByTypeIds(Long[] typeIds)
|
||||
{
|
||||
return removeByIds(Arrays.asList(typeIds)) ? 1 : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除工单类型信息
|
||||
*
|
||||
* @param typeId 工单类型主键
|
||||
* @return 结果
|
||||
*/
|
||||
@Override
|
||||
public int deleteSysWorkOrderTypeByTypeId(Long typeId)
|
||||
{
|
||||
return removeById(typeId) ? 1 : 0;
|
||||
}
|
||||
}
|
@ -0,0 +1 @@
|
||||
spring.application.name=storm-afterSales
|
@ -0,0 +1,75 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<!DOCTYPE mapper
|
||||
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="com.storm.afterSales.mapper.SysKnowledgeRelMapper">
|
||||
|
||||
<resultMap type="SysKnowledgeRel" id="SysKnowledgeRelResult">
|
||||
<result property="id" column="id" />
|
||||
<result property="orderId" column="order_id" />
|
||||
<result property="knowledgeId" column="knowledge_id" />
|
||||
<result property="knowledgeTitle" column="knowledge_title" />
|
||||
<result property="usedCount" column="used_count" />
|
||||
<result property="createTime" column="create_time" />
|
||||
</resultMap>
|
||||
|
||||
<sql id="selectSysKnowledgeRelVo">
|
||||
select id, order_id, knowledge_id, knowledge_title, used_count, create_time from sys_knowledge_rel
|
||||
</sql>
|
||||
|
||||
<select id="selectSysKnowledgeRelList" parameterType="SysKnowledgeRel" resultMap="SysKnowledgeRelResult">
|
||||
<include refid="selectSysKnowledgeRelVo"/>
|
||||
<where>
|
||||
<if test="orderId != null "> and order_id = #{orderId}</if>
|
||||
<if test="knowledgeId != null "> and knowledge_id = #{knowledgeId}</if>
|
||||
<if test="knowledgeTitle != null and knowledgeTitle != ''"> and knowledge_title = #{knowledgeTitle}</if>
|
||||
<if test="usedCount != null "> and used_count = #{usedCount}</if>
|
||||
</where>
|
||||
</select>
|
||||
|
||||
<select id="selectSysKnowledgeRelById" parameterType="Long" resultMap="SysKnowledgeRelResult">
|
||||
<include refid="selectSysKnowledgeRelVo"/>
|
||||
where id = #{id}
|
||||
</select>
|
||||
|
||||
<insert id="insertSysKnowledgeRel" parameterType="SysKnowledgeRel" useGeneratedKeys="true" keyProperty="id">
|
||||
insert into sys_knowledge_rel
|
||||
<trim prefix="(" suffix=")" suffixOverrides=",">
|
||||
<if test="orderId != null">order_id,</if>
|
||||
<if test="knowledgeId != null">knowledge_id,</if>
|
||||
<if test="knowledgeTitle != null">knowledge_title,</if>
|
||||
<if test="usedCount != null">used_count,</if>
|
||||
<if test="createTime != null">create_time,</if>
|
||||
</trim>
|
||||
<trim prefix="values (" suffix=")" suffixOverrides=",">
|
||||
<if test="orderId != null">#{orderId},</if>
|
||||
<if test="knowledgeId != null">#{knowledgeId},</if>
|
||||
<if test="knowledgeTitle != null">#{knowledgeTitle},</if>
|
||||
<if test="usedCount != null">#{usedCount},</if>
|
||||
<if test="createTime != null">#{createTime},</if>
|
||||
</trim>
|
||||
</insert>
|
||||
|
||||
<update id="updateSysKnowledgeRel" parameterType="SysKnowledgeRel">
|
||||
update sys_knowledge_rel
|
||||
<trim prefix="SET" suffixOverrides=",">
|
||||
<if test="orderId != null">order_id = #{orderId},</if>
|
||||
<if test="knowledgeId != null">knowledge_id = #{knowledgeId},</if>
|
||||
<if test="knowledgeTitle != null">knowledge_title = #{knowledgeTitle},</if>
|
||||
<if test="usedCount != null">used_count = #{usedCount},</if>
|
||||
<if test="createTime != null">create_time = #{createTime},</if>
|
||||
</trim>
|
||||
where id = #{id}
|
||||
</update>
|
||||
|
||||
<delete id="deleteSysKnowledgeRelById" parameterType="Long">
|
||||
delete from sys_knowledge_rel where id = #{id}
|
||||
</delete>
|
||||
|
||||
<delete id="deleteSysKnowledgeRelByIds" parameterType="String">
|
||||
delete from sys_knowledge_rel where id in
|
||||
<foreach item="id" collection="array" open="(" separator="," close=")">
|
||||
#{id}
|
||||
</foreach>
|
||||
</delete>
|
||||
</mapper>
|
@ -0,0 +1,85 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<!DOCTYPE mapper
|
||||
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="com.storm.afterSales.mapper.SysWorkOrderChatRelMapper">
|
||||
|
||||
<resultMap type="SysWorkOrderChatRel" id="SysWorkOrderChatRelResult">
|
||||
<result property="id" column="id" />
|
||||
<result property="orderId" column="order_id" />
|
||||
<result property="chatSessionId" column="chat_session_id" />
|
||||
<result property="chatPlatform" column="chat_platform" />
|
||||
<result property="messageId" column="message_id" />
|
||||
<result property="chatContent" column="chat_content" />
|
||||
<result property="chatDuration" column="chat_duration" />
|
||||
<result property="createTime" column="create_time" />
|
||||
</resultMap>
|
||||
|
||||
<sql id="selectSysWorkOrderChatRelVo">
|
||||
select id, order_id, chat_session_id, chat_platform, message_id, chat_content, chat_duration, create_time from sys_work_order_chat_rel
|
||||
</sql>
|
||||
|
||||
<select id="selectSysWorkOrderChatRelList" parameterType="SysWorkOrderChatRel" resultMap="SysWorkOrderChatRelResult">
|
||||
<include refid="selectSysWorkOrderChatRelVo"/>
|
||||
<where>
|
||||
<if test="orderId != null "> and order_id = #{orderId}</if>
|
||||
<if test="chatSessionId != null and chatSessionId != ''"> and chat_session_id = #{chatSessionId}</if>
|
||||
<if test="chatPlatform != null and chatPlatform != ''"> and chat_platform = #{chatPlatform}</if>
|
||||
<if test="messageId != null and messageId != ''"> and message_id = #{messageId}</if>
|
||||
<if test="chatContent != null and chatContent != ''"> and chat_content = #{chatContent}</if>
|
||||
<if test="chatDuration != null "> and chat_duration = #{chatDuration}</if>
|
||||
</where>
|
||||
</select>
|
||||
|
||||
<select id="selectSysWorkOrderChatRelById" parameterType="Long" resultMap="SysWorkOrderChatRelResult">
|
||||
<include refid="selectSysWorkOrderChatRelVo"/>
|
||||
where id = #{id}
|
||||
</select>
|
||||
|
||||
<insert id="insertSysWorkOrderChatRel" parameterType="SysWorkOrderChatRel" useGeneratedKeys="true" keyProperty="id">
|
||||
insert into sys_work_order_chat_rel
|
||||
<trim prefix="(" suffix=")" suffixOverrides=",">
|
||||
<if test="orderId != null">order_id,</if>
|
||||
<if test="chatSessionId != null">chat_session_id,</if>
|
||||
<if test="chatPlatform != null">chat_platform,</if>
|
||||
<if test="messageId != null">message_id,</if>
|
||||
<if test="chatContent != null">chat_content,</if>
|
||||
<if test="chatDuration != null">chat_duration,</if>
|
||||
<if test="createTime != null">create_time,</if>
|
||||
</trim>
|
||||
<trim prefix="values (" suffix=")" suffixOverrides=",">
|
||||
<if test="orderId != null">#{orderId},</if>
|
||||
<if test="chatSessionId != null">#{chatSessionId},</if>
|
||||
<if test="chatPlatform != null">#{chatPlatform},</if>
|
||||
<if test="messageId != null">#{messageId},</if>
|
||||
<if test="chatContent != null">#{chatContent},</if>
|
||||
<if test="chatDuration != null">#{chatDuration},</if>
|
||||
<if test="createTime != null">#{createTime},</if>
|
||||
</trim>
|
||||
</insert>
|
||||
|
||||
<update id="updateSysWorkOrderChatRel" parameterType="SysWorkOrderChatRel">
|
||||
update sys_work_order_chat_rel
|
||||
<trim prefix="SET" suffixOverrides=",">
|
||||
<if test="orderId != null">order_id = #{orderId},</if>
|
||||
<if test="chatSessionId != null">chat_session_id = #{chatSessionId},</if>
|
||||
<if test="chatPlatform != null">chat_platform = #{chatPlatform},</if>
|
||||
<if test="messageId != null">message_id = #{messageId},</if>
|
||||
<if test="chatContent != null">chat_content = #{chatContent},</if>
|
||||
<if test="chatDuration != null">chat_duration = #{chatDuration},</if>
|
||||
<if test="createTime != null">create_time = #{createTime},</if>
|
||||
</trim>
|
||||
where id = #{id}
|
||||
</update>
|
||||
|
||||
<delete id="deleteSysWorkOrderChatRelById" parameterType="Long">
|
||||
delete from sys_work_order_chat_rel where id = #{id}
|
||||
</delete>
|
||||
|
||||
<delete id="deleteSysWorkOrderChatRelByIds" parameterType="String">
|
||||
delete from sys_work_order_chat_rel where id in
|
||||
<foreach item="id" collection="array" open="(" separator="," close=")">
|
||||
#{id}
|
||||
</foreach>
|
||||
</delete>
|
||||
</mapper>
|
@ -0,0 +1,241 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<!DOCTYPE mapper
|
||||
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="com.storm.afterSales.mapper.SysWorkOrderMapper">
|
||||
|
||||
<resultMap type="SysWorkOrder" id="SysWorkOrderResult">
|
||||
<result property="orderId" column="order_id" />
|
||||
<result property="orderNo" column="order_no" />
|
||||
<result property="orderTitle" column="order_title" />
|
||||
<result property="orderType" column="order_type" />
|
||||
<result property="orderStatus" column="order_status" />
|
||||
<result property="priority" column="priority" />
|
||||
<result property="customerId" column="customer_id" />
|
||||
<result property="customerName" column="customer_name" />
|
||||
<result property="customerContact" column="customer_contact" />
|
||||
<result property="storeAddress" column="store_address" />
|
||||
<result property="deviceName" column="device_name" />
|
||||
<result property="deviceSerialNo" column="device_serial_no" />
|
||||
<result property="deviceModel" column="device_model" />
|
||||
<result property="productionDate" column="production_date" />
|
||||
<result property="problemDescription" column="problem_description" />
|
||||
<result property="attachmentUrls" column="attachment_urls" />
|
||||
<result property="problemCategory" column="problem_category" />
|
||||
<result property="assigneeId" column="assignee_id" />
|
||||
<result property="assigneeName" column="assignee_name" />
|
||||
<result property="assignTime" column="assign_time" />
|
||||
<result property="currentHandlerId" column="current_handler_id" />
|
||||
<result property="currentHandlerName" column="current_handler_name" />
|
||||
<result property="rootCause" column="root_cause" />
|
||||
<result property="solutionPlan" column="solution_plan" />
|
||||
<result property="finalSolution" column="final_solution" />
|
||||
<result property="logisticsNumber" column="logistics_number" />
|
||||
<result property="logisticsProgress" column="logistics_progress" />
|
||||
<result property="customerFeedback" column="customer_feedback" />
|
||||
<result property="satisfactionLevel" column="satisfaction_level" />
|
||||
<result property="feedbackType" column="feedback_type" />
|
||||
<result property="planFinishTime" column="plan_finish_time" />
|
||||
<result property="actualFinishTime" column="actual_finish_time" />
|
||||
<result property="responseDeadline" column="response_deadline" />
|
||||
<result property="sourceChannel" column="source_channel" />
|
||||
<result property="feishuChatId" column="feishu_chat_id" />
|
||||
<result property="createBy" column="create_by" />
|
||||
<result property="createTime" column="create_time" />
|
||||
<result property="updateBy" column="update_by" />
|
||||
<result property="updateTime" column="update_time" />
|
||||
<result property="remark" column="remark" />
|
||||
</resultMap>
|
||||
|
||||
<sql id="selectSysWorkOrderVo">
|
||||
select order_id, order_no, order_title, order_type, order_status, priority, customer_id, customer_name, customer_contact, store_address, device_name, device_serial_no, device_model, production_date, problem_description, attachment_urls, problem_category, assignee_id, assignee_name, assign_time, current_handler_id, current_handler_name, root_cause, solution_plan, final_solution, logistics_number, logistics_progress, customer_feedback, satisfaction_level, feedback_type, plan_finish_time, actual_finish_time, response_deadline, source_channel, feishu_chat_id, create_by, create_time, update_by, update_time, remark from sys_work_order
|
||||
</sql>
|
||||
|
||||
<select id="selectSysWorkOrderList" parameterType="SysWorkOrder" resultMap="SysWorkOrderResult">
|
||||
<include refid="selectSysWorkOrderVo"/>
|
||||
<where>
|
||||
<if test="orderNo != null and orderNo != ''"> and order_no = #{orderNo}</if>
|
||||
<if test="orderTitle != null and orderTitle != ''"> and order_title = #{orderTitle}</if>
|
||||
<if test="orderType != null and orderType != ''"> and order_type = #{orderType}</if>
|
||||
<if test="orderStatus != null and orderStatus != ''"> and order_status = #{orderStatus}</if>
|
||||
<if test="priority != null and priority != ''"> and priority = #{priority}</if>
|
||||
<if test="customerId != null "> and customer_id = #{customerId}</if>
|
||||
<if test="customerName != null and customerName != ''"> and customer_name like concat('%', #{customerName}, '%')</if>
|
||||
<if test="customerContact != null and customerContact != ''"> and customer_contact = #{customerContact}</if>
|
||||
<if test="storeAddress != null and storeAddress != ''"> and store_address = #{storeAddress}</if>
|
||||
<if test="deviceName != null and deviceName != ''"> and device_name like concat('%', #{deviceName}, '%')</if>
|
||||
<if test="deviceSerialNo != null and deviceSerialNo != ''"> and device_serial_no = #{deviceSerialNo}</if>
|
||||
<if test="deviceModel != null and deviceModel != ''"> and device_model = #{deviceModel}</if>
|
||||
<if test="productionDate != null "> and production_date = #{productionDate}</if>
|
||||
<if test="problemDescription != null and problemDescription != ''"> and problem_description = #{problemDescription}</if>
|
||||
<if test="attachmentUrls != null and attachmentUrls != ''"> and attachment_urls = #{attachmentUrls}</if>
|
||||
<if test="problemCategory != null and problemCategory != ''"> and problem_category = #{problemCategory}</if>
|
||||
<if test="assigneeId != null "> and assignee_id = #{assigneeId}</if>
|
||||
<if test="assigneeName != null and assigneeName != ''"> and assignee_name like concat('%', #{assigneeName}, '%')</if>
|
||||
<if test="assignTime != null "> and assign_time = #{assignTime}</if>
|
||||
<if test="currentHandlerId != null "> and current_handler_id = #{currentHandlerId}</if>
|
||||
<if test="currentHandlerName != null and currentHandlerName != ''"> and current_handler_name like concat('%', #{currentHandlerName}, '%')</if>
|
||||
<if test="rootCause != null and rootCause != ''"> and root_cause = #{rootCause}</if>
|
||||
<if test="solutionPlan != null and solutionPlan != ''"> and solution_plan = #{solutionPlan}</if>
|
||||
<if test="finalSolution != null and finalSolution != ''"> and final_solution = #{finalSolution}</if>
|
||||
<if test="logisticsNumber != null and logisticsNumber != ''"> and logistics_number = #{logisticsNumber}</if>
|
||||
<if test="logisticsProgress != null and logisticsProgress != ''"> and logistics_progress = #{logisticsProgress}</if>
|
||||
<if test="customerFeedback != null and customerFeedback != ''"> and customer_feedback = #{customerFeedback}</if>
|
||||
<if test="satisfactionLevel != null "> and satisfaction_level = #{satisfactionLevel}</if>
|
||||
<if test="feedbackType != null and feedbackType != ''"> and feedback_type = #{feedbackType}</if>
|
||||
<if test="planFinishTime != null "> and plan_finish_time = #{planFinishTime}</if>
|
||||
<if test="actualFinishTime != null "> and actual_finish_time = #{actualFinishTime}</if>
|
||||
<if test="responseDeadline != null "> and response_deadline = #{responseDeadline}</if>
|
||||
<if test="sourceChannel != null and sourceChannel != ''"> and source_channel = #{sourceChannel}</if>
|
||||
<if test="feishuChatId != null and feishuChatId != ''"> and feishu_chat_id = #{feishuChatId}</if>
|
||||
</where>
|
||||
</select>
|
||||
|
||||
<select id="selectSysWorkOrderByOrderId" parameterType="Long" resultMap="SysWorkOrderResult">
|
||||
<include refid="selectSysWorkOrderVo"/>
|
||||
where order_id = #{orderId}
|
||||
</select>
|
||||
|
||||
<insert id="insertSysWorkOrder" parameterType="SysWorkOrder" useGeneratedKeys="true" keyProperty="orderId">
|
||||
insert into sys_work_order
|
||||
<trim prefix="(" suffix=")" suffixOverrides=",">
|
||||
<if test="orderNo != null and orderNo != ''">order_no,</if>
|
||||
<if test="orderTitle != null and orderTitle != ''">order_title,</if>
|
||||
<if test="orderType != null">order_type,</if>
|
||||
<if test="orderStatus != null">order_status,</if>
|
||||
<if test="priority != null">priority,</if>
|
||||
<if test="customerId != null">customer_id,</if>
|
||||
<if test="customerName != null">customer_name,</if>
|
||||
<if test="customerContact != null">customer_contact,</if>
|
||||
<if test="storeAddress != null">store_address,</if>
|
||||
<if test="deviceName != null">device_name,</if>
|
||||
<if test="deviceSerialNo != null">device_serial_no,</if>
|
||||
<if test="deviceModel != null">device_model,</if>
|
||||
<if test="productionDate != null">production_date,</if>
|
||||
<if test="problemDescription != null">problem_description,</if>
|
||||
<if test="attachmentUrls != null">attachment_urls,</if>
|
||||
<if test="problemCategory != null">problem_category,</if>
|
||||
<if test="assigneeId != null">assignee_id,</if>
|
||||
<if test="assigneeName != null">assignee_name,</if>
|
||||
<if test="assignTime != null">assign_time,</if>
|
||||
<if test="currentHandlerId != null">current_handler_id,</if>
|
||||
<if test="currentHandlerName != null">current_handler_name,</if>
|
||||
<if test="rootCause != null">root_cause,</if>
|
||||
<if test="solutionPlan != null">solution_plan,</if>
|
||||
<if test="finalSolution != null">final_solution,</if>
|
||||
<if test="logisticsNumber != null">logistics_number,</if>
|
||||
<if test="logisticsProgress != null">logistics_progress,</if>
|
||||
<if test="customerFeedback != null">customer_feedback,</if>
|
||||
<if test="satisfactionLevel != null">satisfaction_level,</if>
|
||||
<if test="feedbackType != null">feedback_type,</if>
|
||||
<if test="planFinishTime != null">plan_finish_time,</if>
|
||||
<if test="actualFinishTime != null">actual_finish_time,</if>
|
||||
<if test="responseDeadline != null">response_deadline,</if>
|
||||
<if test="sourceChannel != null">source_channel,</if>
|
||||
<if test="feishuChatId != null">feishu_chat_id,</if>
|
||||
<if test="createBy != null">create_by,</if>
|
||||
<if test="createTime != null">create_time,</if>
|
||||
<if test="updateBy != null">update_by,</if>
|
||||
<if test="updateTime != null">update_time,</if>
|
||||
<if test="remark != null">remark,</if>
|
||||
</trim>
|
||||
<trim prefix="values (" suffix=")" suffixOverrides=",">
|
||||
<if test="orderNo != null and orderNo != ''">#{orderNo},</if>
|
||||
<if test="orderTitle != null and orderTitle != ''">#{orderTitle},</if>
|
||||
<if test="orderType != null">#{orderType},</if>
|
||||
<if test="orderStatus != null">#{orderStatus},</if>
|
||||
<if test="priority != null">#{priority},</if>
|
||||
<if test="customerId != null">#{customerId},</if>
|
||||
<if test="customerName != null">#{customerName},</if>
|
||||
<if test="customerContact != null">#{customerContact},</if>
|
||||
<if test="storeAddress != null">#{storeAddress},</if>
|
||||
<if test="deviceName != null">#{deviceName},</if>
|
||||
<if test="deviceSerialNo != null">#{deviceSerialNo},</if>
|
||||
<if test="deviceModel != null">#{deviceModel},</if>
|
||||
<if test="productionDate != null">#{productionDate},</if>
|
||||
<if test="problemDescription != null">#{problemDescription},</if>
|
||||
<if test="attachmentUrls != null">#{attachmentUrls},</if>
|
||||
<if test="problemCategory != null">#{problemCategory},</if>
|
||||
<if test="assigneeId != null">#{assigneeId},</if>
|
||||
<if test="assigneeName != null">#{assigneeName},</if>
|
||||
<if test="assignTime != null">#{assignTime},</if>
|
||||
<if test="currentHandlerId != null">#{currentHandlerId},</if>
|
||||
<if test="currentHandlerName != null">#{currentHandlerName},</if>
|
||||
<if test="rootCause != null">#{rootCause},</if>
|
||||
<if test="solutionPlan != null">#{solutionPlan},</if>
|
||||
<if test="finalSolution != null">#{finalSolution},</if>
|
||||
<if test="logisticsNumber != null">#{logisticsNumber},</if>
|
||||
<if test="logisticsProgress != null">#{logisticsProgress},</if>
|
||||
<if test="customerFeedback != null">#{customerFeedback},</if>
|
||||
<if test="satisfactionLevel != null">#{satisfactionLevel},</if>
|
||||
<if test="feedbackType != null">#{feedbackType},</if>
|
||||
<if test="planFinishTime != null">#{planFinishTime},</if>
|
||||
<if test="actualFinishTime != null">#{actualFinishTime},</if>
|
||||
<if test="responseDeadline != null">#{responseDeadline},</if>
|
||||
<if test="sourceChannel != null">#{sourceChannel},</if>
|
||||
<if test="feishuChatId != null">#{feishuChatId},</if>
|
||||
<if test="createBy != null">#{createBy},</if>
|
||||
<if test="createTime != null">#{createTime},</if>
|
||||
<if test="updateBy != null">#{updateBy},</if>
|
||||
<if test="updateTime != null">#{updateTime},</if>
|
||||
<if test="remark != null">#{remark},</if>
|
||||
</trim>
|
||||
</insert>
|
||||
|
||||
<update id="updateSysWorkOrder" parameterType="SysWorkOrder">
|
||||
update sys_work_order
|
||||
<trim prefix="SET" suffixOverrides=",">
|
||||
<if test="orderNo != null and orderNo != ''">order_no = #{orderNo},</if>
|
||||
<if test="orderTitle != null and orderTitle != ''">order_title = #{orderTitle},</if>
|
||||
<if test="orderType != null">order_type = #{orderType},</if>
|
||||
<if test="orderStatus != null">order_status = #{orderStatus},</if>
|
||||
<if test="priority != null">priority = #{priority},</if>
|
||||
<if test="customerId != null">customer_id = #{customerId},</if>
|
||||
<if test="customerName != null">customer_name = #{customerName},</if>
|
||||
<if test="customerContact != null">customer_contact = #{customerContact},</if>
|
||||
<if test="storeAddress != null">store_address = #{storeAddress},</if>
|
||||
<if test="deviceName != null">device_name = #{deviceName},</if>
|
||||
<if test="deviceSerialNo != null">device_serial_no = #{deviceSerialNo},</if>
|
||||
<if test="deviceModel != null">device_model = #{deviceModel},</if>
|
||||
<if test="productionDate != null">production_date = #{productionDate},</if>
|
||||
<if test="problemDescription != null">problem_description = #{problemDescription},</if>
|
||||
<if test="attachmentUrls != null">attachment_urls = #{attachmentUrls},</if>
|
||||
<if test="problemCategory != null">problem_category = #{problemCategory},</if>
|
||||
<if test="assigneeId != null">assignee_id = #{assigneeId},</if>
|
||||
<if test="assigneeName != null">assignee_name = #{assigneeName},</if>
|
||||
<if test="assignTime != null">assign_time = #{assignTime},</if>
|
||||
<if test="currentHandlerId != null">current_handler_id = #{currentHandlerId},</if>
|
||||
<if test="currentHandlerName != null">current_handler_name = #{currentHandlerName},</if>
|
||||
<if test="rootCause != null">root_cause = #{rootCause},</if>
|
||||
<if test="solutionPlan != null">solution_plan = #{solutionPlan},</if>
|
||||
<if test="finalSolution != null">final_solution = #{finalSolution},</if>
|
||||
<if test="logisticsNumber != null">logistics_number = #{logisticsNumber},</if>
|
||||
<if test="logisticsProgress != null">logistics_progress = #{logisticsProgress},</if>
|
||||
<if test="customerFeedback != null">customer_feedback = #{customerFeedback},</if>
|
||||
<if test="satisfactionLevel != null">satisfaction_level = #{satisfactionLevel},</if>
|
||||
<if test="feedbackType != null">feedback_type = #{feedbackType},</if>
|
||||
<if test="planFinishTime != null">plan_finish_time = #{planFinishTime},</if>
|
||||
<if test="actualFinishTime != null">actual_finish_time = #{actualFinishTime},</if>
|
||||
<if test="responseDeadline != null">response_deadline = #{responseDeadline},</if>
|
||||
<if test="sourceChannel != null">source_channel = #{sourceChannel},</if>
|
||||
<if test="feishuChatId != null">feishu_chat_id = #{feishuChatId},</if>
|
||||
<if test="createBy != null">create_by = #{createBy},</if>
|
||||
<if test="createTime != null">create_time = #{createTime},</if>
|
||||
<if test="updateBy != null">update_by = #{updateBy},</if>
|
||||
<if test="updateTime != null">update_time = #{updateTime},</if>
|
||||
<if test="remark != null">remark = #{remark},</if>
|
||||
</trim>
|
||||
where order_id = #{orderId}
|
||||
</update>
|
||||
|
||||
<delete id="deleteSysWorkOrderByOrderId" parameterType="Long">
|
||||
delete from sys_work_order where order_id = #{orderId}
|
||||
</delete>
|
||||
|
||||
<delete id="deleteSysWorkOrderByOrderIds" parameterType="String">
|
||||
delete from sys_work_order where order_id in
|
||||
<foreach item="orderId" collection="array" open="(" separator="," close=")">
|
||||
#{orderId}
|
||||
</foreach>
|
||||
</delete>
|
||||
</mapper>
|
@ -0,0 +1,101 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<!DOCTYPE mapper
|
||||
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="com.storm.afterSales.mapper.SysWorkOrderRecordMapper">
|
||||
|
||||
<resultMap type="SysWorkOrderRecord" id="SysWorkOrderRecordResult">
|
||||
<result property="recordId" column="record_id" />
|
||||
<result property="orderId" column="order_id" />
|
||||
<result property="processType" column="process_type" />
|
||||
<result property="processContent" column="process_content" />
|
||||
<result property="progressType" column="progress_type" />
|
||||
<result property="progressValue" column="progress_value" />
|
||||
<result property="progressPercent" column="progress_percent" />
|
||||
<result property="processUserId" column="process_user_id" />
|
||||
<result property="processUserName" column="process_user_name" />
|
||||
<result property="processTime" column="process_time" />
|
||||
<result property="attachmentUrl" column="attachment_url" />
|
||||
</resultMap>
|
||||
|
||||
<sql id="selectSysWorkOrderRecordVo">
|
||||
select record_id, order_id, process_type, process_content, progress_type, progress_value, progress_percent, process_user_id, process_user_name, process_time, attachment_url from sys_work_order_record
|
||||
</sql>
|
||||
|
||||
<select id="selectSysWorkOrderRecordList" parameterType="SysWorkOrderRecord" resultMap="SysWorkOrderRecordResult">
|
||||
<include refid="selectSysWorkOrderRecordVo"/>
|
||||
<where>
|
||||
<if test="orderId != null "> and order_id = #{orderId}</if>
|
||||
<if test="processType != null and processType != ''"> and process_type = #{processType}</if>
|
||||
<if test="processContent != null and processContent != ''"> and process_content = #{processContent}</if>
|
||||
<if test="progressType != null and progressType != ''"> and progress_type = #{progressType}</if>
|
||||
<if test="progressValue != null and progressValue != ''"> and progress_value = #{progressValue}</if>
|
||||
<if test="progressPercent != null "> and progress_percent = #{progressPercent}</if>
|
||||
<if test="processUserId != null "> and process_user_id = #{processUserId}</if>
|
||||
<if test="processUserName != null and processUserName != ''"> and process_user_name like concat('%', #{processUserName}, '%')</if>
|
||||
<if test="processTime != null "> and process_time = #{processTime}</if>
|
||||
<if test="attachmentUrl != null and attachmentUrl != ''"> and attachment_url = #{attachmentUrl}</if>
|
||||
</where>
|
||||
</select>
|
||||
|
||||
<select id="selectSysWorkOrderRecordByRecordId" parameterType="Long" resultMap="SysWorkOrderRecordResult">
|
||||
<include refid="selectSysWorkOrderRecordVo"/>
|
||||
where record_id = #{recordId}
|
||||
</select>
|
||||
|
||||
<insert id="insertSysWorkOrderRecord" parameterType="SysWorkOrderRecord" useGeneratedKeys="true" keyProperty="recordId">
|
||||
insert into sys_work_order_record
|
||||
<trim prefix="(" suffix=")" suffixOverrides=",">
|
||||
<if test="orderId != null">order_id,</if>
|
||||
<if test="processType != null">process_type,</if>
|
||||
<if test="processContent != null">process_content,</if>
|
||||
<if test="progressType != null">progress_type,</if>
|
||||
<if test="progressValue != null">progress_value,</if>
|
||||
<if test="progressPercent != null">progress_percent,</if>
|
||||
<if test="processUserId != null">process_user_id,</if>
|
||||
<if test="processUserName != null">process_user_name,</if>
|
||||
<if test="processTime != null">process_time,</if>
|
||||
<if test="attachmentUrl != null">attachment_url,</if>
|
||||
</trim>
|
||||
<trim prefix="values (" suffix=")" suffixOverrides=",">
|
||||
<if test="orderId != null">#{orderId},</if>
|
||||
<if test="processType != null">#{processType},</if>
|
||||
<if test="processContent != null">#{processContent},</if>
|
||||
<if test="progressType != null">#{progressType},</if>
|
||||
<if test="progressValue != null">#{progressValue},</if>
|
||||
<if test="progressPercent != null">#{progressPercent},</if>
|
||||
<if test="processUserId != null">#{processUserId},</if>
|
||||
<if test="processUserName != null">#{processUserName},</if>
|
||||
<if test="processTime != null">#{processTime},</if>
|
||||
<if test="attachmentUrl != null">#{attachmentUrl},</if>
|
||||
</trim>
|
||||
</insert>
|
||||
|
||||
<update id="updateSysWorkOrderRecord" parameterType="SysWorkOrderRecord">
|
||||
update sys_work_order_record
|
||||
<trim prefix="SET" suffixOverrides=",">
|
||||
<if test="orderId != null">order_id = #{orderId},</if>
|
||||
<if test="processType != null">process_type = #{processType},</if>
|
||||
<if test="processContent != null">process_content = #{processContent},</if>
|
||||
<if test="progressType != null">progress_type = #{progressType},</if>
|
||||
<if test="progressValue != null">progress_value = #{progressValue},</if>
|
||||
<if test="progressPercent != null">progress_percent = #{progressPercent},</if>
|
||||
<if test="processUserId != null">process_user_id = #{processUserId},</if>
|
||||
<if test="processUserName != null">process_user_name = #{processUserName},</if>
|
||||
<if test="processTime != null">process_time = #{processTime},</if>
|
||||
<if test="attachmentUrl != null">attachment_url = #{attachmentUrl},</if>
|
||||
</trim>
|
||||
where record_id = #{recordId}
|
||||
</update>
|
||||
|
||||
<delete id="deleteSysWorkOrderRecordByRecordId" parameterType="Long">
|
||||
delete from sys_work_order_record where record_id = #{recordId}
|
||||
</delete>
|
||||
|
||||
<delete id="deleteSysWorkOrderRecordByRecordIds" parameterType="String">
|
||||
delete from sys_work_order_record where record_id in
|
||||
<foreach item="recordId" collection="array" open="(" separator="," close=")">
|
||||
#{recordId}
|
||||
</foreach>
|
||||
</delete>
|
||||
</mapper>
|
@ -0,0 +1,91 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<!DOCTYPE mapper
|
||||
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="com.storm.afterSales.mapper.SysWorkOrderTypeMapper">
|
||||
|
||||
<resultMap type="SysWorkOrderType" id="SysWorkOrderTypeResult">
|
||||
<result property="typeId" column="type_id" />
|
||||
<result property="typeName" column="type_name" />
|
||||
<result property="typeKey" column="type_key" />
|
||||
<result property="STATUS" column="STATUS" />
|
||||
<result property="orderNum" column="order_num" />
|
||||
<result property="createBy" column="create_by" />
|
||||
<result property="createTime" column="create_time" />
|
||||
<result property="updateBy" column="update_by" />
|
||||
<result property="updateTime" column="update_time" />
|
||||
<result property="remark" column="remark" />
|
||||
</resultMap>
|
||||
|
||||
<sql id="selectSysWorkOrderTypeVo">
|
||||
select type_id, type_name, type_key, STATUS, order_num, create_by, create_time, update_by, update_time, remark from sys_work_order_type
|
||||
</sql>
|
||||
|
||||
<select id="selectSysWorkOrderTypeList" parameterType="SysWorkOrderType" resultMap="SysWorkOrderTypeResult">
|
||||
<include refid="selectSysWorkOrderTypeVo"/>
|
||||
<where>
|
||||
<if test="typeName != null and typeName != ''"> and type_name like concat('%', #{typeName}, '%')</if>
|
||||
<if test="typeKey != null and typeKey != ''"> and type_key = #{typeKey}</if>
|
||||
<if test="STATUS != null and STATUS != ''"> and STATUS = #{STATUS}</if>
|
||||
<if test="orderNum != null "> and order_num = #{orderNum}</if>
|
||||
</where>
|
||||
</select>
|
||||
|
||||
<select id="selectSysWorkOrderTypeByTypeId" parameterType="Long" resultMap="SysWorkOrderTypeResult">
|
||||
<include refid="selectSysWorkOrderTypeVo"/>
|
||||
where type_id = #{typeId}
|
||||
</select>
|
||||
|
||||
<insert id="insertSysWorkOrderType" parameterType="SysWorkOrderType" useGeneratedKeys="true" keyProperty="typeId">
|
||||
insert into sys_work_order_type
|
||||
<trim prefix="(" suffix=")" suffixOverrides=",">
|
||||
<if test="typeName != null and typeName != ''">type_name,</if>
|
||||
<if test="typeKey != null and typeKey != ''">type_key,</if>
|
||||
<if test="STATUS != null">STATUS,</if>
|
||||
<if test="orderNum != null">order_num,</if>
|
||||
<if test="createBy != null">create_by,</if>
|
||||
<if test="createTime != null">create_time,</if>
|
||||
<if test="updateBy != null">update_by,</if>
|
||||
<if test="updateTime != null">update_time,</if>
|
||||
<if test="remark != null">remark,</if>
|
||||
</trim>
|
||||
<trim prefix="values (" suffix=")" suffixOverrides=",">
|
||||
<if test="typeName != null and typeName != ''">#{typeName},</if>
|
||||
<if test="typeKey != null and typeKey != ''">#{typeKey},</if>
|
||||
<if test="STATUS != null">#{STATUS},</if>
|
||||
<if test="orderNum != null">#{orderNum},</if>
|
||||
<if test="createBy != null">#{createBy},</if>
|
||||
<if test="createTime != null">#{createTime},</if>
|
||||
<if test="updateBy != null">#{updateBy},</if>
|
||||
<if test="updateTime != null">#{updateTime},</if>
|
||||
<if test="remark != null">#{remark},</if>
|
||||
</trim>
|
||||
</insert>
|
||||
|
||||
<update id="updateSysWorkOrderType" parameterType="SysWorkOrderType">
|
||||
update sys_work_order_type
|
||||
<trim prefix="SET" suffixOverrides=",">
|
||||
<if test="typeName != null and typeName != ''">type_name = #{typeName},</if>
|
||||
<if test="typeKey != null and typeKey != ''">type_key = #{typeKey},</if>
|
||||
<if test="STATUS != null">STATUS = #{STATUS},</if>
|
||||
<if test="orderNum != null">order_num = #{orderNum},</if>
|
||||
<if test="createBy != null">create_by = #{createBy},</if>
|
||||
<if test="createTime != null">create_time = #{createTime},</if>
|
||||
<if test="updateBy != null">update_by = #{updateBy},</if>
|
||||
<if test="updateTime != null">update_time = #{updateTime},</if>
|
||||
<if test="remark != null">remark = #{remark},</if>
|
||||
</trim>
|
||||
where type_id = #{typeId}
|
||||
</update>
|
||||
|
||||
<delete id="deleteSysWorkOrderTypeByTypeId" parameterType="Long">
|
||||
delete from sys_work_order_type where type_id = #{typeId}
|
||||
</delete>
|
||||
|
||||
<delete id="deleteSysWorkOrderTypeByTypeIds" parameterType="String">
|
||||
delete from sys_work_order_type where type_id in
|
||||
<foreach item="typeId" collection="array" open="(" separator="," close=")">
|
||||
#{typeId}
|
||||
</foreach>
|
||||
</delete>
|
||||
</mapper>
|
@ -13,8 +13,8 @@
|
||||
<artifactId>storm-common-oss</artifactId>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>11</maven.compiler.source>
|
||||
<maven.compiler.target>11</maven.compiler.target>
|
||||
<maven.compiler.source>17</maven.compiler.source>
|
||||
<maven.compiler.target>17</maven.compiler.target>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
</properties>
|
||||
|
||||
|
@ -12,8 +12,8 @@
|
||||
<artifactId>storm-device</artifactId>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>11</maven.compiler.source>
|
||||
<maven.compiler.target>11</maven.compiler.target>
|
||||
<maven.compiler.source>17</maven.compiler.source>
|
||||
<maven.compiler.target>17</maven.compiler.target>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
</properties>
|
||||
|
||||
@ -104,12 +104,6 @@
|
||||
<artifactId>huaweicloud-sdk-iotda</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.qpid</groupId>
|
||||
<artifactId>qpid-jms-client</artifactId>
|
||||
|
@ -1,5 +1,6 @@
|
||||
package com.storm.device.domain.po;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableLogic;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.storm.common.core.annotation.Excel;
|
||||
import com.storm.common.core.web.domain.BaseEntity;
|
||||
@ -70,4 +71,12 @@ public class DeviceHeadUsage extends BaseEntity
|
||||
@Excel(name = "最后使用时间")
|
||||
@Schema(description = "最后使用时间")
|
||||
private Date lastUsedTime;
|
||||
|
||||
private String description;
|
||||
private String remark;
|
||||
|
||||
@TableLogic
|
||||
private Integer isDeleted;
|
||||
private String createBy;
|
||||
private String updateBy;
|
||||
}
|
@ -68,4 +68,12 @@ public class DeviceRuntimeStats extends BaseEntity
|
||||
|
||||
@TableField(fill = FieldFill.INSERT_UPDATE)
|
||||
private Date updateTime;
|
||||
|
||||
private String description;
|
||||
private String remark;
|
||||
|
||||
@TableLogic
|
||||
private Integer isDeleted;
|
||||
private String createBy;
|
||||
private String updateBy;
|
||||
}
|
@ -55,6 +55,9 @@ public class DeviceStatusLog {
|
||||
@TableField(exist = false)
|
||||
private Date endTimeDate;
|
||||
|
||||
private String description;
|
||||
private String remark;
|
||||
|
||||
public Date getStartTimeDate() {
|
||||
return massageStartTime != null ? new Date(massageStartTime) : null;
|
||||
}
|
||||
|
@ -41,6 +41,13 @@ public class MassageTask {
|
||||
@TableField(exist = false)
|
||||
private Date endTimeDate;
|
||||
|
||||
private String description;
|
||||
private String remark;
|
||||
|
||||
@TableLogic
|
||||
private Integer isDeleted;
|
||||
private String updateBy;
|
||||
|
||||
public Date getStartTimeDate() {
|
||||
return startTime != null ? new Date(startTime) : null;
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,182 +1,255 @@
|
||||
package com.storm.device.service.impl;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.storm.device.domain.po.DeviceRuntimeStats;
|
||||
import com.storm.device.mapper.DeviceRuntimeStatsMapper;
|
||||
import com.storm.device.service.IDeviceRuntimeStatsService;
|
||||
import com.storm.device.task.vo.IotMsgNotifyDataPro;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.dao.DuplicateKeyException;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.stereotype.Service;
|
||||
import com.storm.device.mapper.DeviceRuntimeStatsMapper;
|
||||
import com.storm.device.service.IDeviceRuntimeStatsService;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import org.springframework.transaction.annotation.Propagation;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import java.time.Duration;
|
||||
import java.util.Arrays;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* 设备运行时长统计Service业务层处理
|
||||
*
|
||||
* @author storm
|
||||
* @date 2025-09-25
|
||||
*/
|
||||
@Service
|
||||
@Slf4j
|
||||
public class DeviceRuntimeStatsServiceImpl extends ServiceImpl<DeviceRuntimeStatsMapper, DeviceRuntimeStats> implements IDeviceRuntimeStatsService
|
||||
{
|
||||
@Service
|
||||
public class DeviceRuntimeStatsServiceImpl extends ServiceImpl<DeviceRuntimeStatsMapper, DeviceRuntimeStats> implements IDeviceRuntimeStatsService {
|
||||
|
||||
@Autowired
|
||||
private DeviceRuntimeStatsMapper deviceRuntimeStatsMapper;
|
||||
|
||||
/**
|
||||
* 查询设备运行时长统计
|
||||
*
|
||||
* @param id 设备运行时长统计主键
|
||||
* @return 设备运行时长统计
|
||||
*/
|
||||
@Override
|
||||
public DeviceRuntimeStats selectDeviceRuntimeStatsById(Long id)
|
||||
{
|
||||
return getById(id);
|
||||
}
|
||||
@Autowired
|
||||
private RedisTemplate<String, String> redisTemplate;
|
||||
private static final String RUNTIME_STATS_PREFIX = "runtime:stats:id:";
|
||||
private static final long RUNTIME_STATS_EXPIRE_HOURS = 24;
|
||||
|
||||
/**
|
||||
* 查询设备运行时长统计列表
|
||||
*
|
||||
* @param deviceRuntimeStats 设备运行时长统计
|
||||
* @return 设备运行时长统计
|
||||
*/
|
||||
@Override
|
||||
public List<DeviceRuntimeStats> selectDeviceRuntimeStatsList(DeviceRuntimeStats deviceRuntimeStats)
|
||||
{
|
||||
return deviceRuntimeStatsMapper.selectDeviceRuntimeStatsList(deviceRuntimeStats);
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增设备运行时长统计
|
||||
*
|
||||
* @param deviceRuntimeStats 设备运行时长统计
|
||||
* @return 结果
|
||||
*/
|
||||
@Override
|
||||
public int insertDeviceRuntimeStats(DeviceRuntimeStats deviceRuntimeStats)
|
||||
{
|
||||
return save(deviceRuntimeStats) ? 1 : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改设备运行时长统计
|
||||
*
|
||||
* @param deviceRuntimeStats 设备运行时长统计
|
||||
* @return 结果
|
||||
*/
|
||||
@Override
|
||||
public int updateDeviceRuntimeStats(DeviceRuntimeStats deviceRuntimeStats)
|
||||
{
|
||||
return updateById(deviceRuntimeStats) ? 1 : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量删除设备运行时长统计
|
||||
*
|
||||
* @param ids 需要删除的设备运行时长统计主键
|
||||
* @return 结果
|
||||
*/
|
||||
@Override
|
||||
public int deleteDeviceRuntimeStatsByIds(Long[] ids)
|
||||
{
|
||||
return removeByIds(Arrays.asList(ids)) ? 1 : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 安全更新运行统计 - 修复重复累加问题
|
||||
* 安全更新运行统计 - 增强版,防止重复累加
|
||||
*/
|
||||
@Transactional(propagation = Propagation.REQUIRES_NEW)
|
||||
public boolean safeUpdateRuntimeStats(String deviceId, IotMsgNotifyDataPro iotMsgNotifyData) {
|
||||
try {
|
||||
Date today = new Date();
|
||||
Date now = new Date();
|
||||
// 生成运行统计唯一标识
|
||||
String statsUniqueId = generateStatsUniqueId(deviceId, iotMsgNotifyData);
|
||||
if (StrUtil.isNotBlank(statsUniqueId)) {
|
||||
String idempotentKey = RUNTIME_STATS_PREFIX + statsUniqueId;
|
||||
Boolean isNewStats = redisTemplate.opsForValue().setIfAbsent(
|
||||
idempotentKey, "1", Duration.ofHours(RUNTIME_STATS_EXPIRE_HOURS));
|
||||
|
||||
// 计算要增加的时长和在线次数
|
||||
Long durationToAdd = calculateMassageDuration(iotMsgNotifyData);
|
||||
Integer onlineCountToAdd = shouldCountOnline(iotMsgNotifyData) ? 1 : 0;
|
||||
|
||||
// 先尝试查询今日是否已有记录
|
||||
DeviceRuntimeStats existingStats = deviceRuntimeStatsMapper.selectTodayStats(deviceId);
|
||||
|
||||
if (existingStats == null) {
|
||||
// 插入新记录
|
||||
DeviceRuntimeStats newStats = new DeviceRuntimeStats();
|
||||
newStats.setDeviceId(deviceId);
|
||||
newStats.setStatDate(today);
|
||||
newStats.setDailyDuration(durationToAdd);
|
||||
newStats.setWeeklyDuration(durationToAdd);
|
||||
newStats.setMonthlyDuration(durationToAdd);
|
||||
newStats.setOnlineCount(onlineCountToAdd);
|
||||
newStats.setCreateTime(now);
|
||||
newStats.setUpdateTime(now);
|
||||
newStats.setCreateBy("系统自动创建");
|
||||
|
||||
deviceRuntimeStatsMapper.insert(newStats);
|
||||
log.debug("创建新的运行统计记录: {}", deviceId);
|
||||
} else {
|
||||
// 更新现有记录 - 确保不重复累加
|
||||
long newDailyDuration = existingStats.getDailyDuration() + durationToAdd;
|
||||
long newWeeklyDuration = existingStats.getWeeklyDuration() + durationToAdd;
|
||||
long newMonthlyDuration = existingStats.getMonthlyDuration() + durationToAdd;
|
||||
int newOnlineCount = existingStats.getOnlineCount() + onlineCountToAdd;
|
||||
|
||||
// 验证数据合理性
|
||||
if (isDurationReasonable(newDailyDuration) &&
|
||||
isDurationReasonable(newWeeklyDuration) &&
|
||||
isDurationReasonable(newMonthlyDuration)) {
|
||||
|
||||
existingStats.setDailyDuration(newDailyDuration);
|
||||
existingStats.setWeeklyDuration(newWeeklyDuration);
|
||||
existingStats.setMonthlyDuration(newMonthlyDuration);
|
||||
existingStats.setOnlineCount(newOnlineCount);
|
||||
existingStats.setUpdateTime(now);
|
||||
|
||||
deviceRuntimeStatsMapper.updateById(existingStats);
|
||||
log.debug("更新运行统计记录: {} -> 增加{}秒", deviceId, durationToAdd);
|
||||
} else {
|
||||
log.warn("运行统计时长异常,跳过更新: {}", deviceId);
|
||||
return false;
|
||||
if (isNewStats != null && !isNewStats) {
|
||||
log.debug("运行统计已更新,跳过处理: {}", statsUniqueId);
|
||||
return true; // 已更新也算成功
|
||||
}
|
||||
}
|
||||
|
||||
// 检查是否重复消息
|
||||
if (isDuplicateEvent(deviceId, iotMsgNotifyData)) {
|
||||
log.debug("检测到重复消息,跳过运行统计更新: {}", deviceId);
|
||||
return true;
|
||||
}
|
||||
|
||||
for (int attempt = 0; attempt < 3; attempt++) {
|
||||
try {
|
||||
boolean result = doUpdateRuntimeStats(deviceId, iotMsgNotifyData, attempt);
|
||||
if (!result && StrUtil.isNotBlank(statsUniqueId)) {
|
||||
// 更新失败时,删除幂等键,允许重试
|
||||
redisTemplate.delete(RUNTIME_STATS_PREFIX + statsUniqueId);
|
||||
}
|
||||
return result;
|
||||
} catch (DuplicateKeyException e) {
|
||||
log.debug("运行统计记录已存在(并发情况),设备: {}", deviceId);
|
||||
// 递归重试一次
|
||||
return safeUpdateRuntimeStatsRetry(deviceId, iotMsgNotifyData);
|
||||
} catch (Exception e) {
|
||||
log.error("更新运行统计失败,设备: {}", deviceId, e);
|
||||
log.warn("运行统计更新重复键异常,尝试次数: {},设备: {}", attempt + 1, deviceId);
|
||||
if (attempt == 2) {
|
||||
log.error("运行统计更新最终失败,设备: {}", deviceId, e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 重试机制
|
||||
*/
|
||||
private boolean safeUpdateRuntimeStatsRetry(String deviceId, IotMsgNotifyDataPro iotMsgNotifyData) {
|
||||
// 等待后重试
|
||||
try {
|
||||
Thread.sleep(100); // 短暂等待
|
||||
return safeUpdateRuntimeStats(deviceId, iotMsgNotifyData);
|
||||
Thread.sleep(100 * (attempt + 1));
|
||||
} catch (InterruptedException ie) {
|
||||
Thread.currentThread().interrupt();
|
||||
return false;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("运行统计重试也失败,设备: {}", deviceId, e);
|
||||
log.error("运行统计更新异常,尝试次数: {},设备: {}", attempt + 1, deviceId, e);
|
||||
if (attempt == 2) {
|
||||
if (StrUtil.isNotBlank(statsUniqueId)) {
|
||||
// 更新失败时,删除幂等键,允许重试
|
||||
redisTemplate.delete(RUNTIME_STATS_PREFIX + statsUniqueId);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成运行统计唯一标识
|
||||
*/
|
||||
private String generateStatsUniqueId(String deviceId, IotMsgNotifyDataPro iotMsgNotifyData) {
|
||||
if (iotMsgNotifyData.getBody() == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
String eventTime = null;
|
||||
|
||||
// 尝试从body中获取事件时间
|
||||
if (iotMsgNotifyData.getBody().getStatusUpdateTime() != null) {
|
||||
eventTime = String.valueOf(iotMsgNotifyData.getBody().getStatusUpdateTime().getTime());
|
||||
}
|
||||
|
||||
// 尝试从services中获取事件时间
|
||||
if (StrUtil.isBlank(eventTime) && iotMsgNotifyData.getBody().getServices() != null) {
|
||||
for (IotMsgNotifyDataPro.IotMsgService service : iotMsgNotifyData.getBody().getServices()) {
|
||||
if (service.getEventTime() != null) {
|
||||
eventTime = String.valueOf(service.getEventTime().getTime());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 如果仍然没有事件时间,使用当前时间
|
||||
if (StrUtil.isBlank(eventTime)) {
|
||||
eventTime = String.valueOf(System.currentTimeMillis());
|
||||
}
|
||||
|
||||
// 添加服务类型信息
|
||||
String serviceTypes = "";
|
||||
if (iotMsgNotifyData.getBody().getServices() != null) {
|
||||
for (IotMsgNotifyDataPro.IotMsgService service : iotMsgNotifyData.getBody().getServices()) {
|
||||
serviceTypes += service.getServiceId() + ",";
|
||||
}
|
||||
}
|
||||
|
||||
return deviceId + ":" + eventTime + ":" + serviceTypes;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查是否为重复事件
|
||||
*/
|
||||
private boolean isDuplicateEvent(String deviceId, IotMsgNotifyDataPro iotMsgNotifyData) {
|
||||
try {
|
||||
// 使用 massage_start_time 作为唯一标识
|
||||
Long startTime = extractMassageStartTime(iotMsgNotifyData);
|
||||
if (startTime == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 使用Redis设置短期过期键,实现幂等性检查
|
||||
String eventKey = "device:runtime:event:" + deviceId + ":" + startTime;
|
||||
Boolean isNewEvent = redisTemplate.opsForValue().setIfAbsent(
|
||||
eventKey, "1", 5, TimeUnit.MINUTES); // 5分钟幂等窗口
|
||||
|
||||
return isNewEvent != null && !isNewEvent;
|
||||
} catch (Exception e) {
|
||||
log.warn("重复事件检查失败: {}", deviceId, e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private Long extractMassageStartTime(IotMsgNotifyDataPro iotMsgNotifyData) {
|
||||
if (iotMsgNotifyData.getBody() == null || iotMsgNotifyData.getBody().getServices() == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
for (IotMsgNotifyDataPro.IotMsgService service : iotMsgNotifyData.getBody().getServices()) {
|
||||
if ("StatusChange".equals(service.getServiceId()) && service.getProperties() != null) {
|
||||
Map<String, Object> props = service.getProperties();
|
||||
return safeParseTimestamp(props.get("massage_start_time"));
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private boolean doUpdateRuntimeStats(String deviceId, IotMsgNotifyDataPro iotMsgNotifyData, int attempt) {
|
||||
Date today = new Date();
|
||||
Long durationToAdd = calculateMassageDuration(iotMsgNotifyData);
|
||||
Integer onlineCountToAdd = shouldCountOnline(iotMsgNotifyData) ? 1 : 0;
|
||||
|
||||
// 方法1: 尝试使用原子更新
|
||||
if (attempt == 0) {
|
||||
try {
|
||||
int result = deviceRuntimeStatsMapper.atomicUpdateStats(
|
||||
deviceId, today, durationToAdd, durationToAdd, durationToAdd, onlineCountToAdd);
|
||||
if (result > 0) {
|
||||
log.debug("原子更新运行统计成功: {}", deviceId);
|
||||
return true;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.debug("原子更新失败,尝试其他方法: {}", deviceId);
|
||||
}
|
||||
}
|
||||
|
||||
// 方法2: 尝试使用 INSERT ... ON DUPLICATE KEY UPDATE
|
||||
if (attempt <= 1) {
|
||||
try {
|
||||
DeviceRuntimeStats stats = createStatsObject(deviceId, today, durationToAdd, onlineCountToAdd);
|
||||
int result = deviceRuntimeStatsMapper.insertOrUpdate(stats);
|
||||
if (result > 0) {
|
||||
log.debug("插入或更新运行统计成功: {}", deviceId);
|
||||
return true;
|
||||
}
|
||||
} catch (DuplicateKeyException e) {
|
||||
// 继续尝试下一种方法
|
||||
if (attempt == 1) {
|
||||
throw e; // 最后一次尝试时抛出异常
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.debug("插入或更新失败: {}", deviceId);
|
||||
}
|
||||
}
|
||||
|
||||
// 方法3: 使用查询+更新(最安全但性能较差)
|
||||
return fallbackUpdate(deviceId, today, durationToAdd, onlineCountToAdd);
|
||||
}
|
||||
|
||||
private DeviceRuntimeStats createStatsObject(String deviceId, Date statDate, Long duration, Integer onlineCount) {
|
||||
DeviceRuntimeStats stats = new DeviceRuntimeStats();
|
||||
stats.setDeviceId(deviceId);
|
||||
stats.setStatDate(statDate);
|
||||
stats.setDailyDuration(duration);
|
||||
stats.setWeeklyDuration(duration);
|
||||
stats.setMonthlyDuration(duration);
|
||||
stats.setOnlineCount(onlineCount);
|
||||
stats.setCreateTime(new Date());
|
||||
stats.setUpdateTime(new Date());
|
||||
stats.setCreateBy("系统自动创建");
|
||||
return stats;
|
||||
}
|
||||
|
||||
private boolean fallbackUpdate(String deviceId, Date statDate, Long durationToAdd, Integer onlineCountToAdd) {
|
||||
try {
|
||||
// 使用行级锁查询
|
||||
DeviceRuntimeStats existingStats = deviceRuntimeStatsMapper.selectByDeviceAndDateForUpdate(deviceId, statDate);
|
||||
|
||||
if (existingStats == null) {
|
||||
// 插入新记录
|
||||
DeviceRuntimeStats newStats = createStatsObject(deviceId, statDate, durationToAdd, onlineCountToAdd);
|
||||
deviceRuntimeStatsMapper.insert(newStats);
|
||||
} else {
|
||||
// 更新现有记录
|
||||
existingStats.setDailyDuration(existingStats.getDailyDuration() + durationToAdd);
|
||||
existingStats.setWeeklyDuration(existingStats.getWeeklyDuration() + durationToAdd);
|
||||
existingStats.setMonthlyDuration(existingStats.getMonthlyDuration() + durationToAdd);
|
||||
existingStats.setOnlineCount(existingStats.getOnlineCount() + onlineCountToAdd);
|
||||
existingStats.setUpdateTime(new Date());
|
||||
deviceRuntimeStatsMapper.updateById(existingStats);
|
||||
}
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
log.error("回退方案更新运行统计失败: {}", deviceId, e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算按摩时长
|
||||
*/
|
||||
private Long calculateMassageDuration(IotMsgNotifyDataPro iotMsgNotifyData) {
|
||||
if (iotMsgNotifyData.getBody() == null || iotMsgNotifyData.getBody().getServices() == null) {
|
||||
return 0L;
|
||||
@ -189,17 +262,7 @@ public class DeviceRuntimeStatsServiceImpl extends ServiceImpl<DeviceRuntimeStat
|
||||
Long endTime = safeParseTimestamp(props.get("massage_end_time"));
|
||||
|
||||
if (startTime != null && endTime != null && startTime < endTime && startTime > 0) {
|
||||
long durationMs = endTime - startTime;
|
||||
long durationSeconds = durationMs / 1000;
|
||||
|
||||
// 验证时长合理性
|
||||
if (isDurationReasonable(durationSeconds)) {
|
||||
return durationSeconds;
|
||||
} else {
|
||||
log.warn("按摩时长异常: {}秒,设备: {}", durationSeconds,
|
||||
iotMsgNotifyData.getHeader().getDeviceId());
|
||||
return 0L;
|
||||
}
|
||||
return (endTime - startTime) / 1000; // 转换为秒
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -207,52 +270,47 @@ public class DeviceRuntimeStatsServiceImpl extends ServiceImpl<DeviceRuntimeStat
|
||||
return 0L;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否应该计数为上线
|
||||
*/
|
||||
private boolean shouldCountOnline(IotMsgNotifyDataPro iotMsgNotifyData) {
|
||||
return iotMsgNotifyData.getBody() != null &&
|
||||
"ONLINE".equalsIgnoreCase(iotMsgNotifyData.getBody().getStatus());
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证时长是否合理(最大24小时)
|
||||
*/
|
||||
private boolean isDurationReasonable(long durationSeconds) {
|
||||
return durationSeconds >= 0 && durationSeconds <= 24 * 60 * 60; // 24小时
|
||||
}
|
||||
|
||||
/**
|
||||
* 安全解析时间戳
|
||||
*/
|
||||
private Long safeParseTimestamp(Object timestampObj) {
|
||||
if (timestampObj == null) return null;
|
||||
|
||||
try {
|
||||
if (timestampObj instanceof Number) {
|
||||
long timestamp = ((Number) timestampObj).longValue();
|
||||
if (timestamp < 10000000000L) { // 秒级时间戳
|
||||
return timestamp * 1000;
|
||||
} else { // 毫秒级时间戳
|
||||
return timestamp;
|
||||
}
|
||||
return ((Number) timestampObj).longValue();
|
||||
} else if (timestampObj instanceof String) {
|
||||
String timestampStr = timestampObj.toString();
|
||||
if (timestampStr.contains(".")) {
|
||||
double timestamp = Double.parseDouble(timestampStr);
|
||||
return (long) (timestamp * 1000);
|
||||
} else {
|
||||
long timestamp = Long.parseLong(timestampStr);
|
||||
if (timestamp < 10000000000L) {
|
||||
return timestamp * 1000;
|
||||
} else {
|
||||
return timestamp;
|
||||
}
|
||||
}
|
||||
return Long.parseLong(timestampObj.toString());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.warn("时间戳解析失败: {}", timestampObj);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DeviceRuntimeStats selectDeviceRuntimeStatsById(Long id) {
|
||||
return getById(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<DeviceRuntimeStats> selectDeviceRuntimeStatsList(DeviceRuntimeStats deviceRuntimeStats) {
|
||||
return deviceRuntimeStatsMapper.selectDeviceRuntimeStatsList(deviceRuntimeStats);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int insertDeviceRuntimeStats(DeviceRuntimeStats deviceRuntimeStats) {
|
||||
return save(deviceRuntimeStats) ? 1 : 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int updateDeviceRuntimeStats(DeviceRuntimeStats deviceRuntimeStats) {
|
||||
return updateById(deviceRuntimeStats) ? 1 : 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int deleteDeviceRuntimeStatsByIds(Long[] ids) {
|
||||
return removeByIds(Arrays.asList(ids)) ? 1 : 0;
|
||||
}
|
||||
}
|
@ -1,16 +1,19 @@
|
||||
package com.storm.device.service.impl;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.*;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import com.storm.device.domain.po.DeviceStatusLog;
|
||||
import com.storm.device.manager.DeviceManagerService;
|
||||
import com.storm.device.mapper.DeviceStatusLogMapper;
|
||||
import com.storm.device.service.IDeviceStatusLogService;
|
||||
import com.storm.device.task.vo.IotMsgNotifyDataPro;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.dao.DuplicateKeyException;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.stereotype.Service;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import org.springframework.transaction.annotation.Propagation;
|
||||
@ -28,8 +31,9 @@ public class DeviceStatusLogServiceImpl extends ServiceImpl<DeviceStatusLogMappe
|
||||
@Autowired
|
||||
private DeviceStatusLogMapper deviceStatusLogMapper;
|
||||
@Autowired
|
||||
private DeviceManagerService deviceManagerService;
|
||||
|
||||
private RedisTemplate<String, String> redisTemplate;
|
||||
private static final String STATUS_LOG_PREFIX = "status:log:id:";
|
||||
private static final long STATUS_LOG_EXPIRE_HOURS = 24;
|
||||
@Override
|
||||
public List<DeviceStatusLog> selectDeviceStatusLogList(DeviceStatusLog deviceStatusLog) {
|
||||
return deviceStatusLogMapper.selectList(Wrappers.<DeviceStatusLog>lambdaQuery()
|
||||
@ -41,93 +45,32 @@ public class DeviceStatusLogServiceImpl extends ServiceImpl<DeviceStatusLogMappe
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理服务属性
|
||||
* 安全插入状态日志 - 增强版,确保事件类型正确设置
|
||||
*/
|
||||
private void processServiceProperties(DeviceStatusLog statusLog, List<IotMsgNotifyDataPro.IotMsgService> services) {
|
||||
for (IotMsgNotifyDataPro.IotMsgService service : services) {
|
||||
if (service.getProperties() == null) continue;
|
||||
|
||||
Map<String, Object> properties = service.getProperties();
|
||||
|
||||
// 处理按摩相关属性
|
||||
if (properties.containsKey("head_type")) {
|
||||
statusLog.setHeadType(properties.get("head_type").toString());
|
||||
}
|
||||
if (properties.containsKey("body_part")) {
|
||||
statusLog.setBodyPart(properties.get("body_part").toString());
|
||||
}
|
||||
if (properties.containsKey("massage_plan")) {
|
||||
statusLog.setMassagePlan(properties.get("massage_plan").toString());
|
||||
}
|
||||
|
||||
// 处理按摩时间
|
||||
if (properties.containsKey("massage_start_time")) {
|
||||
Long startTime = parseTimestampToMillis(properties.get("massage_start_time"));
|
||||
statusLog.setMassageStartTime(startTime);
|
||||
}
|
||||
if (properties.containsKey("massage_end_time")) {
|
||||
Long endTime = parseTimestampToMillis(properties.get("massage_end_time"));
|
||||
statusLog.setMassageEndTime(endTime);
|
||||
|
||||
// 计算持续时间
|
||||
if (statusLog.getMassageStartTime() != null && endTime != null) {
|
||||
statusLog.setMassageDuration(endTime - statusLog.getMassageStartTime());
|
||||
}
|
||||
}
|
||||
|
||||
// 如果是按摩任务,设置事件类型
|
||||
if (properties.containsKey("massage_start_time") ||
|
||||
properties.containsKey("head_type") ||
|
||||
properties.containsKey("body_part")) {
|
||||
statusLog.setEventType("MASSAGE_TASK");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析时间戳为毫秒
|
||||
*/
|
||||
private Long parseTimestampToMillis(Object timestampObj) {
|
||||
if (timestampObj == null) return null;
|
||||
|
||||
try {
|
||||
if (timestampObj instanceof Number) {
|
||||
long timestamp = ((Number) timestampObj).longValue();
|
||||
if (timestamp < 10000000000L) { // 秒级时间戳
|
||||
return timestamp * 1000;
|
||||
} else { // 毫秒级时间戳
|
||||
return timestamp;
|
||||
}
|
||||
} else if (timestampObj instanceof String) {
|
||||
String timestampStr = timestampObj.toString();
|
||||
if (timestampStr.contains(".")) {
|
||||
// 处理浮点数时间戳
|
||||
double timestamp = Double.parseDouble(timestampStr);
|
||||
return (long) (timestamp * 1000);
|
||||
} else {
|
||||
long timestamp = Long.parseLong(timestampStr);
|
||||
if (timestamp < 10000000000L) {
|
||||
return timestamp * 1000;
|
||||
} else {
|
||||
return timestamp;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("时间戳解析失败: {}", timestampObj, e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 安全插入状态日志 - 多层保护机制
|
||||
*/
|
||||
@Transactional(propagation = Propagation.REQUIRES_NEW)
|
||||
@Override
|
||||
@Transactional(propagation = Propagation.REQUIRES_NEW)
|
||||
public boolean safeInsertStatusLog(String deviceId, IotMsgNotifyDataPro iotMsgNotifyData) {
|
||||
// 生成状态日志唯一标识
|
||||
String statusLogUniqueId = generateStatusLogUniqueId(deviceId, iotMsgNotifyData);
|
||||
if (StrUtil.isNotBlank(statusLogUniqueId)) {
|
||||
String idempotentKey = STATUS_LOG_PREFIX + statusLogUniqueId;
|
||||
Boolean isNewLog = redisTemplate.opsForValue().setIfAbsent(
|
||||
idempotentKey, "1", Duration.ofHours(STATUS_LOG_EXPIRE_HOURS));
|
||||
|
||||
if (isNewLog != null && !isNewLog) {
|
||||
log.debug("状态日志已存在,跳过插入: {}", statusLogUniqueId);
|
||||
return true; // 已存在也算成功
|
||||
}
|
||||
}
|
||||
|
||||
for (int attempt = 0; attempt < 3; attempt++) {
|
||||
try {
|
||||
return doInsertStatusLog(deviceId, iotMsgNotifyData, attempt);
|
||||
boolean result = doInsertStatusLog(deviceId, iotMsgNotifyData, attempt);
|
||||
if (!result && StrUtil.isNotBlank(statusLogUniqueId)) {
|
||||
// 插入失败时,删除幂等键,允许重试
|
||||
redisTemplate.delete(STATUS_LOG_PREFIX + statusLogUniqueId);
|
||||
}
|
||||
return result;
|
||||
} catch (DuplicateKeyException e) {
|
||||
log.debug("状态日志重复键异常,尝试次数: {},设备: {}", attempt + 1, deviceId);
|
||||
if (attempt == 2) {
|
||||
@ -144,6 +87,10 @@ public class DeviceStatusLogServiceImpl extends ServiceImpl<DeviceStatusLogMappe
|
||||
} catch (Exception e) {
|
||||
log.error("状态日志插入异常,尝试次数: {},设备: {}", attempt + 1, deviceId, e);
|
||||
if (attempt == 2) {
|
||||
if (StrUtil.isNotBlank(statusLogUniqueId)) {
|
||||
// 插入失败时,删除幂等键,允许重试
|
||||
redisTemplate.delete(STATUS_LOG_PREFIX + statusLogUniqueId);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -151,6 +98,39 @@ public class DeviceStatusLogServiceImpl extends ServiceImpl<DeviceStatusLogMappe
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成状态日志唯一标识
|
||||
*/
|
||||
private String generateStatusLogUniqueId(String deviceId, IotMsgNotifyDataPro iotMsgNotifyData) {
|
||||
if (iotMsgNotifyData.getBody() == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
String eventTime = null;
|
||||
|
||||
// 尝试从body中获取事件时间
|
||||
if (iotMsgNotifyData.getBody().getStatusUpdateTime() != null) {
|
||||
eventTime = String.valueOf(iotMsgNotifyData.getBody().getStatusUpdateTime().getTime());
|
||||
}
|
||||
|
||||
// 尝试从services中获取事件时间
|
||||
if (StrUtil.isBlank(eventTime) && iotMsgNotifyData.getBody().getServices() != null) {
|
||||
for (IotMsgNotifyDataPro.IotMsgService service : iotMsgNotifyData.getBody().getServices()) {
|
||||
if (service.getEventTime() != null) {
|
||||
eventTime = String.valueOf(service.getEventTime().getTime());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 如果仍然没有事件时间,使用当前时间
|
||||
if (StrUtil.isBlank(eventTime)) {
|
||||
eventTime = String.valueOf(System.currentTimeMillis());
|
||||
}
|
||||
|
||||
return deviceId + ":" + eventTime + ":" + iotMsgNotifyData.getBody().getStatus();
|
||||
}
|
||||
|
||||
private boolean doInsertStatusLog(String deviceId, IotMsgNotifyDataPro iotMsgNotifyData, int attempt) {
|
||||
// 先检查是否已存在相同记录
|
||||
if (attempt == 0 && isStatusLogExists(deviceId, iotMsgNotifyData)) {
|
||||
@ -210,128 +190,6 @@ public class DeviceStatusLogServiceImpl extends ServiceImpl<DeviceStatusLogMappe
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 从消息数据创建状态日志对象
|
||||
*/
|
||||
private DeviceStatusLog createStatusLogFromData(String deviceId, IotMsgNotifyDataPro iotMsgNotifyData) {
|
||||
DeviceStatusLog statusLog = new DeviceStatusLog();
|
||||
statusLog.setDeviceId(deviceId);
|
||||
statusLog.setCreateTime(new Date());
|
||||
statusLog.setEventType("STATUS_CHANGE");
|
||||
|
||||
if (iotMsgNotifyData.getBody() == null) {
|
||||
return statusLog;
|
||||
}
|
||||
|
||||
// 设置状态
|
||||
if (iotMsgNotifyData.getBody().getStatus() != null) {
|
||||
String status = iotMsgNotifyData.getBody().getStatus();
|
||||
statusLog.setStatus("ONLINE".equalsIgnoreCase(status) ? "ONLINE" : "OFFLINE");
|
||||
}
|
||||
|
||||
// 设置事件时间
|
||||
if (iotMsgNotifyData.getBody().getStatusUpdateTime() != null) {
|
||||
statusLog.setEventTime(iotMsgNotifyData.getBody().getStatusUpdateTime());
|
||||
} else {
|
||||
statusLog.setEventTime(new Date());
|
||||
}
|
||||
|
||||
// 设置最后在线时间
|
||||
if (iotMsgNotifyData.getBody().getLastOnlineTime() != null) {
|
||||
statusLog.setLastOnlineTime(iotMsgNotifyData.getBody().getLastOnlineTime());
|
||||
}
|
||||
// 设置其他字段
|
||||
statusLog.setCreateBy("系统自动创建");
|
||||
List<IotMsgNotifyDataPro.IotMsgService> services = iotMsgNotifyData.getBody().getServices();
|
||||
if(services!=null&&services.size()>0){
|
||||
for (IotMsgNotifyDataPro.IotMsgService service : services) {
|
||||
if ("StatusChange".equals(service.getServiceId()) && service.getProperties() != null) {
|
||||
Map<String, Object> props = service.getProperties();
|
||||
Long startTime = safeParseTimestamp(props.get("massage_start_time"));
|
||||
Long endTime = safeParseTimestamp(props.get("massage_end_time"));
|
||||
statusLog.setMassageStartTime(startTime);
|
||||
statusLog.setMassageEndTime(endTime);
|
||||
statusLog.setHeadType(safeGetString(props, "head_type"));
|
||||
statusLog.setBodyPart(safeGetString(props, "body_part"));
|
||||
if (startTime != null && endTime != null && startTime < endTime && startTime > 0) {
|
||||
long duration = (endTime - startTime);
|
||||
statusLog.setDuration((int)duration);
|
||||
statusLog.setMassageDuration(duration);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// 处理服务数据
|
||||
processServiceData(statusLog, iotMsgNotifyData);
|
||||
|
||||
return statusLog;
|
||||
}
|
||||
|
||||
private String safeGetString(Map<String, Object> props, String key) {
|
||||
if (props == null || !props.containsKey(key)) return null;
|
||||
Object value = props.get(key);
|
||||
return value != null ? value.toString() : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理服务数据
|
||||
*/
|
||||
private void processServiceData(DeviceStatusLog statusLog, IotMsgNotifyDataPro iotMsgNotifyData) {
|
||||
if (iotMsgNotifyData.getBody() == null || iotMsgNotifyData.getBody().getServices() == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (IotMsgNotifyDataPro.IotMsgService service : iotMsgNotifyData.getBody().getServices()) {
|
||||
if ("StatusChange".equals(service.getServiceId()) && service.getProperties() != null) {
|
||||
// 处理按摩相关数据
|
||||
processMassageData(statusLog, service.getProperties());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理按摩数据 - 修复版本
|
||||
*/
|
||||
private void processMassageData(DeviceStatusLog statusLog, java.util.Map<String, Object> properties) {
|
||||
if (properties.containsKey("head_type") && properties.get("head_type") != null) {
|
||||
statusLog.setHeadType(properties.get("head_type").toString());
|
||||
}
|
||||
if (properties.containsKey("body_part") && properties.get("body_part") != null) {
|
||||
statusLog.setBodyPart(properties.get("body_part").toString());
|
||||
}
|
||||
if (properties.containsKey("massage_plan") && properties.get("massage_plan") != null) {
|
||||
statusLog.setMassagePlan(properties.get("massage_plan").toString());
|
||||
}
|
||||
|
||||
// 处理按摩时间 - 使用安全解析
|
||||
Long startTime = safeParseTimestamp(properties.get("massage_start_time"));
|
||||
Long endTime = safeParseTimestamp(properties.get("massage_end_time"));
|
||||
|
||||
if (startTime != null) {
|
||||
statusLog.setMassageStartTime(startTime);
|
||||
}
|
||||
if (endTime != null) {
|
||||
statusLog.setMassageEndTime(endTime);
|
||||
}
|
||||
|
||||
// 安全计算持续时间
|
||||
Long duration = safeCalculateDuration(startTime, endTime);
|
||||
if (duration != null) {
|
||||
statusLog.setMassageDuration(duration);
|
||||
statusLog.setDuration(duration.intValue());
|
||||
|
||||
// 如果是按摩任务,更改事件类型
|
||||
statusLog.setEventType("MASSAGE_TASK");
|
||||
log.debug("计算按摩持续时间: {} 秒", duration);
|
||||
} else {
|
||||
// 设置默认值
|
||||
statusLog.setMassageDuration(0L);
|
||||
statusLog.setDuration(0);
|
||||
log.warn("无法计算有效的按摩持续时间,startTime: {}, endTime: {}", startTime, endTime);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean insertWithIgnore(DeviceStatusLog statusLog) {
|
||||
try {
|
||||
int result = deviceStatusLogMapper.insertOrIgnore(statusLog);
|
||||
@ -379,11 +237,10 @@ public class DeviceStatusLogServiceImpl extends ServiceImpl<DeviceStatusLogMappe
|
||||
}
|
||||
|
||||
/**
|
||||
* 安全解析时间戳 - 修复版本
|
||||
* 安全解析时间戳
|
||||
*/
|
||||
private Long safeParseTimestamp(Object timestampObj) {
|
||||
if (timestampObj == null) return null;
|
||||
|
||||
try {
|
||||
long timestamp;
|
||||
if (timestampObj instanceof Number) {
|
||||
@ -401,12 +258,6 @@ public class DeviceStatusLogServiceImpl extends ServiceImpl<DeviceStatusLogMappe
|
||||
return null;
|
||||
}
|
||||
|
||||
// 验证时间戳的有效性
|
||||
if (!isValidTimestamp(timestamp)) {
|
||||
log.warn("无效的时间戳: {}", timestamp);
|
||||
return null;
|
||||
}
|
||||
|
||||
// 判断是秒级还是毫秒级
|
||||
if (timestamp < 10000000000L) { // 秒级时间戳(小于 2286-11-21)
|
||||
return timestamp * 1000;
|
||||
@ -427,12 +278,6 @@ public class DeviceStatusLogServiceImpl extends ServiceImpl<DeviceStatusLogMappe
|
||||
return null;
|
||||
}
|
||||
|
||||
// 验证时间戳有效性
|
||||
if (!isValidTimestamp(startTime) || !isValidTimestamp(endTime)) {
|
||||
log.warn("无效的时间戳,startTime: {}, endTime: {}", startTime, endTime);
|
||||
return null;
|
||||
}
|
||||
|
||||
// 确保 startTime 和 endTime 都是毫秒级
|
||||
if (startTime < 10000000000L) {
|
||||
startTime = startTime * 1000;
|
||||
@ -456,27 +301,151 @@ public class DeviceStatusLogServiceImpl extends ServiceImpl<DeviceStatusLogMappe
|
||||
return null;
|
||||
}
|
||||
|
||||
return durationMs / 1000; // 返回秒数
|
||||
return durationMs;
|
||||
}
|
||||
|
||||
// 在 DeviceStatusLogServiceImpl 类中添加以下方法
|
||||
|
||||
/**
|
||||
* 从消息数据创建状态日志对象 - 增强版,补全缺失字段
|
||||
*/
|
||||
private DeviceStatusLog createStatusLogFromData(String deviceId, IotMsgNotifyDataPro iotMsgNotifyData) {
|
||||
DeviceStatusLog statusLog = new DeviceStatusLog();
|
||||
statusLog.setDeviceId(deviceId);
|
||||
statusLog.setCreateTime(new Date());
|
||||
statusLog.setEventType("STATUS_CHANGE"); // 默认类型
|
||||
|
||||
if (iotMsgNotifyData.getBody() == null) {
|
||||
return statusLog;
|
||||
}
|
||||
|
||||
// 设置状态
|
||||
if (iotMsgNotifyData.getBody().getStatus() != null) {
|
||||
String status = iotMsgNotifyData.getBody().getStatus();
|
||||
statusLog.setStatus("ONLINE".equalsIgnoreCase(status) ? "ONLINE" : "OFFLINE");
|
||||
} else {
|
||||
statusLog.setStatus("OFFLINE"); // 默认状态
|
||||
}
|
||||
|
||||
// 设置事件时间
|
||||
if (iotMsgNotifyData.getBody().getStatusUpdateTime() != null) {
|
||||
statusLog.setEventTime(iotMsgNotifyData.getBody().getStatusUpdateTime());
|
||||
} else {
|
||||
statusLog.setEventTime(new Date()); // 默认当前时间
|
||||
}
|
||||
|
||||
// 设置最后在线时间
|
||||
if (iotMsgNotifyData.getBody().getLastOnlineTime() != null) {
|
||||
statusLog.setLastOnlineTime(iotMsgNotifyData.getBody().getLastOnlineTime());
|
||||
}
|
||||
statusLog.setCreateBy("系统自动创建");
|
||||
// 处理服务数据
|
||||
processServiceData(statusLog, iotMsgNotifyData);
|
||||
|
||||
return statusLog;
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证时间戳是否有效
|
||||
* 处理服务数据 - 增强判断逻辑,补全缺失字段
|
||||
*/
|
||||
private boolean isValidTimestamp(long timestamp) {
|
||||
// 检查是否为0或负数
|
||||
if (timestamp <= 0) {
|
||||
return false;
|
||||
private void processServiceData(DeviceStatusLog statusLog, IotMsgNotifyDataPro iotMsgNotifyData) {
|
||||
if (iotMsgNotifyData.getBody() == null || iotMsgNotifyData.getBody().getServices() == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 检查是否在合理范围内(1970年 - 2100年)
|
||||
long minValidTimestamp = 0L; // 1970-01-01
|
||||
long maxValidTimestamp = 4102444800000L; // 2100-01-01 的毫秒时间戳
|
||||
for (IotMsgNotifyDataPro.IotMsgService service : iotMsgNotifyData.getBody().getServices()) {
|
||||
Map<String, Object> props = service.getProperties();
|
||||
if (props == null) continue;
|
||||
|
||||
// 如果是秒级时间戳,转换为毫秒后检查
|
||||
if (timestamp < 10000000000L) {
|
||||
timestamp = timestamp * 1000;
|
||||
// 补全缺失字段
|
||||
completeMissingServiceProperties(props, statusLog.getDeviceId());
|
||||
|
||||
// 最终判断:只要包含按摩开始时间、按摩头类型、身体部位中任意两个字段,就认为是按摩任务
|
||||
boolean hasMassageStart = props.containsKey("massage_start_time");
|
||||
boolean hasHeadType = props.containsKey("head_type") && props.get("head_type") != null;
|
||||
boolean hasBodyPart = props.containsKey("body_part") && props.get("body_part") != null;
|
||||
|
||||
int massageEvidenceCount = (hasMassageStart ? 1 : 0) + (hasHeadType ? 1 : 0) + (hasBodyPart ? 1 : 0);
|
||||
|
||||
if (massageEvidenceCount >= 2) {
|
||||
statusLog.setEventType("MASSAGE_TASK");
|
||||
processMassageData(statusLog, props); // 处理按摩数据
|
||||
} else if ("StatusChange".equals(service.getServiceId())) {
|
||||
// 普通状态变更事件
|
||||
statusLog.setEventType("STATUS_CHANGE");
|
||||
} else {
|
||||
// 其他类型的事件
|
||||
statusLog.setEventType(service.getServiceId());
|
||||
}
|
||||
// 找到一个服务就跳出循环,通常一个消息只包含一个主要事件
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return timestamp >= minValidTimestamp && timestamp <= maxValidTimestamp;
|
||||
/**
|
||||
* 补全服务属性中的缺失字段
|
||||
*/
|
||||
private void completeMissingServiceProperties(Map<String, Object> props, String deviceId) {
|
||||
if (!props.containsKey("head_type")) {
|
||||
props.put("head_type", "default_head");
|
||||
log.debug("设备 {} 的 head_type 缺失,使用默认值", deviceId);
|
||||
}
|
||||
if (!props.containsKey("body_part")) {
|
||||
props.put("body_part", "default_body_part");
|
||||
log.debug("设备 {} 的 body_part 缺失,使用默认值", deviceId);
|
||||
}
|
||||
if (!props.containsKey("massage_plan")) {
|
||||
props.put("massage_plan", "default_plan");
|
||||
log.debug("设备 {} 的 massage_plan 缺失,使用默认值", deviceId);
|
||||
}
|
||||
if (!props.containsKey("massage_start_time")) {
|
||||
long startTime = System.currentTimeMillis() - 600000; // 10分钟前
|
||||
props.put("massage_start_time", startTime);
|
||||
log.debug("设备 {} 的 massage_start_time 缺失,使用默认值", deviceId);
|
||||
}
|
||||
if (!props.containsKey("massage_end_time")) {
|
||||
long endTime = System.currentTimeMillis();
|
||||
props.put("massage_end_time", endTime);
|
||||
log.debug("设备 {} 的 massage_end_time 缺失,使用默认值", deviceId);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理按摩数据 - 修复版本,确保字段正确设置
|
||||
*/
|
||||
private void processMassageData(DeviceStatusLog statusLog, Map<String, Object> properties) {
|
||||
// 使用补全后的属性
|
||||
if (properties.containsKey("head_type") && properties.get("head_type") != null) {
|
||||
statusLog.setHeadType(properties.get("head_type").toString());
|
||||
}
|
||||
if (properties.containsKey("body_part") && properties.get("body_part") != null) {
|
||||
statusLog.setBodyPart(properties.get("body_part").toString());
|
||||
}
|
||||
if (properties.containsKey("massage_plan") && properties.get("massage_plan") != null) {
|
||||
statusLog.setMassagePlan(properties.get("massage_plan").toString());
|
||||
}
|
||||
|
||||
// 处理按摩时间 - 使用安全解析
|
||||
Long startTime = safeParseTimestamp(properties.get("massage_start_time"));
|
||||
Long endTime = safeParseTimestamp(properties.get("massage_end_time"));
|
||||
|
||||
if (startTime != null) {
|
||||
statusLog.setMassageStartTime(startTime);
|
||||
}
|
||||
if (endTime != null) {
|
||||
statusLog.setMassageEndTime(endTime);
|
||||
}
|
||||
|
||||
// 安全计算持续时间
|
||||
Long duration = safeCalculateDuration(startTime, endTime);
|
||||
if (duration != null) {
|
||||
statusLog.setMassageDuration(duration);
|
||||
statusLog.setDuration(duration.intValue());
|
||||
} else {
|
||||
// 设置默认值
|
||||
statusLog.setMassageDuration(0L);
|
||||
statusLog.setDuration(0);
|
||||
log.warn("无法计算有效的按摩持续时间,startTime: {}, endTime: {}", startTime, endTime);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,6 +2,8 @@ package com.storm.device.task;
|
||||
|
||||
import cn.hutool.core.text.CharSequenceUtil;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.crypto.digest.DigestUtil;
|
||||
import cn.hutool.json.JSONObject;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import com.storm.device.config.properties.HuaWeiIotConfigProperties;
|
||||
@ -15,6 +17,7 @@ import org.apache.qpid.jms.transports.TransportSupport;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.ApplicationArguments;
|
||||
import org.springframework.boot.ApplicationRunner;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
|
||||
import org.springframework.stereotype.Component;
|
||||
import javax.jms.*;
|
||||
@ -22,6 +25,7 @@ import java.net.InetAddress;
|
||||
import java.net.URI;
|
||||
import java.net.UnknownHostException;
|
||||
import java.text.MessageFormat;
|
||||
import java.time.Duration;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
@ -33,13 +37,15 @@ import java.util.stream.Collectors;
|
||||
public class LocationAmqpClient implements ApplicationRunner {
|
||||
@Autowired
|
||||
private HuaWeiIotConfigProperties huaWeiIotConfigProperties;
|
||||
|
||||
//业务处理异步线程池,线程池参数可以根据您的业务特点调整,或者您也可以用其他异步方式处理接收到的消息。
|
||||
@Autowired
|
||||
private ThreadPoolTaskExecutor threadPoolTaskExecutor;
|
||||
@Autowired
|
||||
private DeviceDataCoordinator deviceDataCoordinator;
|
||||
|
||||
@Autowired
|
||||
private RedisTemplate<String, String> redisTemplate;
|
||||
private static final String MESSAGE_IDEMPOTENT_PREFIX = "iot:message:id:";
|
||||
private static final long MESSAGE_IDEMPOTENT_EXPIRE_MINUTES = 5;
|
||||
//控制台服务端订阅中消费组状态页客户端ID一栏将显示clientId参数。
|
||||
//建议使用机器UUID、MAC地址、IP等唯一标识等作为clientId。便于您区分识别不同的客户端。
|
||||
private static String clientId;
|
||||
@ -177,12 +183,31 @@ public class LocationAmqpClient implements ApplicationRunner {
|
||||
|
||||
// 修改processMessage方法
|
||||
private void processMessage(Message message) {
|
||||
String contentStr;
|
||||
String messageId = null;
|
||||
try {
|
||||
String contentStr = message.getBody(String.class);
|
||||
contentStr = message.getBody(String.class);
|
||||
String topic = message.getStringProperty("topic");
|
||||
String messageId = message.getStringProperty("messageId");
|
||||
messageId = message.getStringProperty("messageId");
|
||||
log.info("receive message,\n topic = {},\n messageId = {},\n content = {}", topic, messageId, contentStr);
|
||||
|
||||
// 生成幂等键:如果 messageId 为空,则基于消息内容生成 SHA256 哈希
|
||||
if (StrUtil.isBlank(messageId)) {
|
||||
messageId = DigestUtil.sha256Hex(contentStr);
|
||||
log.debug("生成基于内容的 messageId: {}", messageId);
|
||||
}
|
||||
|
||||
// 幂等性检查
|
||||
String idempotentKey = MESSAGE_IDEMPOTENT_PREFIX + messageId;
|
||||
Boolean isNewMessage = redisTemplate.opsForValue().setIfAbsent(
|
||||
idempotentKey, "1", Duration.ofMinutes(MESSAGE_IDEMPOTENT_EXPIRE_MINUTES));
|
||||
|
||||
if (isNewMessage != null && !isNewMessage) {
|
||||
log.info("消息已处理,跳过重复消息,messageId: {}", messageId);
|
||||
return;
|
||||
}
|
||||
|
||||
// 处理消息内容
|
||||
JSONObject jsonMsg = JSONUtil.parseObj(contentStr);
|
||||
JSONObject notifyData = jsonMsg.getJSONObject("notify_data");
|
||||
if (ObjectUtil.isEmpty(notifyData)) {
|
||||
@ -190,12 +215,15 @@ public class LocationAmqpClient implements ApplicationRunner {
|
||||
}
|
||||
|
||||
IotMsgNotifyDataPro iotMsgNotifyData = JSONUtil.toBean(notifyData, IotMsgNotifyDataPro.class);
|
||||
|
||||
// 使用统一处理器处理所有数据
|
||||
deviceDataCoordinator.processDeviceData(iotMsgNotifyData);
|
||||
deviceDataCoordinator.coordinateDeviceData(iotMsgNotifyData);
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("处理消息时发生异常", e);
|
||||
log.error("处理消息时发生异常, messageId: {}", messageId, e);
|
||||
// 处理失败时,删除幂等键,允许重试
|
||||
if (StrUtil.isNotBlank(messageId)) {
|
||||
redisTemplate.delete(MESSAGE_IDEMPOTENT_PREFIX + messageId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2,6 +2,8 @@ package com.storm.device.task;
|
||||
|
||||
import cn.hutool.core.text.CharSequenceUtil;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.crypto.digest.DigestUtil;
|
||||
import cn.hutool.json.JSONObject;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import com.storm.device.config.properties.HuaWeiIotConfigProperties;
|
||||
@ -15,6 +17,7 @@ import org.apache.qpid.jms.transports.TransportSupport;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.ApplicationArguments;
|
||||
import org.springframework.boot.ApplicationRunner;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
|
||||
import org.springframework.stereotype.Component;
|
||||
import javax.jms.*;
|
||||
@ -22,6 +25,7 @@ import java.net.InetAddress;
|
||||
import java.net.URI;
|
||||
import java.net.UnknownHostException;
|
||||
import java.text.MessageFormat;
|
||||
import java.time.Duration;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
@ -34,10 +38,14 @@ public class MassageAmqpClient implements ApplicationRunner {
|
||||
private DeviceDataCoordinator deviceDataProcessor;
|
||||
@Autowired
|
||||
private HuaWeiIotConfigProperties huaWeiIotConfigProperties;
|
||||
|
||||
@Autowired
|
||||
private DeviceDataCoordinator deviceDataCoordinator;
|
||||
@Autowired
|
||||
private ThreadPoolTaskExecutor threadPoolTaskExecutor;
|
||||
|
||||
@Autowired
|
||||
private RedisTemplate<String, String> redisTemplate;
|
||||
private static final String MESSAGE_IDEMPOTENT_PREFIX = "iot:message:id:";
|
||||
private static final long MESSAGE_IDEMPOTENT_EXPIRE_MINUTES = 5;
|
||||
//控制台服务端订阅中消费组状态页客户端ID一栏将显示clientId参数。
|
||||
//建议使用机器UUID、MAC地址、IP等唯一标识等作为clientId。便于您区分识别不同的客户端。
|
||||
private static String clientId;
|
||||
@ -173,12 +181,31 @@ public class MassageAmqpClient implements ApplicationRunner {
|
||||
|
||||
// 修改processMessage方法
|
||||
private void processMessage(Message message) {
|
||||
String contentStr;
|
||||
String messageId = null;
|
||||
try {
|
||||
String contentStr = message.getBody(String.class);
|
||||
contentStr = message.getBody(String.class);
|
||||
String topic = message.getStringProperty("topic");
|
||||
String messageId = message.getStringProperty("messageId");
|
||||
messageId = message.getStringProperty("messageId");
|
||||
log.info("receive message,\n topic = {},\n messageId = {},\n content = {}", topic, messageId, contentStr);
|
||||
|
||||
// 生成幂等键:如果 messageId 为空,则基于消息内容生成 SHA256 哈希
|
||||
if (StrUtil.isBlank(messageId)) {
|
||||
messageId = DigestUtil.sha256Hex(contentStr);
|
||||
log.debug("生成基于内容的 messageId: {}", messageId);
|
||||
}
|
||||
|
||||
// 幂等性检查
|
||||
String idempotentKey = MESSAGE_IDEMPOTENT_PREFIX + messageId;
|
||||
Boolean isNewMessage = redisTemplate.opsForValue().setIfAbsent(
|
||||
idempotentKey, "1", Duration.ofMinutes(MESSAGE_IDEMPOTENT_EXPIRE_MINUTES));
|
||||
|
||||
if (isNewMessage != null && !isNewMessage) {
|
||||
log.info("消息已处理,跳过重复消息,messageId: {}", messageId);
|
||||
return;
|
||||
}
|
||||
|
||||
// 处理消息内容
|
||||
JSONObject jsonMsg = JSONUtil.parseObj(contentStr);
|
||||
JSONObject notifyData = jsonMsg.getJSONObject("notify_data");
|
||||
if (ObjectUtil.isEmpty(notifyData)) {
|
||||
@ -186,12 +213,15 @@ public class MassageAmqpClient implements ApplicationRunner {
|
||||
}
|
||||
|
||||
IotMsgNotifyDataPro iotMsgNotifyData = JSONUtil.toBean(notifyData, IotMsgNotifyDataPro.class);
|
||||
|
||||
// 使用统一处理器处理所有数据
|
||||
deviceDataProcessor.processDeviceData(iotMsgNotifyData);
|
||||
deviceDataCoordinator.processDeviceData(iotMsgNotifyData);
|
||||
deviceDataCoordinator.coordinateDeviceData(iotMsgNotifyData);
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("处理消息时发生异常", e);
|
||||
log.error("处理消息时发生异常, messageId: {}", messageId, e);
|
||||
// 处理失败时,删除幂等键,允许重试
|
||||
if (StrUtil.isNotBlank(messageId)) {
|
||||
redisTemplate.delete(MESSAGE_IDEMPOTENT_PREFIX + messageId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4,6 +4,7 @@ import cn.hutool.core.date.DateUtil;
|
||||
import cn.hutool.core.text.CharSequenceUtil;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.crypto.digest.DigestUtil;
|
||||
import cn.hutool.json.JSONArray;
|
||||
import cn.hutool.json.JSONObject;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
@ -18,14 +19,17 @@ import org.apache.qpid.jms.transports.TransportSupport;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.ApplicationArguments;
|
||||
import org.springframework.boot.ApplicationRunner;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.jms.*;
|
||||
import java.net.InetAddress;
|
||||
import java.net.URI;
|
||||
import java.net.UnknownHostException;
|
||||
import java.text.MessageFormat;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.time.Duration;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@ -36,13 +40,14 @@ public class StatusAmqpClient implements ApplicationRunner {
|
||||
private DeviceDataCoordinator deviceDataCoordinator;
|
||||
@Autowired
|
||||
private HuaWeiIotConfigProperties huaWeiIotConfigProperties;
|
||||
@Autowired
|
||||
private RedisTemplate<String, String> redisTemplate;
|
||||
private static final String MESSAGE_IDEMPOTENT_PREFIX = "iot:message:id:";
|
||||
private static final long MESSAGE_IDEMPOTENT_EXPIRE_MINUTES = 5;
|
||||
|
||||
//业务处理异步线程池,线程池参数可以根据您的业务特点调整,或者您也可以用其他异步方式处理接收到的消息。
|
||||
@Autowired
|
||||
private ThreadPoolTaskExecutor threadPoolTaskExecutor;
|
||||
|
||||
//控制台服务端订阅中消费组状态页客户端ID一栏将显示clientId参数。
|
||||
//建议使用机器UUID、MAC地址、IP等唯一标识等作为clientId。便于您区分识别不同的客户端。
|
||||
private static String clientId;
|
||||
|
||||
static {
|
||||
@ -58,42 +63,27 @@ public class StatusAmqpClient implements ApplicationRunner {
|
||||
}
|
||||
|
||||
public void start() throws Exception {
|
||||
//参数说明,请参见AMQP客户端接入说明文档。
|
||||
for (int i = 0; i < huaWeiIotConfigProperties.getConnectionCount(); i++) {
|
||||
//创建amqp连接
|
||||
Connection connection = getConnection();
|
||||
|
||||
//加入监听者
|
||||
((JmsConnection) connection).addConnectionListener(myJmsConnectionListener);
|
||||
// 创建会话。
|
||||
// Session.CLIENT_ACKNOWLEDGE: 收到消息后,需要手动调用message.acknowledge()。
|
||||
// Session.AUTO_ACKNOWLEDGE: SDK自动ACK(推荐)。
|
||||
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
|
||||
connection.start();
|
||||
|
||||
// 创建Receiver连接。
|
||||
MessageConsumer consumer = newConsumer(session, connection, "queue-status");
|
||||
consumer.setMessageListener(messageListener);
|
||||
}
|
||||
|
||||
log.info("amqp is started successfully, and will exit after server shutdown ");
|
||||
log.info("amqp is started successfully, and will exit after server shutdown");
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建amqp连接
|
||||
*
|
||||
* @return amqp连接
|
||||
*/
|
||||
private Connection getConnection() throws Exception {
|
||||
String connectionUrl = generateConnectUrl();
|
||||
JmsConnectionFactory cf = new JmsConnectionFactory(connectionUrl);
|
||||
// 信任服务端
|
||||
TransportOptions to = new TransportOptions();
|
||||
to.setTrustAll(true);
|
||||
cf.setSslContext(TransportSupport.createJdkSslContext(to));
|
||||
String userName = "accessKey=" + huaWeiIotConfigProperties.getAccessKey();
|
||||
cf.setExtension(JmsConnectionExtensions.USERNAME_OVERRIDE.toString(), (connection, uri) -> {
|
||||
// IoTDA的userName组成格式如下:“accessKey=${accessKey}|timestamp=${timestamp}”
|
||||
String newUserName = userName;
|
||||
if (connection instanceof JmsConnection) {
|
||||
newUserName = ((JmsConnection) connection).getUsername();
|
||||
@ -101,15 +91,9 @@ public class StatusAmqpClient implements ApplicationRunner {
|
||||
return newUserName + "|timestamp=" + System.currentTimeMillis();
|
||||
});
|
||||
|
||||
// 创建连接。
|
||||
return cf.createConnection(userName, huaWeiIotConfigProperties.getAccessCode());
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成amqp连接地址
|
||||
*
|
||||
* @return amqp连接地址
|
||||
*/
|
||||
public String generateConnectUrl() {
|
||||
String uri = MessageFormat.format("{0}://{1}:{2}",
|
||||
(huaWeiIotConfigProperties.isUseSsl() ? "amqps" : "amqp"),
|
||||
@ -151,14 +135,6 @@ public class StatusAmqpClient implements ApplicationRunner {
|
||||
return stringBuilder.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建消费者
|
||||
*
|
||||
* @param session session
|
||||
* @param connection amqp连接
|
||||
* @param queueName 队列名称
|
||||
* @return 消费者
|
||||
*/
|
||||
public MessageConsumer newConsumer(Session session, Connection connection, String queueName) throws Exception {
|
||||
if (connection == null || !(connection instanceof JmsConnection) || ((JmsConnection) connection).isClosed()) {
|
||||
throw new Exception("create consumer failed,the connection is disconnected.");
|
||||
@ -169,34 +145,51 @@ public class StatusAmqpClient implements ApplicationRunner {
|
||||
|
||||
private final MessageListener messageListener = message -> {
|
||||
try {
|
||||
//异步处理收到的消息,确保onMessage函数里没有耗时逻辑
|
||||
threadPoolTaskExecutor.submit(() -> processMessage(message));
|
||||
} catch (Exception e) {
|
||||
log.error("submit task occurs exception ", e);
|
||||
}
|
||||
};
|
||||
|
||||
// 修改processMessage方法
|
||||
private void processMessage(Message message) {
|
||||
String contentStr;
|
||||
String messageId = null;
|
||||
try {
|
||||
String contentStr = message.getBody(String.class);
|
||||
contentStr = message.getBody(String.class);
|
||||
String topic = message.getStringProperty("topic");
|
||||
String messageId = message.getStringProperty("messageId");
|
||||
messageId = message.getStringProperty("messageId");
|
||||
log.info("receive message,\n topic = {},\n messageId = {},\n content = {}", topic, messageId, contentStr);
|
||||
|
||||
if (StrUtil.isBlank(messageId)) {
|
||||
messageId = DigestUtil.sha256Hex(contentStr);
|
||||
log.debug("生成基于内容的 messageId: {}", messageId);
|
||||
}
|
||||
|
||||
String idempotentKey = MESSAGE_IDEMPOTENT_PREFIX + messageId;
|
||||
Boolean isNewMessage = redisTemplate.opsForValue().setIfAbsent(
|
||||
idempotentKey, "1", Duration.ofMinutes(MESSAGE_IDEMPOTENT_EXPIRE_MINUTES));
|
||||
|
||||
if (isNewMessage != null && !isNewMessage) {
|
||||
log.info("消息已处理,跳过重复消息,messageId: {}", messageId);
|
||||
return;
|
||||
}
|
||||
|
||||
JSONObject jsonMsg = JSONUtil.parseObj(contentStr);
|
||||
JSONObject notifyData = jsonMsg.getJSONObject("notify_data");
|
||||
if (ObjectUtil.isEmpty(notifyData)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 手动处理日期字段,避免Hutool自动转换失败
|
||||
// 使用自定义解析方法而不是 Hutool 的自动转换
|
||||
IotMsgNotifyDataPro iotMsgNotifyData = parseIotMsgNotifyData(notifyData);
|
||||
// 使用统一处理器处理所有数据
|
||||
deviceDataCoordinator.processDeviceData(iotMsgNotifyData);
|
||||
deviceDataCoordinator.coordinateDeviceData(iotMsgNotifyData);
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("处理消息时发生异常", e);
|
||||
log.error("处理消息时发生异常, messageId: {}", messageId, e);
|
||||
if (StrUtil.isNotBlank(messageId)) {
|
||||
redisTemplate.delete(MESSAGE_IDEMPOTENT_PREFIX + messageId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -297,7 +290,6 @@ public class StatusAmqpClient implements ApplicationRunner {
|
||||
return iotMsgNotifyData;
|
||||
}
|
||||
|
||||
// 在 StatusAmqpClient 中修复日期解析方法
|
||||
/**
|
||||
* 解析ISO日期时间格式(增强版)
|
||||
*/
|
||||
@ -307,15 +299,20 @@ public class StatusAmqpClient implements ApplicationRunner {
|
||||
}
|
||||
|
||||
try {
|
||||
// 处理格式:20251005T073035Z (16位,没有分隔符)
|
||||
if (dateTimeStr.length() == 16 && dateTimeStr.endsWith("Z") && dateTimeStr.contains("T")) {
|
||||
String pattern = "yyyyMMdd'T'HHmmss'Z'";
|
||||
SimpleDateFormat sdf = new SimpleDateFormat(pattern);
|
||||
sdf.setTimeZone(TimeZone.getTimeZone("UTC"));
|
||||
return sdf.parse(dateTimeStr);
|
||||
}
|
||||
|
||||
// 处理格式:20250927T070832Z (16位,没有分隔符)
|
||||
if (dateTimeStr.length() == 16 && dateTimeStr.endsWith("Z") && dateTimeStr.contains("T")) {
|
||||
String pattern = "yyyyMMdd'T'HHmmss'Z'";
|
||||
SimpleDateFormat sdf = new SimpleDateFormat(pattern);
|
||||
sdf.setTimeZone(TimeZone.getTimeZone("UTC"));
|
||||
Date utcDate = sdf.parse(dateTimeStr);
|
||||
|
||||
// 转换为本地时间(如果需要)
|
||||
return utcDate;
|
||||
return sdf.parse(dateTimeStr);
|
||||
}
|
||||
|
||||
// 处理格式:2025-09-27T07:08:31.394Z (标准ISO格式)
|
||||
@ -366,33 +363,21 @@ public class StatusAmqpClient implements ApplicationRunner {
|
||||
}
|
||||
|
||||
private final JmsConnectionListener myJmsConnectionListener = new JmsConnectionListener() {
|
||||
/**
|
||||
* 连接成功建立。
|
||||
*/
|
||||
@Override
|
||||
public void onConnectionEstablished(URI remoteURI) {
|
||||
log.info("onConnectionEstablished, remoteUri:{}", remoteURI);
|
||||
}
|
||||
|
||||
/**
|
||||
* 尝试过最大重试次数之后,最终连接失败。
|
||||
*/
|
||||
@Override
|
||||
public void onConnectionFailure(Throwable error) {
|
||||
log.error("onConnectionFailure, {}", error.getMessage());
|
||||
}
|
||||
|
||||
/**
|
||||
* 连接中断。
|
||||
*/
|
||||
@Override
|
||||
public void onConnectionInterrupted(URI remoteURI) {
|
||||
log.info("onConnectionInterrupted, remoteUri:{}", remoteURI);
|
||||
}
|
||||
|
||||
/**
|
||||
* 连接中断后又自动重连上。
|
||||
*/
|
||||
@Override
|
||||
public void onConnectionRestored(URI remoteURI) {
|
||||
log.info("onConnectionRestored, remoteUri:{}", remoteURI);
|
||||
|
@ -1,24 +0,0 @@
|
||||
[
|
||||
JmsSession
|
||||
[
|
||||
ID
|
||||
:
|
||||
51c3b405-6453-46e2-b41a-8aab14796183
|
||||
:
|
||||
1
|
||||
:
|
||||
1
|
||||
]
|
||||
delivery
|
||||
dispatcher
|
||||
] INFO com.iot.amqp.examples.AbstractAmqpExample - receive a message,content={"resource": "device.property", "event": "report","event_time": "20250830T062115Z", "event_time_ms": "2025-08-30T06:21:15.731Z", "request_id":"64840a97-683c-495d-af14-19f6f9e18b20", "notify_data": {"header": {"app_id":"9782371cee894c0abbecb30efe4e7cd0", "device_id": "673602f0d14760402fbd033b_jsfb-DA", "node_id": "jsfb-DA","product_id": "673602f0d14760402fbd033b", "gateway_id": "673602f0d14760402fbd033b_jsfb-DA"}, "body": {"services":[{"service_id": "VortXDB", "properties": {"online_time":"20250830T062115Z", "latitude": "112.541115", "software_version": "ioT","vtxdb_version": "1.1.9-beta-ansun", "status": "ONLINE", "longitude":"28.280514"}, "event_time": "20250830T062115Z"}]
|
||||
}
|
||||
}
|
||||
}
|
||||
{"resource": "device.property", "event": "report", "event_time": "20250830T062115Z", "event_time_ms": "2025-08-30T06:21:15.731Z", "request_id": "64840a97-683c-495d-af14-19f6f9e18b20",
|
||||
"notify_data": {"header":
|
||||
{"app_id": "9782371cee894c0abbecb30efe4e7cd0", "device_id": "673602f0d14760402fbd033b_jsfb-DA", "node_id": "jsfb-DA", "product_id": "673602f0d14760402fbd033b", "gateway_id": "673602f0d14760402fbd033b_jsfb-DA"},
|
||||
"body": {"services"}
|
||||
:[{"service_id": "VortXDB",
|
||||
"properties": {"online_time":"20250830T062115Z", "latitude": "112.541115", "software_version": "ioT","vtxdb_version": "1.1.9-beta-ansun", "status": "ONLINE", "longitude":"28.280514"}, "event_time": "20250830T062115Z"
|
||||
}
|
@ -1,659 +0,0 @@
|
||||
{
|
||||
"devices": [
|
||||
{
|
||||
"appId": "9782371cee894c0abbecb30efe4e7cd0",
|
||||
"appName": "DefaultApp_6736wwjm",
|
||||
"deviceId": "673602f0d14760402fbd033b_jsfb-DA",
|
||||
"nodeId": "jsfb-DA",
|
||||
"gatewayId": "673602f0d14760402fbd033b_jsfb-DA",
|
||||
"deviceName": "jsfb-DA",
|
||||
"nodeType": "GATEWAY",
|
||||
"productId": "673602f0d14760402fbd033b",
|
||||
"productName": "RS-LL-X1",
|
||||
"status": "INACTIVE",
|
||||
"tags": []
|
||||
},
|
||||
{
|
||||
"appId": "9782371cee894c0abbecb30efe4e7cd0",
|
||||
"appName": "DefaultApp_6736wwjm",
|
||||
"deviceId": "673602f0d14760402fbd033b_jsfb-023486",
|
||||
"nodeId": "jsfb-023486",
|
||||
"gatewayId": "673602f0d14760402fbd033b_jsfb-023486",
|
||||
"deviceName": "jsfb-023486",
|
||||
"nodeType": "GATEWAY",
|
||||
"productId": "673602f0d14760402fbd033b",
|
||||
"productName": "RS-LL-X1",
|
||||
"status": "INACTIVE",
|
||||
"tags": []
|
||||
},
|
||||
{
|
||||
"appId": "9782371cee894c0abbecb30efe4e7cd0",
|
||||
"appName": "DefaultApp_6736wwjm",
|
||||
"deviceId": "673602f0d14760402fbd033b_jsfb-6FF12E",
|
||||
"nodeId": "jsfb-6FF12E",
|
||||
"gatewayId": "673602f0d14760402fbd033b_jsfb-6FF12E",
|
||||
"deviceName": "jsfb-6FF12E",
|
||||
"nodeType": "GATEWAY",
|
||||
"productId": "673602f0d14760402fbd033b",
|
||||
"productName": "RS-LL-X1",
|
||||
"status": "INACTIVE",
|
||||
"tags": []
|
||||
},
|
||||
{
|
||||
"appId": "9782371cee894c0abbecb30efe4e7cd0",
|
||||
"appName": "DefaultApp_6736wwjm",
|
||||
"deviceId": "673602f0d14760402fbd033b_jsfb-62B336",
|
||||
"nodeId": "jsfb-62B336",
|
||||
"gatewayId": "673602f0d14760402fbd033b_jsfb-62B336",
|
||||
"deviceName": "jsfb-62B336",
|
||||
"nodeType": "GATEWAY",
|
||||
"productId": "673602f0d14760402fbd033b",
|
||||
"productName": "RS-LL-X1",
|
||||
"status": "INACTIVE",
|
||||
"tags": []
|
||||
},
|
||||
{
|
||||
"appId": "9782371cee894c0abbecb30efe4e7cd0",
|
||||
"appName": "DefaultApp_6736wwjm",
|
||||
"deviceId": "673602f0d14760402fbd033b_jsfb-F706AD",
|
||||
"nodeId": "jsfb-F706AD",
|
||||
"gatewayId": "673602f0d14760402fbd033b_jsfb-F706AD",
|
||||
"deviceName": "jsfb-F706AD",
|
||||
"nodeType": "GATEWAY",
|
||||
"productId": "673602f0d14760402fbd033b",
|
||||
"productName": "RS-LL-X1",
|
||||
"status": "INACTIVE",
|
||||
"tags": []
|
||||
},
|
||||
{
|
||||
"appId": "9782371cee894c0abbecb30efe4e7cd0",
|
||||
"appName": "DefaultApp_6736wwjm",
|
||||
"deviceId": "673602f0d14760402fbd033b_jsfb-50441C",
|
||||
"nodeId": "jsfb-50441C",
|
||||
"gatewayId": "673602f0d14760402fbd033b_jsfb-50441C",
|
||||
"deviceName": "jsfb-50441C",
|
||||
"nodeType": "GATEWAY",
|
||||
"productId": "673602f0d14760402fbd033b",
|
||||
"productName": "RS-LL-X1",
|
||||
"status": "INACTIVE",
|
||||
"tags": []
|
||||
},
|
||||
{
|
||||
"appId": "9782371cee894c0abbecb30efe4e7cd0",
|
||||
"appName": "DefaultApp_6736wwjm",
|
||||
"deviceId": "673602f0d14760402fbd033b_jsfb-B25A00",
|
||||
"nodeId": "jsfb-B25A00",
|
||||
"gatewayId": "673602f0d14760402fbd033b_jsfb-B25A00",
|
||||
"deviceName": "jsfb-B25A00",
|
||||
"nodeType": "GATEWAY",
|
||||
"productId": "673602f0d14760402fbd033b",
|
||||
"productName": "RS-LL-X1",
|
||||
"status": "INACTIVE",
|
||||
"tags": []
|
||||
},
|
||||
{
|
||||
"appId": "9782371cee894c0abbecb30efe4e7cd0",
|
||||
"appName": "DefaultApp_6736wwjm",
|
||||
"deviceId": "673602f0d14760402fbd033b_jsfb-DCF894",
|
||||
"nodeId": "jsfb-DCF894",
|
||||
"gatewayId": "673602f0d14760402fbd033b_jsfb-DCF894",
|
||||
"deviceName": "jsfb-DCF894",
|
||||
"nodeType": "GATEWAY",
|
||||
"productId": "673602f0d14760402fbd033b",
|
||||
"productName": "RS-LL-X1",
|
||||
"status": "INACTIVE",
|
||||
"tags": []
|
||||
},
|
||||
{
|
||||
"appId": "9782371cee894c0abbecb30efe4e7cd0",
|
||||
"appName": "DefaultApp_6736wwjm",
|
||||
"deviceId": "673602f0d14760402fbd033b_jsfb-6F0F92",
|
||||
"nodeId": "jsfb-6F0F92",
|
||||
"gatewayId": "673602f0d14760402fbd033b_jsfb-6F0F92",
|
||||
"deviceName": "jsfb-6F0F92",
|
||||
"nodeType": "GATEWAY",
|
||||
"productId": "673602f0d14760402fbd033b",
|
||||
"productName": "RS-LL-X1",
|
||||
"status": "INACTIVE",
|
||||
"tags": []
|
||||
},
|
||||
{
|
||||
"appId": "9782371cee894c0abbecb30efe4e7cd0",
|
||||
"appName": "DefaultApp_6736wwjm",
|
||||
"deviceId": "673602f0d14760402fbd033b_jsfb-5A1B1E",
|
||||
"nodeId": "jsfb-5A1B1E",
|
||||
"gatewayId": "673602f0d14760402fbd033b_jsfb-5A1B1E",
|
||||
"deviceName": "jsfb-5A1B1E",
|
||||
"nodeType": "GATEWAY",
|
||||
"productId": "673602f0d14760402fbd033b",
|
||||
"productName": "RS-LL-X1",
|
||||
"status": "INACTIVE",
|
||||
"tags": []
|
||||
},
|
||||
{
|
||||
"appId": "9782371cee894c0abbecb30efe4e7cd0",
|
||||
"appName": "DefaultApp_6736wwjm",
|
||||
"deviceId": "673602f0d14760402fbd033b_jsfb-1DB8E2",
|
||||
"nodeId": "jsfb-1DB8E2",
|
||||
"gatewayId": "673602f0d14760402fbd033b_jsfb-1DB8E2",
|
||||
"deviceName": "jsfb-1DB8E2",
|
||||
"nodeType": "GATEWAY",
|
||||
"productId": "673602f0d14760402fbd033b",
|
||||
"productName": "RS-LL-X1",
|
||||
"status": "INACTIVE",
|
||||
"tags": []
|
||||
},
|
||||
{
|
||||
"appId": "9782371cee894c0abbecb30efe4e7cd0",
|
||||
"appName": "DefaultApp_6736wwjm",
|
||||
"deviceId": "673602f0d14760402fbd033b_jsfb-FD7C6B",
|
||||
"nodeId": "jsfb-FD7C6B",
|
||||
"gatewayId": "673602f0d14760402fbd033b_jsfb-FD7C6B",
|
||||
"deviceName": "jsfb-FD7C6B",
|
||||
"nodeType": "GATEWAY",
|
||||
"productId": "673602f0d14760402fbd033b",
|
||||
"productName": "RS-LL-X1",
|
||||
"status": "INACTIVE",
|
||||
"tags": []
|
||||
},
|
||||
{
|
||||
"appId": "9782371cee894c0abbecb30efe4e7cd0",
|
||||
"appName": "DefaultApp_6736wwjm",
|
||||
"deviceId": "673602f0d14760402fbd033b_jsfb-3554A0",
|
||||
"nodeId": "jsfb-3554A0",
|
||||
"gatewayId": "673602f0d14760402fbd033b_jsfb-3554A0",
|
||||
"deviceName": "jsfb-3554A0",
|
||||
"nodeType": "GATEWAY",
|
||||
"productId": "673602f0d14760402fbd033b",
|
||||
"productName": "RS-LL-X1",
|
||||
"status": "INACTIVE",
|
||||
"tags": []
|
||||
},
|
||||
{
|
||||
"appId": "9782371cee894c0abbecb30efe4e7cd0",
|
||||
"appName": "DefaultApp_6736wwjm",
|
||||
"deviceId": "673602f0d14760402fbd033b_jsfb-BD51A6",
|
||||
"nodeId": "jsfb-BD51A6",
|
||||
"gatewayId": "673602f0d14760402fbd033b_jsfb-BD51A6",
|
||||
"deviceName": "jsfb-BD51A6",
|
||||
"nodeType": "GATEWAY",
|
||||
"productId": "673602f0d14760402fbd033b",
|
||||
"productName": "RS-LL-X1",
|
||||
"status": "INACTIVE",
|
||||
"tags": []
|
||||
},
|
||||
{
|
||||
"appId": "9782371cee894c0abbecb30efe4e7cd0",
|
||||
"appName": "DefaultApp_6736wwjm",
|
||||
"deviceId": "673602f0d14760402fbd033b_jsfb-98BF09",
|
||||
"nodeId": "jsfb-98BF09",
|
||||
"gatewayId": "673602f0d14760402fbd033b_jsfb-98BF09",
|
||||
"deviceName": "jsfb-98BF09",
|
||||
"nodeType": "GATEWAY",
|
||||
"productId": "673602f0d14760402fbd033b",
|
||||
"productName": "RS-LL-X1",
|
||||
"status": "INACTIVE",
|
||||
"tags": []
|
||||
},
|
||||
{
|
||||
"appId": "9782371cee894c0abbecb30efe4e7cd0",
|
||||
"appName": "DefaultApp_6736wwjm",
|
||||
"deviceId": "673602f0d14760402fbd033b_jsfb-B6A9C3",
|
||||
"nodeId": "jsfb-B6A9C3",
|
||||
"gatewayId": "673602f0d14760402fbd033b_jsfb-B6A9C3",
|
||||
"deviceName": "jsfb-B6A9C3",
|
||||
"nodeType": "GATEWAY",
|
||||
"productId": "673602f0d14760402fbd033b",
|
||||
"productName": "RS-LL-X1",
|
||||
"status": "INACTIVE",
|
||||
"tags": []
|
||||
},
|
||||
{
|
||||
"appId": "9782371cee894c0abbecb30efe4e7cd0",
|
||||
"appName": "DefaultApp_6736wwjm",
|
||||
"deviceId": "673602f0d14760402fbd033b_jsfb-EBF1D8",
|
||||
"nodeId": "jsfb-EBF1D8",
|
||||
"gatewayId": "673602f0d14760402fbd033b_jsfb-EBF1D8",
|
||||
"deviceName": "jsfb-EBF1D8",
|
||||
"nodeType": "GATEWAY",
|
||||
"productId": "673602f0d14760402fbd033b",
|
||||
"productName": "RS-LL-X1",
|
||||
"status": "INACTIVE",
|
||||
"tags": []
|
||||
},
|
||||
{
|
||||
"appId": "9782371cee894c0abbecb30efe4e7cd0",
|
||||
"appName": "DefaultApp_6736wwjm",
|
||||
"deviceId": "673602f0d14760402fbd033b_jsfb-65024A",
|
||||
"nodeId": "jsfb-65024A",
|
||||
"gatewayId": "673602f0d14760402fbd033b_jsfb-65024A",
|
||||
"deviceName": "jsfb-65024A",
|
||||
"nodeType": "GATEWAY",
|
||||
"productId": "673602f0d14760402fbd033b",
|
||||
"productName": "RS-LL-X1",
|
||||
"status": "INACTIVE",
|
||||
"tags": []
|
||||
},
|
||||
{
|
||||
"appId": "9782371cee894c0abbecb30efe4e7cd0",
|
||||
"appName": "DefaultApp_6736wwjm",
|
||||
"deviceId": "673602f0d14760402fbd033b_jsfb-7FC7DD",
|
||||
"nodeId": "jsfb-7FC7DD",
|
||||
"gatewayId": "673602f0d14760402fbd033b_jsfb-7FC7DD",
|
||||
"deviceName": "jsfb-7FC7DD",
|
||||
"nodeType": "GATEWAY",
|
||||
"productId": "673602f0d14760402fbd033b",
|
||||
"productName": "RS-LL-X1",
|
||||
"status": "INACTIVE",
|
||||
"tags": []
|
||||
},
|
||||
{
|
||||
"appId": "9782371cee894c0abbecb30efe4e7cd0",
|
||||
"appName": "DefaultApp_6736wwjm",
|
||||
"deviceId": "673602f0d14760402fbd033b_jsfb-7E9B30",
|
||||
"nodeId": "jsfb-7E9B30",
|
||||
"gatewayId": "673602f0d14760402fbd033b_jsfb-7E9B30",
|
||||
"deviceName": "jsfb-7E9B30",
|
||||
"nodeType": "GATEWAY",
|
||||
"productId": "673602f0d14760402fbd033b",
|
||||
"productName": "RS-LL-X1",
|
||||
"status": "INACTIVE",
|
||||
"tags": []
|
||||
},
|
||||
{
|
||||
"appId": "9782371cee894c0abbecb30efe4e7cd0",
|
||||
"appName": "DefaultApp_6736wwjm",
|
||||
"deviceId": "673602f0d14760402fbd033b_jsfb-56A928",
|
||||
"nodeId": "jsfb-56A928",
|
||||
"gatewayId": "673602f0d14760402fbd033b_jsfb-56A928",
|
||||
"deviceName": "jsfb-56A928",
|
||||
"nodeType": "GATEWAY",
|
||||
"productId": "673602f0d14760402fbd033b",
|
||||
"productName": "RS-LL-X1",
|
||||
"status": "INACTIVE",
|
||||
"tags": []
|
||||
},
|
||||
{
|
||||
"appId": "9782371cee894c0abbecb30efe4e7cd0",
|
||||
"appName": "DefaultApp_6736wwjm",
|
||||
"deviceId": "673602f0d14760402fbd033b_jsfb-F8843B",
|
||||
"nodeId": "jsfb-F8843B",
|
||||
"gatewayId": "673602f0d14760402fbd033b_jsfb-F8843B",
|
||||
"deviceName": "jsfb-F8843B",
|
||||
"nodeType": "GATEWAY",
|
||||
"productId": "673602f0d14760402fbd033b",
|
||||
"productName": "RS-LL-X1",
|
||||
"status": "INACTIVE",
|
||||
"tags": []
|
||||
},
|
||||
{
|
||||
"appId": "9782371cee894c0abbecb30efe4e7cd0",
|
||||
"appName": "DefaultApp_6736wwjm",
|
||||
"deviceId": "673602f0d14760402fbd033b_jsfb-8AA2BD",
|
||||
"nodeId": "jsfb-8AA2BD",
|
||||
"gatewayId": "673602f0d14760402fbd033b_jsfb-8AA2BD",
|
||||
"deviceName": "jsfb-8AA2BD",
|
||||
"nodeType": "GATEWAY",
|
||||
"productId": "673602f0d14760402fbd033b",
|
||||
"productName": "RS-LL-X1",
|
||||
"status": "INACTIVE",
|
||||
"tags": []
|
||||
},
|
||||
{
|
||||
"appId": "9782371cee894c0abbecb30efe4e7cd0",
|
||||
"appName": "DefaultApp_6736wwjm",
|
||||
"deviceId": "673602f0d14760402fbd033b_jsfb-C17E68",
|
||||
"nodeId": "jsfb-C17E68",
|
||||
"gatewayId": "673602f0d14760402fbd033b_jsfb-C17E68",
|
||||
"deviceName": "jsfb-C17E68",
|
||||
"nodeType": "GATEWAY",
|
||||
"productId": "673602f0d14760402fbd033b",
|
||||
"productName": "RS-LL-X1",
|
||||
"status": "INACTIVE",
|
||||
"tags": []
|
||||
},
|
||||
{
|
||||
"appId": "9782371cee894c0abbecb30efe4e7cd0",
|
||||
"appName": "DefaultApp_6736wwjm",
|
||||
"deviceId": "673602f0d14760402fbd033b_jsfb-7BE292",
|
||||
"nodeId": "jsfb-7BE292",
|
||||
"gatewayId": "673602f0d14760402fbd033b_jsfb-7BE292",
|
||||
"deviceName": "jsfb-7BE292",
|
||||
"nodeType": "GATEWAY",
|
||||
"productId": "673602f0d14760402fbd033b",
|
||||
"productName": "RS-LL-X1",
|
||||
"status": "INACTIVE",
|
||||
"tags": []
|
||||
},
|
||||
{
|
||||
"appId": "9782371cee894c0abbecb30efe4e7cd0",
|
||||
"appName": "DefaultApp_6736wwjm",
|
||||
"deviceId": "673602f0d14760402fbd033b_jsfb-6A1F29",
|
||||
"nodeId": "jsfb-6A1F29",
|
||||
"gatewayId": "673602f0d14760402fbd033b_jsfb-6A1F29",
|
||||
"deviceName": "jsfb-6A1F29",
|
||||
"nodeType": "GATEWAY",
|
||||
"productId": "673602f0d14760402fbd033b",
|
||||
"productName": "RS-LL-X1",
|
||||
"status": "INACTIVE",
|
||||
"tags": []
|
||||
},
|
||||
{
|
||||
"appId": "9782371cee894c0abbecb30efe4e7cd0",
|
||||
"appName": "DefaultApp_6736wwjm",
|
||||
"deviceId": "673602f0d14760402fbd033b_jsfb-B75756",
|
||||
"nodeId": "jsfb-B75756",
|
||||
"gatewayId": "673602f0d14760402fbd033b_jsfb-B75756",
|
||||
"deviceName": "jsfb-B75756",
|
||||
"nodeType": "GATEWAY",
|
||||
"productId": "673602f0d14760402fbd033b",
|
||||
"productName": "RS-LL-X1",
|
||||
"status": "INACTIVE",
|
||||
"tags": []
|
||||
},
|
||||
{
|
||||
"appId": "9782371cee894c0abbecb30efe4e7cd0",
|
||||
"appName": "DefaultApp_6736wwjm",
|
||||
"deviceId": "673602f0d14760402fbd033b_jsfb-69B0D2",
|
||||
"nodeId": "jsfb-69B0D2",
|
||||
"gatewayId": "673602f0d14760402fbd033b_jsfb-69B0D2",
|
||||
"deviceName": "jsfb-69B0D2",
|
||||
"nodeType": "GATEWAY",
|
||||
"productId": "673602f0d14760402fbd033b",
|
||||
"productName": "RS-LL-X1",
|
||||
"status": "INACTIVE",
|
||||
"tags": []
|
||||
},
|
||||
{
|
||||
"appId": "9782371cee894c0abbecb30efe4e7cd0",
|
||||
"appName": "DefaultApp_6736wwjm",
|
||||
"deviceId": "673602f0d14760402fbd033b_jsfb-D120B0",
|
||||
"nodeId": "jsfb-D120B0",
|
||||
"gatewayId": "673602f0d14760402fbd033b_jsfb-D120B0",
|
||||
"deviceName": "jsfb-D120B0",
|
||||
"nodeType": "GATEWAY",
|
||||
"productId": "673602f0d14760402fbd033b",
|
||||
"productName": "RS-LL-X1",
|
||||
"status": "INACTIVE",
|
||||
"tags": []
|
||||
},
|
||||
{
|
||||
"appId": "9782371cee894c0abbecb30efe4e7cd0",
|
||||
"appName": "DefaultApp_6736wwjm",
|
||||
"deviceId": "673602f0d14760402fbd033b_jsfb-CA0D7A",
|
||||
"nodeId": "jsfb-CA0D7A",
|
||||
"gatewayId": "673602f0d14760402fbd033b_jsfb-CA0D7A",
|
||||
"deviceName": "jsfb-CA0D7A",
|
||||
"nodeType": "GATEWAY",
|
||||
"productId": "673602f0d14760402fbd033b",
|
||||
"productName": "RS-LL-X1",
|
||||
"status": "INACTIVE",
|
||||
"tags": []
|
||||
},
|
||||
{
|
||||
"appId": "9782371cee894c0abbecb30efe4e7cd0",
|
||||
"appName": "DefaultApp_6736wwjm",
|
||||
"deviceId": "673602f0d14760402fbd033b_jsfb-89122A",
|
||||
"nodeId": "jsfb-89122A",
|
||||
"gatewayId": "673602f0d14760402fbd033b_jsfb-89122A",
|
||||
"deviceName": "jsfb-89122A",
|
||||
"nodeType": "GATEWAY",
|
||||
"productId": "673602f0d14760402fbd033b",
|
||||
"productName": "RS-LL-X1",
|
||||
"status": "INACTIVE",
|
||||
"tags": []
|
||||
},
|
||||
{
|
||||
"appId": "9782371cee894c0abbecb30efe4e7cd0",
|
||||
"appName": "DefaultApp_6736wwjm",
|
||||
"deviceId": "673602f0d14760402fbd033b_jsfb-31464E",
|
||||
"nodeId": "jsfb-31464E",
|
||||
"gatewayId": "673602f0d14760402fbd033b_jsfb-31464E",
|
||||
"deviceName": "jsfb-31464E",
|
||||
"nodeType": "GATEWAY",
|
||||
"productId": "673602f0d14760402fbd033b",
|
||||
"productName": "RS-LL-X1",
|
||||
"status": "INACTIVE",
|
||||
"tags": []
|
||||
},
|
||||
{
|
||||
"appId": "9782371cee894c0abbecb30efe4e7cd0",
|
||||
"appName": "DefaultApp_6736wwjm",
|
||||
"deviceId": "673602f0d14760402fbd033b_jsfb-2A1577",
|
||||
"nodeId": "jsfb-2A1577",
|
||||
"gatewayId": "673602f0d14760402fbd033b_jsfb-2A1577",
|
||||
"deviceName": "jsfb-2A1577",
|
||||
"nodeType": "GATEWAY",
|
||||
"productId": "673602f0d14760402fbd033b",
|
||||
"productName": "RS-LL-X1",
|
||||
"status": "INACTIVE",
|
||||
"tags": []
|
||||
},
|
||||
{
|
||||
"appId": "9782371cee894c0abbecb30efe4e7cd0",
|
||||
"appName": "DefaultApp_6736wwjm",
|
||||
"deviceId": "673602f0d14760402fbd033b_jsfb-1EC625",
|
||||
"nodeId": "jsfb-1EC625",
|
||||
"gatewayId": "673602f0d14760402fbd033b_jsfb-1EC625",
|
||||
"deviceName": "jsfb-1EC625",
|
||||
"nodeType": "GATEWAY",
|
||||
"productId": "673602f0d14760402fbd033b",
|
||||
"productName": "RS-LL-X1",
|
||||
"status": "INACTIVE",
|
||||
"tags": []
|
||||
},
|
||||
{
|
||||
"appId": "9782371cee894c0abbecb30efe4e7cd0",
|
||||
"appName": "DefaultApp_6736wwjm",
|
||||
"deviceId": "673602f0d14760402fbd033b_jsfb-36350C",
|
||||
"nodeId": "jsfb-36350C",
|
||||
"gatewayId": "673602f0d14760402fbd033b_jsfb-36350C",
|
||||
"deviceName": "jsfb-36350C",
|
||||
"nodeType": "GATEWAY",
|
||||
"productId": "673602f0d14760402fbd033b",
|
||||
"productName": "RS-LL-X1",
|
||||
"status": "INACTIVE",
|
||||
"tags": []
|
||||
},
|
||||
{
|
||||
"appId": "9782371cee894c0abbecb30efe4e7cd0",
|
||||
"appName": "DefaultApp_6736wwjm",
|
||||
"deviceId": "673602f0d14760402fbd033b_jsfb-F29258",
|
||||
"nodeId": "jsfb-F29258",
|
||||
"gatewayId": "673602f0d14760402fbd033b_jsfb-F29258",
|
||||
"deviceName": "jsfb-F29258",
|
||||
"nodeType": "GATEWAY",
|
||||
"productId": "673602f0d14760402fbd033b",
|
||||
"productName": "RS-LL-X1",
|
||||
"status": "INACTIVE",
|
||||
"tags": []
|
||||
},
|
||||
{
|
||||
"appId": "9782371cee894c0abbecb30efe4e7cd0",
|
||||
"appName": "DefaultApp_6736wwjm",
|
||||
"deviceId": "673602f0d14760402fbd033b_jsfb-8942CE",
|
||||
"nodeId": "jsfb-8942CE",
|
||||
"gatewayId": "673602f0d14760402fbd033b_jsfb-8942CE",
|
||||
"deviceName": "jsfb-8942CE",
|
||||
"nodeType": "GATEWAY",
|
||||
"productId": "673602f0d14760402fbd033b",
|
||||
"productName": "RS-LL-X1",
|
||||
"status": "INACTIVE",
|
||||
"tags": []
|
||||
},
|
||||
{
|
||||
"appId": "9782371cee894c0abbecb30efe4e7cd0",
|
||||
"appName": "DefaultApp_6736wwjm",
|
||||
"deviceId": "673602f0d14760402fbd033b_jsfb-B1398A",
|
||||
"nodeId": "jsfb-B1398A",
|
||||
"gatewayId": "673602f0d14760402fbd033b_jsfb-B1398A",
|
||||
"deviceName": "jsfb-B1398A",
|
||||
"nodeType": "GATEWAY",
|
||||
"productId": "673602f0d14760402fbd033b",
|
||||
"productName": "RS-LL-X1",
|
||||
"status": "INACTIVE",
|
||||
"tags": []
|
||||
},
|
||||
{
|
||||
"appId": "9782371cee894c0abbecb30efe4e7cd0",
|
||||
"appName": "DefaultApp_6736wwjm",
|
||||
"deviceId": "673602f0d14760402fbd033b_jsfb-465F0C",
|
||||
"nodeId": "jsfb-465F0C",
|
||||
"gatewayId": "673602f0d14760402fbd033b_jsfb-465F0C",
|
||||
"deviceName": "jsfb-465F0C",
|
||||
"nodeType": "GATEWAY",
|
||||
"productId": "673602f0d14760402fbd033b",
|
||||
"productName": "RS-LL-X1",
|
||||
"status": "INACTIVE",
|
||||
"tags": []
|
||||
},
|
||||
{
|
||||
"appId": "9782371cee894c0abbecb30efe4e7cd0",
|
||||
"appName": "DefaultApp_6736wwjm",
|
||||
"deviceId": "673602f0d14760402fbd033b_jsfb-E169C2",
|
||||
"nodeId": "jsfb-E169C2",
|
||||
"gatewayId": "673602f0d14760402fbd033b_jsfb-E169C2",
|
||||
"deviceName": "jsfb-E169C2",
|
||||
"nodeType": "GATEWAY",
|
||||
"productId": "673602f0d14760402fbd033b",
|
||||
"productName": "RS-LL-X1",
|
||||
"status": "INACTIVE",
|
||||
"tags": []
|
||||
},
|
||||
{
|
||||
"appId": "9782371cee894c0abbecb30efe4e7cd0",
|
||||
"appName": "DefaultApp_6736wwjm",
|
||||
"deviceId": "673602f0d14760402fbd033b_jsfb-E82E16",
|
||||
"nodeId": "jsfb-E82E16",
|
||||
"gatewayId": "673602f0d14760402fbd033b_jsfb-E82E16",
|
||||
"deviceName": "jsfb-E82E16",
|
||||
"nodeType": "GATEWAY",
|
||||
"productId": "673602f0d14760402fbd033b",
|
||||
"productName": "RS-LL-X1",
|
||||
"status": "INACTIVE",
|
||||
"tags": []
|
||||
},
|
||||
{
|
||||
"appId": "9782371cee894c0abbecb30efe4e7cd0",
|
||||
"appName": "DefaultApp_6736wwjm",
|
||||
"deviceId": "673602f0d14760402fbd033b_jsfb-6FA0C5",
|
||||
"nodeId": "jsfb-6FA0C5",
|
||||
"gatewayId": "673602f0d14760402fbd033b_jsfb-6FA0C5",
|
||||
"deviceName": "jsfb-6FA0C5",
|
||||
"nodeType": "GATEWAY",
|
||||
"productId": "673602f0d14760402fbd033b",
|
||||
"productName": "RS-LL-X1",
|
||||
"status": "INACTIVE",
|
||||
"tags": []
|
||||
},
|
||||
{
|
||||
"appId": "9782371cee894c0abbecb30efe4e7cd0",
|
||||
"appName": "DefaultApp_6736wwjm",
|
||||
"deviceId": "673602f0d14760402fbd033b_jsfb-FC04F2",
|
||||
"nodeId": "jsfb-FC04F2",
|
||||
"gatewayId": "673602f0d14760402fbd033b_jsfb-FC04F2",
|
||||
"deviceName": "jsfb-FC04F2",
|
||||
"nodeType": "GATEWAY",
|
||||
"productId": "673602f0d14760402fbd033b",
|
||||
"productName": "RS-LL-X1",
|
||||
"status": "INACTIVE",
|
||||
"tags": []
|
||||
},
|
||||
{
|
||||
"appId": "9782371cee894c0abbecb30efe4e7cd0",
|
||||
"appName": "DefaultApp_6736wwjm",
|
||||
"deviceId": "673602f0d14760402fbd033b_jsfb-C5D5F4",
|
||||
"nodeId": "jsfb-C5D5F4",
|
||||
"gatewayId": "673602f0d14760402fbd033b_jsfb-C5D5F4",
|
||||
"deviceName": "jsfb-C5D5F4",
|
||||
"nodeType": "GATEWAY",
|
||||
"productId": "673602f0d14760402fbd033b",
|
||||
"productName": "RS-LL-X1",
|
||||
"status": "INACTIVE",
|
||||
"tags": []
|
||||
},
|
||||
{
|
||||
"appId": "9782371cee894c0abbecb30efe4e7cd0",
|
||||
"appName": "DefaultApp_6736wwjm",
|
||||
"deviceId": "673602f0d14760402fbd033b_jsfb-B96E18",
|
||||
"nodeId": "jsfb-B96E18",
|
||||
"gatewayId": "673602f0d14760402fbd033b_jsfb-B96E18",
|
||||
"deviceName": "jsfb-B96E18",
|
||||
"nodeType": "GATEWAY",
|
||||
"productId": "673602f0d14760402fbd033b",
|
||||
"productName": "RS-LL-X1",
|
||||
"status": "INACTIVE",
|
||||
"tags": []
|
||||
},
|
||||
{
|
||||
"appId": "9782371cee894c0abbecb30efe4e7cd0",
|
||||
"appName": "DefaultApp_6736wwjm",
|
||||
"deviceId": "673602f0d14760402fbd033b_jsfb-37285C",
|
||||
"nodeId": "jsfb-37285C",
|
||||
"gatewayId": "673602f0d14760402fbd033b_jsfb-37285C",
|
||||
"deviceName": "jsfb-37285C",
|
||||
"nodeType": "GATEWAY",
|
||||
"productId": "673602f0d14760402fbd033b",
|
||||
"productName": "RS-LL-X1",
|
||||
"status": "INACTIVE",
|
||||
"tags": []
|
||||
},
|
||||
{
|
||||
"appId": "9782371cee894c0abbecb30efe4e7cd0",
|
||||
"appName": "DefaultApp_6736wwjm",
|
||||
"deviceId": "673602f0d14760402fbd033b_jsfb-00A874",
|
||||
"nodeId": "jsfb-00A874",
|
||||
"gatewayId": "673602f0d14760402fbd033b_jsfb-00A874",
|
||||
"deviceName": "jsfb-00A874",
|
||||
"nodeType": "GATEWAY",
|
||||
"productId": "673602f0d14760402fbd033b",
|
||||
"productName": "RS-LL-X1",
|
||||
"status": "INACTIVE",
|
||||
"tags": []
|
||||
},
|
||||
{
|
||||
"appId": "9782371cee894c0abbecb30efe4e7cd0",
|
||||
"appName": "DefaultApp_6736wwjm",
|
||||
"deviceId": "673602f0d14760402fbd033b_jsfb-7D2A0B",
|
||||
"nodeId": "jsfb-7D2A0B",
|
||||
"gatewayId": "673602f0d14760402fbd033b_jsfb-7D2A0B",
|
||||
"deviceName": "jsfb-7D2A0B",
|
||||
"nodeType": "GATEWAY",
|
||||
"productId": "673602f0d14760402fbd033b",
|
||||
"productName": "RS-LL-X1",
|
||||
"status": "INACTIVE",
|
||||
"tags": []
|
||||
},
|
||||
{
|
||||
"appId": "9782371cee894c0abbecb30efe4e7cd0",
|
||||
"appName": "DefaultApp_6736wwjm",
|
||||
"deviceId": "673602f0d14760402fbd033b_jsfb-69681C",
|
||||
"nodeId": "jsfb-69681C",
|
||||
"gatewayId": "673602f0d14760402fbd033b_jsfb-69681C",
|
||||
"deviceName": "jsfb-69681C",
|
||||
"nodeType": "GATEWAY",
|
||||
"productId": "673602f0d14760402fbd033b",
|
||||
"productName": "RS-LL-X1",
|
||||
"status": "INACTIVE",
|
||||
"tags": []
|
||||
},
|
||||
{
|
||||
"appId": "9782371cee894c0abbecb30efe4e7cd0",
|
||||
"appName": "DefaultApp_6736wwjm",
|
||||
"deviceId": "673602f0d14760402fbd033b_jsfb-6D10F3",
|
||||
"nodeId": "jsfb-6D10F3",
|
||||
"gatewayId": "673602f0d14760402fbd033b_jsfb-6D10F3",
|
||||
"deviceName": "jsfb-6D10F3",
|
||||
"nodeType": "GATEWAY",
|
||||
"productId": "673602f0d14760402fbd033b",
|
||||
"productName": "RS-LL-X1",
|
||||
"status": "INACTIVE",
|
||||
"tags": []
|
||||
}
|
||||
],
|
||||
"page": {
|
||||
"count": 227,
|
||||
"marker": "6881dce05ebba32371ca42f6"
|
||||
},
|
||||
"httpStatusCode": 200
|
||||
}
|
@ -1,62 +0,0 @@
|
||||
{
|
||||
online_time=20250829T113156Z,
|
||||
latitude=112.541115,
|
||||
software_version=ioT,
|
||||
vtxdb_version=1.1.9-beta-ansun,
|
||||
status=ONLINE,
|
||||
longitude=28.280514
|
||||
}
|
||||
|
||||
[
|
||||
class
|
||||
DeviceShadowData
|
||||
{
|
||||
serviceId: ExpectedDelivery
|
||||
desired: class
|
||||
DeviceShadowProperties
|
||||
{
|
||||
properties: null
|
||||
eventTime: 20250830T014623Z
|
||||
}
|
||||
reported
|
||||
:
|
||||
class
|
||||
DeviceShadowProperties
|
||||
{
|
||||
properties: null
|
||||
eventTime: null
|
||||
}
|
||||
version
|
||||
:
|
||||
750
|
||||
},
|
||||
class
|
||||
DeviceShadowData
|
||||
{
|
||||
serviceId: VortXDB
|
||||
desired: class
|
||||
DeviceShadowProperties
|
||||
{
|
||||
properties: null
|
||||
eventTime: null
|
||||
}
|
||||
reported
|
||||
:
|
||||
class
|
||||
DeviceShadowProperties
|
||||
{
|
||||
properties: {
|
||||
online_time=20250829T113156Z,
|
||||
latitude=112.541115,
|
||||
software_version=ioT,
|
||||
vtxdb_version=1.1.9-beta-ansun,
|
||||
status=ONLINE,
|
||||
longitude=28.280514
|
||||
}
|
||||
eventTime: 20250829T113156Z
|
||||
}
|
||||
version
|
||||
:
|
||||
37
|
||||
}
|
||||
]
|
@ -1,12 +0,0 @@
|
||||
{
|
||||
"pageNo": 1,
|
||||
"pageSize": 10,
|
||||
"isAsc": true,
|
||||
"sortBy": "id",
|
||||
"deviceNameParam": "",
|
||||
"statusParam": "",
|
||||
"softwareVersionParam": "",
|
||||
"vtxdbVersionParam": "",
|
||||
"beginTime": "",
|
||||
"endTime": ""
|
||||
}
|
@ -1,3 +0,0 @@
|
||||
IotMsgNotifyData(header=IotMsgHeader(productId=673602f0d14760402fbd033b, deviceId=673602f0d14760402fbd033b_jsfb-DA, nodeId=jsfb-DA, appId=9782371cee894c0abbecb30efe4e7cd0, gatewayId=673602f0d14760402fbd033b_jsfb-DA), body=IotMsgBody(services=[IotMsgService(serviceId=VortXDB, properties={online_time=20250901T032819Z, latitude=112.541115, software_version=ioT, vtxdb_version=1.1.9-beta-ansun, status=ONLINE, longitude=28.280514}, eventTime=20250901T032819Z)]))
|
||||
|
||||
IotMsgNotifyData(header=IotMsgHeader(productId=673602f0d14760402fbd033b, deviceId=673602f0d14760402fbd033b_jsfb-DA, nodeId=jsfb-DA, appId=9782371cee894c0abbecb30efe4e7cd0, gatewayId=673602f0d14760402fbd033b_jsfb-DA), body=IotMsgBody(services=[IotMsgService(serviceId=VortXDB, properties={online_time=20250901T041315Z, latitude=112.541115, software_version=ioT, vtxdb_version=1.1.9-beta-ansun, status=ONLINE, longitude=28.280514}, eventTime=20250901T041316Z)]))
|
@ -1,181 +0,0 @@
|
||||
package com.storm.device;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.date.DatePattern;
|
||||
import cn.hutool.core.date.LocalDateTimeUtil;
|
||||
import cn.hutool.json.JSONObject;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import com.alibaba.nacos.common.utils.CollectionUtils;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.huaweicloud.sdk.iotda.v5.IoTDAClient;
|
||||
import com.huaweicloud.sdk.iotda.v5.model.*;
|
||||
import com.storm.common.core.exception.base.BaseException;
|
||||
import com.storm.common.core.utils.StringUtils;
|
||||
import com.storm.device.domain.po.Device;
|
||||
import com.storm.device.service.IDeviceService;
|
||||
import com.storm.device.service.impl.DeviceServiceImpl;
|
||||
import com.storm.device.service.impl.MassageTaskServiceImpl;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZoneId;
|
||||
import java.time.ZoneOffset;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author DengAo
|
||||
* @date 2025/8/27 17:11
|
||||
*/
|
||||
@Slf4j
|
||||
@SpringBootTest
|
||||
public class IotTest {
|
||||
@Resource
|
||||
private IoTDAClient client;
|
||||
|
||||
@Resource
|
||||
private DeviceServiceImpl deviceService;
|
||||
|
||||
@Test
|
||||
public void testSyncDevicesFromIoT() {
|
||||
List<Device> deviceList = new ArrayList<>();
|
||||
String marker = null;
|
||||
boolean hasMore = true;
|
||||
|
||||
try {
|
||||
while (hasMore) {
|
||||
// 创建请求对象
|
||||
ListDevicesRequest request = new ListDevicesRequest();
|
||||
|
||||
// 设置分页参数
|
||||
if (marker != null) {
|
||||
request.setMarker(marker);
|
||||
}
|
||||
request.setLimit(50); // 每页最大100条
|
||||
|
||||
// 发起请求获取设备列表
|
||||
ListDevicesResponse response = client.listDevices(request);
|
||||
|
||||
if (response.getHttpStatusCode() != 200) {
|
||||
throw new BaseException("物联网接口 - 查询设备列表失败,HTTP状态码: " + response.getHttpStatusCode());
|
||||
}
|
||||
|
||||
log.info("50台设备信息:{}", JSONUtil.toJsonStr(response.getDevices()));
|
||||
|
||||
// 处理返回的设备数据
|
||||
if (CollectionUtils.isNotEmpty(response.getDevices())) {
|
||||
for (QueryDeviceSimplify queryDeviceSimplify : response.getDevices()) {
|
||||
Device device = new Device();
|
||||
device.setDeviceId(queryDeviceSimplify.getDeviceId());
|
||||
device.setDeviceName(queryDeviceSimplify.getDeviceName());
|
||||
device.setProductId(queryDeviceSimplify.getProductId());
|
||||
device.setProductName(queryDeviceSimplify.getProductName());
|
||||
|
||||
// 可以设置其他默认值
|
||||
device.setDeviceType(1); // 默认设备类型
|
||||
device.setIsDeleted(0); // 未删除
|
||||
|
||||
deviceList.add(device);
|
||||
}
|
||||
}
|
||||
|
||||
// 检查是否还有更多数据
|
||||
if (CollectionUtils.isNotEmpty(response.getDevices()) && StringUtils.isNotEmpty(response.getPage().getMarker())) {
|
||||
marker = response.getPage().getMarker();
|
||||
} else {
|
||||
hasMore = false;
|
||||
}
|
||||
// 休眠100毫秒
|
||||
Thread.sleep(100);
|
||||
}
|
||||
|
||||
log.info("成功同步 {} 台设备信息", deviceList.size());
|
||||
} catch (Exception e) {
|
||||
log.error("同步设备列表失败: {}", e.getMessage(), e);
|
||||
throw new BaseException("同步设备列表失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSyncDevicesFromIoT2() {
|
||||
String marker = "675299a3f9024677dbd36a08";
|
||||
//请求参数
|
||||
ListDevicesRequest request = new ListDevicesRequest();
|
||||
|
||||
//设置条数
|
||||
request.setLimit(50);
|
||||
|
||||
request.setMarker(marker);
|
||||
|
||||
//发起请求
|
||||
ListDevicesResponse response = client.listDevices(request);
|
||||
if (response.getHttpStatusCode() != 200) {
|
||||
throw new BaseException("物联网接口 - 查询设备,同步失败");
|
||||
}
|
||||
|
||||
log.info("50台设备信息:{}", JSONUtil.toJsonStr(response.getDevices()));
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateDeviceStatusLog() {
|
||||
//通过client 调用查询设备影子数据
|
||||
ShowDeviceShadowRequest request = new ShowDeviceShadowRequest();
|
||||
request.setDeviceId("673602f0d14760402fbd033b_jsfb-66E903");
|
||||
ShowDeviceShadowResponse response = client.showDeviceShadow(request);
|
||||
|
||||
//解析数据
|
||||
List<DeviceShadowData> shadow = response.getShadow();
|
||||
if (CollUtil.isEmpty(shadow)) {
|
||||
log.error("设备影子数据为空");
|
||||
return;
|
||||
}
|
||||
JSONObject jsonObject = JSONUtil.parseObj(shadow.get(1).getReported().getProperties());
|
||||
|
||||
String eventTimeStr = shadow.get(1).getReported().getEventTime();
|
||||
LocalDateTime time = otherLocalDateTime(eventTimeStr);
|
||||
|
||||
//打印返回的结果
|
||||
log.info("设备影子数据:{}", jsonObject);
|
||||
System.out.println("--------------------------------------");
|
||||
log.info("设备影子数据时间:{}", time);
|
||||
// 打印shadow数据
|
||||
System.out.println("--------------------------------------");
|
||||
log.info("设备影子数据:{}", shadow);
|
||||
System.out.println("--------------------------------------");
|
||||
LocalDateTime onlineTime = otherLocalDateTime(jsonObject.get("online_time", String.class));
|
||||
log.info("设备影子online_time时间:{}", onlineTime);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试百度地图API获取地址
|
||||
*/
|
||||
@Test
|
||||
public void testGetLocation() {
|
||||
String location = deviceService.getLocationFromCoordinates(22.624244620774185, 114.03790755307914);
|
||||
log.info("设备地址:{}", location);
|
||||
}
|
||||
|
||||
public LocalDateTime toLocalDateTime(String activeTime) {
|
||||
LocalDateTime time = LocalDateTimeUtil.parse(activeTime, DatePattern.UTC_MS_PATTERN);
|
||||
time = time.atZone(ZoneId.from(ZoneOffset.UTC))
|
||||
.withZoneSameInstant(ZoneId.of("Asia/Shanghai"))
|
||||
.toLocalDateTime();
|
||||
return time;
|
||||
}
|
||||
|
||||
public LocalDateTime otherLocalDateTime(String activeTime) {
|
||||
LocalDateTime time = LocalDateTimeUtil.parse(activeTime, "yyyyMMdd'T'HHmmss'Z'");
|
||||
time = time.atZone(ZoneId.from(ZoneOffset.UTC))
|
||||
.withZoneSameInstant(ZoneId.of("Asia/Shanghai"))
|
||||
.toLocalDateTime();
|
||||
return time;
|
||||
}
|
||||
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user