Explorar el Código

add server_sql

zqy hace 2 años
padre
commit
81c22fd4e4
Se han modificado 85 ficheros con 7389 adiciones y 0 borrados
  1. 78 0
      Server/.gitignore
  2. 7 0
      Server/Dockerfile
  3. 205 0
      Server/README.md
  4. BIN
      Server/design/component.png
  5. BIN
      Server/design/database.png
  6. BIN
      Server/design/interface1.png
  7. BIN
      Server/design/iot_data_queue.png
  8. BIN
      Server/design/iot_data_transfer.png
  9. 77 0
      Server/distschedule-core/pom.xml
  10. 39 0
      Server/distschedule-core/src/main/java/com/example/distschedule/DistScheduleApplication.java
  11. 49 0
      Server/distschedule-core/src/main/java/com/example/distschedule/config/Swagger3Config.java
  12. 219 0
      Server/distschedule-core/src/main/java/com/example/distschedule/controller/DeviceController.java
  13. 69 0
      Server/distschedule-core/src/main/java/com/example/distschedule/controller/DeviceTypeController.java
  14. 96 0
      Server/distschedule-core/src/main/java/com/example/distschedule/controller/DistscheduleGlobalExceptionHandler.java
  15. 212 0
      Server/distschedule-core/src/main/java/com/example/distschedule/controller/FamilyController.java
  16. 181 0
      Server/distschedule-core/src/main/java/com/example/distschedule/controller/ScheduleController.java
  17. 106 0
      Server/distschedule-core/src/main/java/com/example/distschedule/controller/UserController.java
  18. 318 0
      Server/distschedule-core/src/main/java/com/example/distschedule/listener/DeviceMsgListener.java
  19. 297 0
      Server/distschedule-core/src/main/java/com/example/distschedule/listener/DevicePropertyListener.java
  20. 299 0
      Server/distschedule-core/src/main/java/com/example/distschedule/listener/DeviceStatusListener.java
  21. 35 0
      Server/distschedule-core/src/main/java/com/example/distschedule/rabbitmq/RabbitConf.java
  22. 0 0
      Server/distschedule-core/src/main/resources/.gitkeep
  23. 74 0
      Server/distschedule-core/src/main/resources/application-dockercompose.properties
  24. 73 0
      Server/distschedule-core/src/main/resources/application-local.properties
  25. 73 0
      Server/distschedule-core/src/main/resources/application.properties
  26. 15 0
      Server/distschedule-core/src/main/resources/banner.txt
  27. 72 0
      Server/distschedule-core/src/main/resources/log4j2.xml
  28. 0 0
      Server/distschedule-core/src/test/java/.gitkeep
  29. 82 0
      Server/distschedule-dao/pom.xml
  30. 67 0
      Server/distschedule-dao/src/main/java/com/example/distschedule/dao/mapper/DeviceMapper.java
  31. 38 0
      Server/distschedule-dao/src/main/java/com/example/distschedule/dao/mapper/DeviceTypeMapper.java
  32. 76 0
      Server/distschedule-dao/src/main/java/com/example/distschedule/dao/mapper/FamilyMapper.java
  33. 74 0
      Server/distschedule-dao/src/main/java/com/example/distschedule/dao/mapper/ScheduleMapper.java
  34. 55 0
      Server/distschedule-dao/src/main/java/com/example/distschedule/dao/mapper/UserMapper.java
  35. 120 0
      Server/distschedule-dao/src/main/java/com/example/distschedule/dao/model/Device.java
  36. 85 0
      Server/distschedule-dao/src/main/java/com/example/distschedule/dao/model/DeviceType.java
  37. 70 0
      Server/distschedule-dao/src/main/java/com/example/distschedule/dao/model/Family.java
  38. 140 0
      Server/distschedule-dao/src/main/java/com/example/distschedule/dao/model/Schedule.java
  39. 70 0
      Server/distschedule-dao/src/main/java/com/example/distschedule/dao/model/User.java
  40. 48 0
      Server/distschedule-dao/src/main/java/com/example/distschedule/dao/model/UserDevice.java
  41. 73 0
      Server/distschedule-dao/src/main/java/com/example/distschedule/dao/model/UserFamily.java
  42. 55 0
      Server/distschedule-dao/src/main/java/com/example/distschedule/dao/model/UserSchedule.java
  43. 21 0
      Server/distschedule-dao/src/main/resources/dao.properties
  44. 193 0
      Server/distschedule-dao/src/main/resources/db/migration/V1__CreateTables.sql
  45. 119 0
      Server/distschedule-dao/src/main/resources/generatorConfig.xml
  46. 0 0
      Server/distschedule-dao/src/test/java/.gitkeep
  47. 101 0
      Server/distschedule-service/pom.xml
  48. 166 0
      Server/distschedule-service/src/main/java/com/example/distschedule/config/RestClient.java
  49. 58 0
      Server/distschedule-service/src/main/java/com/example/distschedule/config/ServiceConfig.java
  50. 69 0
      Server/distschedule-service/src/main/java/com/example/distschedule/dto/CreateDeviceDto.java
  51. 110 0
      Server/distschedule-service/src/main/java/com/example/distschedule/dto/CreateScheduleDto.java
  52. 51 0
      Server/distschedule-service/src/main/java/com/example/distschedule/dto/DeviceCommandDto.java
  53. 138 0
      Server/distschedule-service/src/main/java/com/example/distschedule/dto/DeviceDto.java
  54. 98 0
      Server/distschedule-service/src/main/java/com/example/distschedule/dto/DeviceTypeDto.java
  55. 31 0
      Server/distschedule-service/src/main/java/com/example/distschedule/dto/FamilyCreateDto.java
  56. 93 0
      Server/distschedule-service/src/main/java/com/example/distschedule/dto/FamilyDto.java
  57. 70 0
      Server/distschedule-service/src/main/java/com/example/distschedule/dto/ResponseResult.java
  58. 165 0
      Server/distschedule-service/src/main/java/com/example/distschedule/dto/ScheduleDto.java
  59. 28 0
      Server/distschedule-service/src/main/java/com/example/distschedule/dto/UpdateDeviceDto.java
  60. 28 0
      Server/distschedule-service/src/main/java/com/example/distschedule/dto/UpdateFamilyDto.java
  61. 69 0
      Server/distschedule-service/src/main/java/com/example/distschedule/dto/UserDeviceDto.java
  62. 84 0
      Server/distschedule-service/src/main/java/com/example/distschedule/dto/UserDto.java
  63. 87 0
      Server/distschedule-service/src/main/java/com/example/distschedule/dto/UserFamilyDto.java
  64. 23 0
      Server/distschedule-service/src/main/java/com/example/distschedule/enums/DeviceState.java
  65. 60 0
      Server/distschedule-service/src/main/java/com/example/distschedule/enums/HandDeviceType.java
  66. 23 0
      Server/distschedule-service/src/main/java/com/example/distschedule/enums/ScheduleComandType.java
  67. 181 0
      Server/distschedule-service/src/main/java/com/example/distschedule/error/ErrorCode.java
  68. 50 0
      Server/distschedule-service/src/main/java/com/example/distschedule/exception/DistscheduleBaseException.java
  69. 28 0
      Server/distschedule-service/src/main/java/com/example/distschedule/exception/DistscheduleDeviceException.java
  70. 28 0
      Server/distschedule-service/src/main/java/com/example/distschedule/exception/DistscheduleFamilyException.java
  71. 28 0
      Server/distschedule-service/src/main/java/com/example/distschedule/exception/DistschedulePermissionException.java
  72. 28 0
      Server/distschedule-service/src/main/java/com/example/distschedule/exception/DistscheduleScheduleException.java
  73. 28 0
      Server/distschedule-service/src/main/java/com/example/distschedule/exception/DistscheduleUserException.java
  74. 362 0
      Server/distschedule-service/src/main/java/com/example/distschedule/service/DeviceService.java
  75. 57 0
      Server/distschedule-service/src/main/java/com/example/distschedule/service/DeviceTypeService.java
  76. 211 0
      Server/distschedule-service/src/main/java/com/example/distschedule/service/FamilyService.java
  77. 54 0
      Server/distschedule-service/src/main/java/com/example/distschedule/service/IOTCloudService.java
  78. 443 0
      Server/distschedule-service/src/main/java/com/example/distschedule/service/ScheduleService.java
  79. 96 0
      Server/distschedule-service/src/main/java/com/example/distschedule/service/UserService.java
  80. 0 0
      Server/distschedule-service/src/main/resources/.gitkeep
  81. 0 0
      Server/distschedule-service/src/main/resources/service.properties
  82. 0 0
      Server/distschedule-service/src/test/java/.gitkeep
  83. 22 0
      Server/docker-compose.yml
  84. 235 0
      Server/pom.xml
  85. 6 0
      Server/share.md

+ 78 - 0
Server/.gitignore

@@ -0,0 +1,78 @@
+# Output Directory
+**/target/
+.idea/
+BuildAccelerateSvr.iml
+model/*
+
+
+# C pre-compile
+*.gch
+*.pch
+
+# C compile
+*.a
+*.o
+*.ko
+*.la
+*.lo
+*.obj
+*.elf
+*.so
+*.so.*
+*.dylib
+*.exe
+*.lib
+*.dll
+*.out
+*.app
+*.hex
+
+# Debug files
+*.dSYM/
+
+# Java
+*.class
+
+# Java Package Files
+*.war
+*.ear
+
+# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
+hs_err_pid*
+
+# Zip Files
+*.rar
+*.7z
+*.tar
+*.gz
+
+# Ant
+
+# Compiled Python
+__pycache__/
+*.py[cod]
+*py.class
+
+# Eclipse
+.settings/
+.classpath
+.project
+
+# IntelliJ, based on http://devnet.jetbrains.net/docs/DOC-1186
+.idea/
+*.iml
+
+# logs and trace
+*.log
+*.trace
+
+# vi swap
+*.swp
+
+# Backup Files
+*.bak
+*.old
+
+# SVN metadata
+.svn/
+

+ 7 - 0
Server/Dockerfile

@@ -0,0 +1,7 @@
+FROM openjdk:8-jre
+WORKDIR /app
+ADD distschedule-core/target/distschedule-core-1.0.0-SNAPSHOT.jar distschedule-core-1.0.0-SNAPSHOT.jar
+EXPOSE 8080
+ENTRYPOINT ["java","-jar"]
+CMD ["distschedule-core-1.0.0-SNAPSHOT.jar"]
+# docker build -t distschedule .

+ 205 - 0
Server/README.md

@@ -0,0 +1,205 @@
+# DistScheduleServer
+数字管家服务端说明
+
+## 数字管家整体架构视图
+![component](design/component.png)
+模块说明:
+
+- FA:  数字管家HarmonyOS应用
+- IOT Device: 基于OpenHarmony开发板的智能设备,如台灯,窗帘
+- DistScheduleServer: 数字管家服务端,对FA提供Restful接口,并调用Huawei IOT Cloud管理IOT设备
+- Huawei IOT Cloud: 华为IOT云,用于以mqtt协议,与设备通信。如下发命令,上报状态
+- RabbitMQ:消息队列,用于获取告警信息,通知给手机FA
+- MySql:数据库。保存日程,设备,用户,家庭等信息
+
+## 如何独立搭建数字管家服务
+
+#### 步骤1.准备部署环境
+
+1.Ubuntu 18.04.4 or 20
+
+2.mysql5.7 (sudo apt install mysql-server)  ,请注意不要使用mysql8+,允许外部访问mysql,修改root默认密码或配置新用户(用于服务端mysql配置)
+
+3.openjdk/jdk 1.8 (sudo apt-get install openjdk-8-jdk)
+
+4.RabbitMQ 3.6.10
+
+- rabbitmq安装:https://blog.csdn.net/u010889616/article/details/80643892
+
+- rabbitmq web页面管理启用:
+
+  ```
+  rabbitmq-plugins enable rabbitmq_management
+  ```
+- 页面管理:http://ip:15672, 服务配置端口为5672
+  
+
+5.IOT云服务([华为云](https://console.huaweicloud.com/iotdm/?region=cn-north-4#/dm-portal/home))  
+
+#### 步骤2. 下载代码
+```bash
+git clone git@gitee.com:openharmony-sig/knowledge_demo_smart_home.git
+```
+
+#### 步骤3. 华为IOT云配置
+
+1.使用华为云IOT服务:[地址](https://console.huaweicloud.com/iotdm/?region=cn-north-4#/dm-portal/instance),建议先[极速体验](https://support.huaweicloud.com/qs-iothub/iot_05_00010.html)
+
+2.新增IOT设备,创建产品(Profile),定义属性,命令,参考[profile](https://gitee.com/openharmony-sig/knowledge_demo_smart_home/tree/master/profile)
+
+3.申请云服务AK,SK以及appId,这些信息是用于连接华为云权限认证的,把他们配置到文件application-local.properties中.参考[获取AK/SK](https://support.huaweicloud.com/devg-apisign/api-sign-provide-aksk.html)
+
+```
+cloud.iot.ak=*****
+cloud.iot.sk=*****
+cloud.iot.appId=*****
+```
+
+3.创建转发规则,转发设备信息到AMQP(需要转发设备属性,设备命令,设备消息,这样数字管家服务才能从IOT云中监听到这些信息的变化)
+配置队列名,队列地址,accessKey,password到配置文件application-xxx.properties中.参考[数据转发介绍](https://support.huaweicloud.com/usermanual-iothub/iot_01_0024.html)
+
+**注意:**转发规则的数据来源分别选择“设备属性,设备命令,设备消息”,转发目标要选择AMQP推送消息队列,并分别建立不同的消息队列接收消息,分别接受设备属性,设备命令,设备消息的消息。
+
+![image-20211230094153015](design/iot_data_queue.png)
+
+
+![iot_data_transfer](design/iot_data_transfer.png)
+
+```
+cloud.iot.amqp.accessKey= #接入凭证,添加转发目标时,点击接入凭证获取
+cloud.iot.amqp.password=  #接入凭证密码,添加转发目标时,点击接入凭证获取
+cloud.iot.amqp.queueStatusName=  #设备状态队列名称(不是规则名称)
+cloud.iot.amqp.queuePropertyName= #设备属性队列名称(不是规则名称)
+cloud.iot.amqp.queueMsgName= #设备消息队列名称(不是规则名称)
+cloud.iot.amqp.connectionUrl= #AMQP连接地址,点击“总览--》平台接入地址”获取
+```
+
+#### 步骤4. 增加sql文件,在数据库中录入新增设备类型
+将上述新增的设备名称,服务ID,产品ID(IOT云界面中,点击“产品”菜单可查看),插入到数据库device_type表中。建议是在distschedule-dao\src\main\resources\db\migration目录中新增SQL,如V2_insertProductType.sql
+
+```
+INSERT INTO device_type (`name`, `name_en`, `service_id`, `product_id`, `category`)
+values ('智能风扇', 'fan', 'SmartFan', '6150601d88056b027dd2ca47', 1);
+```
+
+**注意:**服务使用[flyway](https://www.jianshu.com/p/733a5a95819e)管理数据库,启动时将会按照distschedule-dao\src\main\resources\db\migration里V1,V2 sql的顺序初始化数据库
+
+#### 步骤5. 修改配置文件
+
+修改配置文件distschedule-core\src\main\resources\application-local.properties
+
+```
+# mysql数据库配置,需要提前部署和创建distschedule的数据库
+# 数据库地址
+spring.datasource.url=datasource_url:jdbc:mysql://*****:3306/distschedule?useUnicode=true&characterEncoding=utf-8&useSSL=false&allowMultiQueries=true&serverTimezone=Asia/Shanghai 
+# 数据库账号
+spring.datasource.username=*****
+# 数据库密码
+spring.datasource.password=****
+
+# 华为IOT云配置,参考上面的介绍,申请IOT云的AK,SK,APPID
+cloud.iot.ak=*****
+cloud.iot.sk=*****
+cloud.iot.appId=*****
+
+# IOT云配置,参考上面的介绍,配置并获取华为IOT云的数据转发队列
+cloud.iot.amqp.accessKey= #接入凭证,添加转发目标时,点击接入凭证获取
+cloud.iot.amqp.password=  #接入凭证密码,添加转发目标时,点击接入凭证获取
+cloud.iot.amqp.queueStatusName=  #设备状态队列名称(不是规则名称)
+cloud.iot.amqp.queuePropertyName= #设备属性队列名称(不是规则名称)
+cloud.iot.amqp.queueMsgName= #设备消息队列名称(不是规则名称)
+cloud.iot.amqp.connectionUrl=amqps://*****.iot-amqps.cn-north-4.myhuaweicloud.com:5671?amqp.vhost=default&amqp.idleTimeout=8000&amqp.saslMechanisms=PLAIN #AMQP连接地址,点击“总览--》平台接入地址”获取
+
+
+
+# rabbitmq消息队列,用于手机监听告警信息
+spring.rabbitmq.host=*****    # rabbitmq地址
+spring.rabbitmq.port=*****    # rabbitmq端口
+spring.rabbitmq.username=*****  # rabbitmq用户名
+spring.rabbitmq.password=*****   # rabbitmq密码
+spring.rabbitmq.exchange.deviceproperty=deviceproperty_exchange # 消息exchange,可不改。如修改的话,FA侧也需要修改
+```
+
+#### 步骤6.编译
+
+编译环境:
+
+1. jdk8
+2. maven 3.6.3
+
+进入目录,编译springboot jar包
+
+```bash
+cd knowledge_demo_smart_home/Server
+mvn clean package -Dmaven.test.skip=true  
+```
+
+可执行的jar包在knowledge_demo_smart_home/Server/distschedule-core/target/distschedule-core-1.0.0-SNAPSHOT.jar`
+
+#### 步骤7. 服务启动
+
+指定profile运行,建议使用local,才会加载前面的application-local.properties的配置文件。如果profile为xxx,则会加载application-xxx.properties的配置文件
+
+也可通过环境变量指定所激活local profile
+```bash
+export SPRING_PROFILES_ACTIVE=local # 激活local
+```
+```
+java -jar /opt/distschedule-core-1.0.0-SNAPSHOT.jar --spring.profiles.active=local
+```
+
+默认端口8080,启动后如后台无异常,可用浏览器访问http://127.0.0.1:8080/distschedule-api/swagger-ui/index.html ,验证服务是否已正常启动。 IP和端口需要替换成自己的。
+
+#### 步骤8:配置服务开机自启动
+
+ubuntu18.04服务自启动,参考文档:https://www.cnblogs.com/airdot/p/9688530.html
+
+其中:/etc/rc.local文件
+
+```
+#!/bin/bash
+ 
+nohup java -jar /opt/distschedule-core-1.0.0-SNAPSHOT.jar --spring.profiles.active=local> /clouddragonData/log/distschedule/server.log 2>&1 &
+```
+
+#### 步骤9:手机FA侧,配置使用自己的数字管家服务和rabbitmq服务
+
+参考[FA侧说明](https://gitee.com/openharmony-sig/knowledge_demo_smart_home/blob/master/FA/DistSchedule/docs/%E6%95%B0%E5%AD%97%E7%AE%A1%E5%AE%B6%E8%AE%BE%E5%A4%87%E6%8E%A5%E5%85%A5%E6%8C%87%E5%8D%97.md#4-%E6%8E%A5%E5%8F%A3%E5%AF%B9%E6%8E%A5)
+
+
+## 工程结构
+```
+- distschedule--core       # 入口模块,包含main和controller
+- distschedule-service     # 服务模块
+- distschedule-dao         # 数据库模块
+```
+
+## 接口描述
+服务集成Swagger3.0,部署以后使用swagger.html查看接口,即http://127.0.0.1:8080/distschedule-api/swagger-ui/index.html
+包括如下接口:
+
+- 家庭接口
+- 日程接口
+- 用户接口
+- 设备接口
+- 设备类型接口
+![interface](design/interface1.png)
+
+## 数据库设计
+![datbase](design/database.png)
+使用flyway自动创建数据库,数据库表放在:
+distschedule-dao\src\main\resources\db\migration 目录下
+
+## 使用Docker容器部署
+详见Dockerfile,docker-compose.yml
+1. 参考前面描述,修改配置文件application-local.properties。
+2. 插入新的设备类型,V2_insertProductType.sql
+3. 编译,构建容器镜像,使用docker-compose启动,即可同时启动mysql,rabbitmq以及数字管家服务, 命令如下
+```
+mvn clean package
+docker build -t distschedule .
+docker-compose up
+```
+
+## 开发者分享
+请点击[这里](share.md)

BIN
Server/design/component.png


BIN
Server/design/database.png


BIN
Server/design/interface1.png


BIN
Server/design/iot_data_queue.png


BIN
Server/design/iot_data_transfer.png


+ 77 - 0
Server/distschedule-core/pom.xml

@@ -0,0 +1,77 @@
+<?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">
+    <parent>
+        <artifactId>distschedule</artifactId>
+        <groupId>com.example.distschedule</groupId>
+        <version>1.0.0-SNAPSHOT</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>distschedule-core</artifactId>
+    <description>核心模块,该模块为工程的入口和controller的定义</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-web</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-aop</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-amqp</artifactId>
+        </dependency>
+        <!--
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-security</artifactId>
+        </dependency>-->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-validation</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>io.springfox</groupId>
+            <artifactId>springfox-boot-starter</artifactId>
+            <version>${springfox-swagger.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>com.example.distschedule</groupId>
+            <artifactId>distschedule-service</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.qpid</groupId>
+            <artifactId>qpid-jms-client</artifactId>
+            <version>${qpid.jms.client}</version>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <resources>
+            <resource>
+                <directory>src/main/resources</directory>
+                <filtering>true</filtering>
+            </resource>
+        </resources>
+        <plugins>
+            <plugin>
+                <groupId>org.springframework.boot</groupId>
+                <artifactId>spring-boot-maven-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-resources-plugin</artifactId>
+                <configuration>
+                    <delimiters>
+                        <delimiter>@</delimiter>
+                    </delimiters>
+                    <useDefaultDelimiters>false</useDefaultDelimiters>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+</project>

+ 39 - 0
Server/distschedule-core/src/main/java/com/example/distschedule/DistScheduleApplication.java

@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2021 Huawei Device Co., Ltd.
+ * Licensed under the Apache License,Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ 
+package com.example.distschedule;
+
+
+import org.mybatis.spring.annotation.MapperScan;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.context.annotation.EnableAspectJAutoProxy;
+import org.springframework.scheduling.annotation.EnableScheduling;
+import springfox.documentation.oas.annotations.EnableOpenApi;
+import springfox.documentation.swagger2.annotations.EnableSwagger2;
+
+@SpringBootApplication
+//@EnableSwagger2
+@EnableOpenApi
+//@EnableRetry
+//@MappedTypes({ProjectHistoryMsgMapper.class})
+@MapperScan({"com.example.distschedule.dao.mapper"})
+//@EnableScheduling
+//@EnableAspectJAutoProxy
+public class DistScheduleApplication {
+    public static void main(String[] args) {
+        SpringApplication.run(DistScheduleApplication.class, args);
+    }
+}

+ 49 - 0
Server/distschedule-core/src/main/java/com/example/distschedule/config/Swagger3Config.java

@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2021 Huawei Device Co., Ltd.
+ * Licensed under the Apache License,Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.distschedule.config;
+
+import io.swagger.annotations.ApiOperation;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import springfox.documentation.builders.ApiInfoBuilder;
+import springfox.documentation.builders.PathSelectors;
+import springfox.documentation.builders.RequestHandlerSelectors;
+import springfox.documentation.service.ApiInfo;
+import springfox.documentation.service.Contact;
+import springfox.documentation.spi.DocumentationType;
+import springfox.documentation.spring.web.plugins.Docket;
+
+@Configuration
+public class Swagger3Config {
+    @Bean
+    public Docket createRestApi() {
+        return new Docket(DocumentationType.OAS_30)
+                .apiInfo(apiInfo())
+                .select()
+                .apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class))
+                .paths(PathSelectors.any())
+                .build();
+    }
+
+    private ApiInfo apiInfo() {
+        return new ApiInfoBuilder()
+                .title("超级终端接口文档")
+                .description("更多请咨询服务开发者madixin。")
+                .contact(new Contact("Madixin", "https://developer.harmonyos.com/", "madixin@huawei.com"))
+                .version("1.0")
+                .build();
+    }
+}

+ 219 - 0
Server/distschedule-core/src/main/java/com/example/distschedule/controller/DeviceController.java

@@ -0,0 +1,219 @@
+/*
+ * Copyright (c) 2021 Huawei Device Co., Ltd.
+ * Licensed under the Apache License,Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ 
+package com.example.distschedule.controller;
+
+import com.example.distschedule.dto.*;
+import com.example.distschedule.error.ErrorCode;
+import com.example.distschedule.exception.DistscheduleDeviceException;
+import com.example.distschedule.service.DeviceService;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiImplicitParam;
+import io.swagger.annotations.ApiImplicitParams;
+import io.swagger.annotations.ApiOperation;
+import org.apache.commons.lang3.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import javax.validation.Valid;
+import javax.validation.constraints.Size;
+import java.util.List;
+import java.util.Optional;
+
+@Api(tags = "设备接口")
+@RestController
+@Validated
+@RequestMapping(value = "/device")
+public class DeviceController {
+    private static final Logger LOGGER = LoggerFactory.getLogger(DeviceController.class);
+    @Autowired
+    private DeviceService deviceService;
+
+    @ApiOperation(value = "根据用户id,查询用户的设备列表")
+    @ApiImplicitParams({
+    })
+    @GetMapping("/userId/{userId}")
+    public ResponseResult<List<DeviceDto>> getUserDevices(@PathVariable("userId") String userId) {
+        LOGGER.info("getUserDevices:userId=" + userId);
+        return ResponseResult.success(deviceService.getDevicesByUserId(userId));
+    }
+
+    @ApiOperation(value = "根据设备id,查询设备")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "userId", value = "user id", required = true, paramType = "header", dataTypeClass = String.class),
+    })
+    @GetMapping("/{deviceId}")
+    public ResponseResult<DeviceDto> getDevicesById(@PathVariable("deviceId") String deviceId,
+                                                    @Size(min = 36, max = 36, message = "illegal user id format") @RequestHeader(name = "userId") String userId) {
+        LOGGER.info("getDevicesById:userId=" + userId + ",deviceId=" + deviceId);
+        Optional<DeviceDto> deviceDtoOptional = deviceService.getDeviceById(deviceId);
+        if (deviceDtoOptional.isPresent()) {
+            return ResponseResult.success(deviceDtoOptional.get());
+        }
+        return ResponseResult.fail(ErrorCode.DEVICE_NOT_FOUND);
+    }
+
+    @ApiOperation(value = "创建设备")
+    @PostMapping
+    public ResponseResult<DeviceDto> createDevice(@Valid @RequestBody CreateDeviceDto createDeviceDto) {
+        LOGGER.info("createDevice:createDeviceDto=" + createDeviceDto);
+        String deviceId = null;
+        try {
+            deviceId = deviceService.saveDevice(createDeviceDto);
+        } catch (DistscheduleDeviceException e) {
+            LOGGER.error(e.getMessage(), e);
+            if (StringUtils.isBlank(e.getMessage())) {
+                return ResponseResult.fail(e.getErrorCode());
+            }
+            return ResponseResult.fail(e.getMessage(), e.getErrorCode());
+        }
+
+        Optional<DeviceDto> newDeviceOptional = deviceService.getDeviceById(deviceId);
+        if (newDeviceOptional.isPresent()) {
+            return ResponseResult.success(newDeviceOptional.get());
+        }
+
+        return ResponseResult.fail(ErrorCode.ILLEGAL_STATE);
+    }
+
+    @ApiOperation(value = "删除设备")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "userId", value = "device owner id", required = true, paramType = "header", dataTypeClass = String.class),
+    })
+    @DeleteMapping("/{deviceId}")
+    public ResponseResult<String> deleteDeviceById(@PathVariable("deviceId") String deviceId,
+                                                   @Size(min = 36, max = 36, message = "illegal user id format") @RequestHeader(name = "userId") String userId) {
+        LOGGER.info("deleteDeviceById:userId=" + userId + ",deviceId=" + deviceId);
+        int res = 0;
+        try {
+            res = deviceService.deleteDevice(userId, deviceId);
+        } catch (DistscheduleDeviceException e) {
+            if (StringUtils.isBlank(e.getMessage())) {
+                return ResponseResult.fail(e.getErrorCode());
+            }
+            return ResponseResult.fail(e.getMessage(), e.getErrorCode());
+        }
+
+        if (res == 0) {
+            return ResponseResult.fail(ErrorCode.DEVICE_DELETE_FAIL);
+        }
+        return ResponseResult.success("");
+    }
+
+
+    @ApiOperation(value = "更新设备")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "userId", value = "device owner id", required = true, paramType = "header", dataTypeClass = String.class),
+    })
+    @PutMapping("/{deviceId}")
+    public ResponseResult<String> updateDeviceById(
+            @PathVariable("deviceId") String deviceId,
+            @Valid @RequestBody UpdateDeviceDto updateDeviceDto,
+            @Size(min = 36, max = 36, message = "illegal user id format") @RequestHeader(name = "userId") String userId) {
+        LOGGER.info("updateDeviceById:userId=" + userId + ",deviceId=" + deviceId + ",updateDeviceDto=" + updateDeviceDto);
+        int res = 0;
+        try {
+            res = deviceService.updateDeviceById(userId, deviceId, updateDeviceDto);
+        } catch (DistscheduleDeviceException e) {
+            LOGGER.error(e.getMessage(), e);
+            return ResponseResult.fail(e.getErrorCode());
+        }
+        if (res == 0) {
+            return ResponseResult.fail(ErrorCode.DEVICE_UPDATE_FAIL);
+        } else {
+            return ResponseResult.success("success");
+        }
+    }
+
+    @ApiOperation(value = "共享设备")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "userId", value = "user owner id", required = true, paramType = "header", dataTypeClass = String.class),
+    })
+    @PostMapping("/share")
+    public ResponseResult<String> shareDevice(
+            @Size(min = 36, max = 36, message = "illegal user id format") @RequestHeader String userId,
+            @Size(min = 36, max = 36, message = "illegal shareUserId id format") @RequestParam("shareUserId") String shareUserId,
+            @RequestParam("deviceId") String deviceId) {
+        LOGGER.info("shareDevice:userId=" + userId + ",deviceId=" + deviceId + ",shareUserId=" + shareUserId);
+        int res = 0;
+        try {
+            res = deviceService.shareDevice(userId, shareUserId, deviceId);
+        } catch (DistscheduleDeviceException e) {
+            LOGGER.error(e.getMessage(), e);
+            return ResponseResult.fail(e.getErrorCode());
+        }
+        if (res == 0) {
+            ResponseResult.fail(ErrorCode.DEVICE_SHARE_FAIL);
+        }
+        return ResponseResult.success("");
+    }
+
+    @ApiOperation(value = "取消共享设备")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "userId", value = "user owner id", required = true, paramType = "header", dataTypeClass = String.class),
+    })
+    @PostMapping("/unshare")
+    public ResponseResult<String> unshareDevice(
+            @Size(min = 36, max = 36, message = "illegal user id format") @RequestHeader String userId,
+            @Size(min = 36, max = 36, message = "illegal shareUserId id format") @RequestParam("shareUserId") String shareUserId,
+            @RequestParam("deviceId") String deviceId) {
+        LOGGER.info("unshareDevice:userId=" + userId + ",deviceId=" + deviceId + ",shareUserId=" + shareUserId);
+        int res = 0;
+        try {
+            res = deviceService.unshareDevice(userId, shareUserId, deviceId);
+        } catch (DistscheduleDeviceException e) {
+            LOGGER.error(e.getMessage(), e);
+            return ResponseResult.fail(e.getErrorCode());
+        }
+        if (res == 0) {
+            ResponseResult.fail(ErrorCode.DEVICE_UNSHARE_FAIL);
+        }
+        return ResponseResult.success("");
+    }
+
+    @ApiOperation(value = "给设备发送命令")
+    @PostMapping("/{deviceId}/sendCommand")
+    public ResponseResult<String> sendDeviceCommand(
+            @PathVariable("deviceId") String deviceId,
+            @Valid @RequestBody DeviceCommandDto deviceCommandDto) {
+        LOGGER.info("sendDeviceCommand:deviceId=" + deviceId + ",deviceCommandDto=" + deviceCommandDto);
+        //1.记录发送日志
+
+        //2.向IOT云发送命令
+        try {
+            deviceService.sendCommand(deviceId, deviceCommandDto);
+        } catch (DistscheduleDeviceException e) {
+            if (StringUtils.isBlank(e.getMessage())) {
+                return ResponseResult.fail(e.getErrorCode());
+            }
+            return ResponseResult.fail(e.getMessage(), e.getErrorCode());
+        }
+        return ResponseResult.success("");
+    }
+
+    @ApiOperation(value = "根据设备id,查询已分享的用户")
+    @ApiImplicitParams({
+    })
+    @GetMapping("/deviceId/{deviceId}/userShares")
+    public ResponseResult<List<UserDeviceDto>> getShareUsersByDeviceId(@PathVariable("deviceId") String deviceId) {
+        LOGGER.info("getShareUsersByDeviceId:deviceId=" + deviceId);
+
+        List<UserDeviceDto> userDeviceDtos = deviceService.getShareUsersByDeviceId(deviceId);
+        return ResponseResult.success(userDeviceDtos);
+    }
+}

+ 69 - 0
Server/distschedule-core/src/main/java/com/example/distschedule/controller/DeviceTypeController.java

@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2021 Huawei Device Co., Ltd.
+ * Licensed under the Apache License,Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ 
+package com.example.distschedule.controller;
+
+
+import com.example.distschedule.dto.DeviceTypeDto;
+import com.example.distschedule.dto.ResponseResult;
+import com.example.distschedule.error.ErrorCode;
+import com.example.distschedule.service.DeviceTypeService;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiImplicitParams;
+import io.swagger.annotations.ApiOperation;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.List;
+import java.util.Optional;
+
+@Api(tags = "设备类型接口")
+@RestController
+@Validated
+@RequestMapping(value = "/devicetype")
+public class DeviceTypeController {
+    private static final Logger LOGGER = LoggerFactory.getLogger(DeviceTypeController.class);
+    @Autowired
+    private DeviceTypeService deviceTypeService;
+
+    @ApiOperation(value = "查询设备类型")
+    @ApiImplicitParams({
+    })
+    @GetMapping
+    public ResponseResult<List<DeviceTypeDto>> getDeviceTypes() {
+        LOGGER.info("getDeviceTypes:");
+        return ResponseResult.success(deviceTypeService.getDeviceTypes());
+    }
+
+    @ApiOperation(value = "根据ProductID查询设备类型")
+    @ApiImplicitParams({
+    })
+    @GetMapping("/productId/{productId}")
+    public ResponseResult<DeviceTypeDto> getDeviceTypesByProductId(@PathVariable(name = "productId") String productId) {
+        LOGGER.info("getDeviceTypesByProductId:productId=" + productId);
+
+        Optional<DeviceTypeDto> deviceTypeDto =deviceTypeService.getDeviceTypesByProductId(productId);
+        if (deviceTypeDto.isPresent()){
+            return ResponseResult.success(deviceTypeDto.get());
+        }
+        return ResponseResult.fail(ErrorCode.DEVICE_TYPE_NOT_EXIST_FAIL);
+    }
+}

+ 96 - 0
Server/distschedule-core/src/main/java/com/example/distschedule/controller/DistscheduleGlobalExceptionHandler.java

@@ -0,0 +1,96 @@
+/*
+ * Copyright (c) 2021 Huawei Device Co., Ltd.
+ * Licensed under the Apache License,Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ 
+package com.example.distschedule.controller;
+
+import com.example.distschedule.dto.ResponseResult;
+import com.example.distschedule.error.ErrorCode;
+import com.example.distschedule.exception.DistschedulePermissionException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.context.support.DefaultMessageSourceResolvable;
+import org.springframework.web.bind.MethodArgumentNotValidException;
+import org.springframework.web.bind.annotation.ControllerAdvice;
+import org.springframework.web.bind.annotation.ExceptionHandler;
+import org.springframework.web.bind.annotation.ResponseBody;
+import org.springframework.web.context.request.WebRequest;
+
+import javax.validation.ConstraintViolation;
+import javax.validation.ConstraintViolationException;
+import java.sql.SQLException;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+@ControllerAdvice
+public class DistscheduleGlobalExceptionHandler {
+    private static final Logger LOGGER = LoggerFactory.getLogger(DistscheduleGlobalExceptionHandler.class);
+
+    /**
+     * 处理校验参数不合法的异常.
+     */
+    @ExceptionHandler(value = {ConstraintViolationException.class})
+    @ResponseBody
+    public ResponseResult<String> handleConstraintViolation(Exception ex, WebRequest request) {
+        ConstraintViolationException violationException = (ConstraintViolationException) ex;
+        Set<ConstraintViolation<?>> violations = violationException.getConstraintViolations();
+        String errorMessage = violations.stream()
+                .map(ConstraintViolation::getMessage)
+                .collect(Collectors.joining(", "));
+        return ResponseResult.fail(errorMessage, ErrorCode.ILLEGAL_PARAM);
+    }
+
+    @ExceptionHandler(value = {MethodArgumentNotValidException.class})
+    @ResponseBody
+    protected ResponseResult<String> handleMethodArgumentNotValid(Exception ex, WebRequest request) {
+        MethodArgumentNotValidException methodArgumentNotValidException = (MethodArgumentNotValidException) ex;
+        String errorMessage = methodArgumentNotValidException.getBindingResult()
+                .getAllErrors().stream()
+                .map(DefaultMessageSourceResolvable::getDefaultMessage)
+                .collect(Collectors.joining(", "));
+        return ResponseResult.fail(errorMessage, ErrorCode.ILLEGAL_PARAM);
+    }
+
+    @ExceptionHandler(value = {IllegalArgumentException.class, IllegalStateException.class,
+            UnsupportedOperationException.class})
+    @ResponseBody
+    public ResponseResult<String> handleCommonException(Exception ex, WebRequest request) {
+        LOGGER.error("{}", ex.getMessage());
+        if (ex instanceof IllegalArgumentException) {
+            return ResponseResult.fail("Illegal argument, " + ex.getMessage(), ErrorCode.ILLEGAL_PARAM);
+        } else if (ex instanceof IllegalStateException) {
+            return ResponseResult.fail("Illegal state, " + ex.getMessage(), ErrorCode.ILLEGAL_STATE);
+        } else if (ex instanceof UnsupportedOperationException) {
+            return ResponseResult.fail("Unsupported operation, " + ex.getMessage(), ErrorCode.UNSUPPORTED_OPERATION);
+        }
+        return ResponseResult.fail("internal error");
+    }
+
+    @ExceptionHandler(value = {SQLException.class})
+    @ResponseBody
+    public ResponseResult<String> handleSQLException(Exception ex, WebRequest request) {
+        LOGGER.error("{}", ex.getMessage());
+        return ResponseResult.fail("internal error", ErrorCode.DATABASE);
+    }
+
+    @ExceptionHandler(value = {DistschedulePermissionException.class})
+    @ResponseBody
+    public ResponseResult<String> handleDistschedulePermissionException(Exception ex, WebRequest request) {
+        LOGGER.error("{}", ex.getMessage());
+        return ResponseResult.fail(ErrorCode.ILLEGAL_PERMISSION);
+    }
+
+
+
+}

+ 212 - 0
Server/distschedule-core/src/main/java/com/example/distschedule/controller/FamilyController.java

@@ -0,0 +1,212 @@
+/*
+ * Copyright (c) 2021 Huawei Device Co., Ltd.
+ * Licensed under the Apache License,Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ 
+package com.example.distschedule.controller;
+
+
+import com.example.distschedule.dto.*;
+import com.example.distschedule.error.ErrorCode;
+import com.example.distschedule.exception.DistscheduleFamilyException;
+import com.example.distschedule.exception.DistschedulePermissionException;
+import com.example.distschedule.exception.DistscheduleUserException;
+import com.example.distschedule.service.FamilyService;
+import com.example.distschedule.service.UserService;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiImplicitParam;
+import io.swagger.annotations.ApiImplicitParams;
+import io.swagger.annotations.ApiOperation;
+import org.apache.commons.lang3.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import javax.validation.Valid;
+import javax.validation.constraints.NotNull;
+import javax.validation.constraints.Size;
+
+import java.util.List;
+import java.util.Optional;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+@Api(tags = "家庭接口")
+@RestController
+@Validated
+@RequestMapping(value = "/family")
+public class FamilyController {
+    private static final Logger LOGGER = LoggerFactory.getLogger(FamilyController.class);
+    @Autowired
+    private FamilyService familyService;
+
+    @Autowired
+    private UserService userService;
+
+    @ApiOperation(value = "根据用户id查询家庭信息")
+    @GetMapping("/userId/{userId}")
+    public ResponseResult<List<FamilyDto>> getFamiliesByUserId(@Size(min = 36, max = 36, message = "illegal user id format") @PathVariable("userId") String userId) {
+        LOGGER.info("getFamiliesByUserId:userId=" + userId);
+        return ResponseResult.success(familyService.getFamiliesByUserId(userId));
+    }
+
+    @ApiOperation(value = "创建家庭")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "userId", value = "family owner id", required = true, paramType = "header", dataTypeClass = String.class),
+    })
+    @PostMapping
+    public ResponseResult<FamilyDto> createFamily(@Valid @RequestBody FamilyCreateDto familyCreateDto,
+                                                  @Size(min = 36, max = 36, message = "illegal user id format") @RequestHeader(name = "userId") String userId) {
+        LOGGER.info("createFamily:userId=" + userId + ",familyCreateDto=" + familyCreateDto.toString());
+        String familyId = null;
+        try {
+            familyId = familyService.saveFamily(userId, familyCreateDto);
+        } catch (DistscheduleUserException e) {
+            LOGGER.error(e.getMessage(), e);
+            return ResponseResult.fail(e.getErrorCode());
+        }
+
+        Optional<FamilyDto> newFamilyOptional = familyService.getFamilyById(familyId);
+        if (newFamilyOptional.isPresent()) {
+            return ResponseResult.success(newFamilyOptional.get());
+        }
+
+        return ResponseResult.fail(ErrorCode.ILLEGAL_STATE);
+    }
+
+    @ApiOperation(value = "删除家庭")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "userId", value = "family owner id", required = true, paramType = "header", dataTypeClass = String.class),
+    })
+    @DeleteMapping("/{familyId}")
+    public ResponseResult<String> deleteFamilyById(
+            @Size(min = 36, max = 36, message = "illegal family id format") @PathVariable("familyId") String familyId,
+            @Size(min = 36, max = 36, message = "illegal user id format") @RequestHeader(name = "userId") String userId) {
+        LOGGER.info("deleteFamilyById:userId=" + userId + ",familyId=" + familyId);
+        int res = 0;
+        try {
+            res = familyService.deleteFamily(userId, familyId);
+        } catch (DistscheduleFamilyException e) {
+            LOGGER.error(e.getMessage(), e);
+            return ResponseResult.fail(e.getErrorCode());
+        }
+
+        if (res == 0) {
+            return ResponseResult.fail(ErrorCode.FAMILY_DELETE_FAIL);
+        }
+
+        return ResponseResult.success("");
+    }
+
+
+    @ApiOperation(value = "更新家庭")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "userId", value = "family owner id", required = true, paramType = "header", dataTypeClass = String.class),
+    })
+    @PutMapping("/{familyId}")
+    public ResponseResult<String> updateFamilyById(
+            @Size(min = 36, max = 36, message = "illegal family id format")
+            @PathVariable("familyId") String familyId,
+            @Size(min = 36, max = 36, message = "illegal user id format") @RequestHeader(name = "userId") String userId,
+            @Valid @RequestBody UpdateFamilyDto updateFamilyDto) throws DistschedulePermissionException {
+        LOGGER.info("updateFamilyById:userId=" + userId + ",familyId=" + familyId + ",updateFamilyDto=" + updateFamilyDto);
+        int res = 0;
+        try {
+            res = familyService.updateFamily(userId, familyId, updateFamilyDto);
+        } catch (DistscheduleFamilyException e) {
+            LOGGER.error(e.getMessage(), e);
+            return ResponseResult.fail(e.getErrorCode());
+        }
+
+        if (res == 0) {
+            return ResponseResult.fail(ErrorCode.FAMILY_UPDATE_FAIL);
+        } else {
+            return ResponseResult.success("success");
+        }
+    }
+
+    @ApiOperation(value = "创建家庭成员")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "userId", value = "family owner id", required = true, paramType = "header", dataTypeClass = String.class),
+    })
+    @PostMapping("/{familyId}/member")
+    public ResponseResult<String> createFamilyMember(@RequestParam(required = true) String phone,
+                                                     @Size(min = 36, max = 36, message = "illegal family id format") @PathVariable(name = "familyId") String familyId,
+                                                     @Size(min = 36, max = 36, message = "illegal user id format") @RequestHeader(name = "userId") String userId,
+                                                     @Size(min = 1, max = 32, message = "illegal role size, role size should less than 32")
+                                                     @NotNull(message = "role should not empty") @RequestParam(required = true) String role) {
+        LOGGER.info("createFamilyMember:userId=" + userId + ",familyId=" + familyId + ",role=" + role);
+        //手机号码正则表达式
+        String str = "^[1][3,5,7,8][0-9]\\d{8}$";
+        Pattern patternPhtone = Pattern.compile(str);
+        Matcher matcherPhone = patternPhtone.matcher(phone);
+
+        if (StringUtils.isBlank(phone) || !matcherPhone.matches()) {
+            return ResponseResult.fail(ErrorCode.ILLEGAL_PARAM);
+        }
+        //保存关系
+        int res = 0;
+        try {
+            res = familyService.saveFamilyMember(userId, familyId, phone, role);
+        } catch (DistscheduleUserException e) {
+            LOGGER.error(e.getMessage(), e);
+            return ResponseResult.fail(e.getErrorCode());
+        }
+
+        if (res == 0) {
+            return ResponseResult.fail(ErrorCode.FAMILY_MEMBER_SAVE_FAIL);
+        } else {
+            return ResponseResult.success("success");
+        }
+    }
+
+    @ApiOperation(value = "删除家庭成员")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "userId", value = "family owner id", required = true, paramType = "header", dataTypeClass = String.class),
+    })
+    @DeleteMapping("/{familyId}/member")
+    public ResponseResult<String> deleteFamilyMember(
+            @Size(min = 36, max = 36, message = "illegal family id format") @PathVariable("familyId") String familyId,
+            @Size(min = 36, max = 36, message = "illegal user id format") @RequestHeader(name = "userId") String userId,
+            @Size(min = 36, max = 36, message = "illegal familyMember id format") @RequestParam(name = "familyMemberId") String familyMemberId) {
+        LOGGER.info("deleteFamilyMember:userId=" + userId + ",familyId=" + familyId + ",familyMemberId=" + familyMemberId);
+
+        int res = 0;
+        try {
+            res = familyService.deleteFamilyMember(userId, familyMemberId, familyId);
+        } catch (DistscheduleUserException e) {
+            LOGGER.error(e.getMessage(), e);
+            return ResponseResult.fail(e.getErrorCode());
+        }
+
+        if (res == 0) {
+            return ResponseResult.fail(ErrorCode.FAMILY_MEMBER_DELETE_FAIL);
+        }
+
+        return ResponseResult.success("");
+    }
+
+    @ApiOperation(value = "查询家庭成员信息")
+    @GetMapping("/{familyId}/member")
+    public ResponseResult<List<UserFamilyDto>> getFamilyMembersById(@Size(min = 36, max = 36, message = "illegal family id format") @PathVariable("familyId") String familyId) {
+        LOGGER.info("getFamilyMembersById:familyId=" + familyId);
+        try {
+            return ResponseResult.success(familyService.getFamilyMembersById(familyId));
+        } catch (DistscheduleUserException e) {
+            LOGGER.error(e.getMessage(), e);
+            return ResponseResult.fail(e.getErrorCode());
+        }
+    }
+}

+ 181 - 0
Server/distschedule-core/src/main/java/com/example/distschedule/controller/ScheduleController.java

@@ -0,0 +1,181 @@
+/*
+ * Copyright (c) 2021 Huawei Device Co., Ltd.
+ * Licensed under the Apache License,Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ 
+package com.example.distschedule.controller;
+
+import com.example.distschedule.dto.*;
+import com.example.distschedule.error.ErrorCode;
+import com.example.distschedule.exception.DistscheduleScheduleException;
+import com.example.distschedule.service.ScheduleService;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiImplicitParam;
+import io.swagger.annotations.ApiImplicitParams;
+import io.swagger.annotations.ApiOperation;
+import org.apache.commons.lang3.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import javax.validation.Valid;
+import javax.validation.constraints.Size;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.*;
+
+@Api(tags = "日程接口")
+@RestController
+@Validated
+@RequestMapping(value = "/schedule")
+public class ScheduleController {
+    private static final Logger LOGGER = LoggerFactory.getLogger(ScheduleController.class);
+    @Autowired
+    private ScheduleService scheduleService;
+
+    @ApiOperation(value = "创建日程")
+    @PostMapping
+    public ResponseResult<ScheduleDto> createSchedule(@Valid @RequestBody CreateScheduleDto createScheduleDto) {
+        LOGGER.info("createSchedule:createScheduleDto=" + createScheduleDto);
+
+        String scheduleId = null;
+        try {
+            scheduleId = scheduleService.saveSchedule(createScheduleDto);
+        } catch (DistscheduleScheduleException e) {
+            if (StringUtils.isBlank(e.getMessage())) {
+                return ResponseResult.fail(e.getErrorCode());
+            }
+            return ResponseResult.fail(e.getMessage(), e.getErrorCode());
+        }
+
+
+        Optional<ScheduleDto> newScheduleOptional = scheduleService.getScheduleById(scheduleId);
+        if (newScheduleOptional.isPresent()) {
+            return ResponseResult.success(newScheduleOptional.get());
+        }
+
+        return ResponseResult.fail(ErrorCode.ILLEGAL_STATE);
+    }
+
+    @ApiOperation(value = "删除日程")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "userId", value = "schedule creator id", required = true, paramType = "header", dataTypeClass = String.class),
+    })
+    @DeleteMapping("/{scheduleId}")
+    public ResponseResult<String> deleteScheduleById(
+            @Size(min = 36, max = 36, message = "illegal schedule id format") @PathVariable("scheduleId")
+                    String scheduleId,
+            @Size(min = 36, max = 36, message = "illegal user id format") @RequestHeader(name = "userId") String userId) {
+        LOGGER.info("deleteScheduleById:userId=" + userId + ",scheduleId=" + scheduleId);
+        int res = 0;
+        try {
+            res = scheduleService.deleteSchedule(userId, scheduleId);
+        } catch (DistscheduleScheduleException e) {
+            LOGGER.error(e.getMessage(), e);
+            return ResponseResult.fail(e.getErrorCode());
+        }
+
+        if (res == 0) {
+            return ResponseResult.fail(ErrorCode.SCHEDULE_DELETE_FAIL);
+        }
+        return ResponseResult.success("");
+    }
+
+
+    @ApiOperation(value = "更新日程")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "userId", value = "creator owner id", required = true, paramType = "header", dataTypeClass = String.class),
+    })
+    @PutMapping("/{scheduleId}")
+    public ResponseResult<String> updateScheduleById(
+            @Size(min = 36, max = 36, message = "illegal schedule id format") @PathVariable("scheduleId") String scheduleId,
+            @Size(min = 36, max = 36, message = "illegal user id format") @RequestHeader(name = "userId") String userId,
+            @Valid @RequestBody CreateScheduleDto updateScheduleDto) {
+        LOGGER.info("updateScheduleById:userId=" + userId + ",scheduleId=" + scheduleId + ",updateScheduleDto=" + updateScheduleDto);
+        int res = 0;
+        try {
+            res = scheduleService.updateSchedule(userId, scheduleId, updateScheduleDto);
+        } catch (DistscheduleScheduleException e) {
+            LOGGER.error(e.getMessage(), e);
+            return ResponseResult.fail(e.getErrorCode());
+        }
+
+        if (res == 0) {
+            return ResponseResult.fail(ErrorCode.SCHEDULE_UPDATE_FAIL);
+        } else {
+            return ResponseResult.success("success");
+        }
+    }
+
+    @ApiOperation(value = "根据id查询日程")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "userId", value = "creator owner id", required = true, paramType = "header", dataTypeClass = String.class),
+    })
+    @GetMapping("/{scheduleId}")
+    public ResponseResult<ScheduleDto> getScheduleById(@Size(min = 36, max = 36, message = "illegal schedule id format") @PathVariable("scheduleId") String scheduleId) {
+        LOGGER.info("getScheduleById:scheduleId=" + scheduleId);
+        Optional<ScheduleDto> scheduleDtoOptional = scheduleService.getScheduleById(scheduleId);
+        if (scheduleDtoOptional.isPresent()) {
+            return ResponseResult.success(scheduleDtoOptional.get());
+        }
+        return ResponseResult.fail(ErrorCode.SCHEDULE_NOT_FOUND);
+    }
+
+    @ApiOperation(value = "时间范围查询日程")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "userId", value = "creator owner id", required = true, paramType = "header", dataTypeClass = String.class),
+    })
+    @GetMapping("/query")
+    public ResponseResult<List<ScheduleDto>> querySchedules(@RequestParam("startTime") String startTimeStr, @RequestParam("endTime") String endTimeStr,
+                                                            @Size(min = 36, max = 36, message = "illegal user id format") @RequestHeader(name = "userId") String userId) {
+        LOGGER.info("querySchedules:userId=" + userId + ",startTimeStr=" + startTimeStr + ", endTimeStr = " + endTimeStr);
+        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+        Date startTime = null;
+        Date endTime = null;
+        try {
+            startTime = simpleDateFormat.parse(startTimeStr);
+            endTime = simpleDateFormat.parse(endTimeStr);
+        } catch (ParseException e) {
+            LOGGER.error(e.getMessage(), e);
+            return ResponseResult.fail(ErrorCode.ILLEGAL_PARAM);
+        }
+        return ResponseResult.success(scheduleService.queryScheduleByUser(userId, startTime, endTime));
+    }
+
+    @ApiOperation(value = "按名称搜索日程")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "userId", value = "creator user id", required = true, paramType = "header", dataTypeClass = String.class),
+    })
+    @GetMapping("/search")
+    public ResponseResult<List<ScheduleDto>> searchSchedulesByName(@RequestParam("name") String name,
+                                                                   @Size(min = 36, max = 36, message = "illegal user id format") @RequestHeader(name = "userId") String userId) {
+        LOGGER.info("searchSchedulesByName:userId=" + userId + ",name=" + name);
+        return ResponseResult.success(scheduleService.searchSchedulesByName(userId, name));
+    }
+
+    @ApiOperation(value = "删除当前用户所有日程(临时联调时接口)")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "userId", value = "schedule creator id", required = true, paramType = "header", dataTypeClass = String.class),
+    })
+    @DeleteMapping("/deleteUserSchedule")
+    public ResponseResult<String> deleteUserSchedule(
+            @Size(min = 36, max = 36, message = "illegal user id format") @RequestHeader(name = "userId") String userId) {
+        LOGGER.info("deleteUserSchedule:userId=" + userId);
+
+        scheduleService.deleteUserAllSchedules(userId);
+
+        return ResponseResult.success("");
+    }
+}

+ 106 - 0
Server/distschedule-core/src/main/java/com/example/distschedule/controller/UserController.java

@@ -0,0 +1,106 @@
+/*
+ * Copyright (c) 2021 Huawei Device Co., Ltd.
+ * Licensed under the Apache License,Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ 
+package com.example.distschedule.controller;
+
+
+import com.example.distschedule.dto.ResponseResult;
+import com.example.distschedule.dto.UserDeviceDto;
+import com.example.distschedule.dto.UserDto;
+import com.example.distschedule.error.ErrorCode;
+import com.example.distschedule.exception.DistscheduleUserException;
+import com.example.distschedule.service.UserService;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiImplicitParam;
+import io.swagger.annotations.ApiImplicitParams;
+import io.swagger.annotations.ApiOperation;
+import org.apache.commons.lang3.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import javax.validation.constraints.NotNull;
+import javax.validation.constraints.Size;
+import java.util.Optional;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+@Api(tags = "用户接口")
+@RestController
+@Validated
+@RequestMapping(value = "/user")
+public class UserController {
+    private static final Logger LOGGER = LoggerFactory.getLogger(UserController.class);
+
+    @Autowired
+    private UserService userService;
+
+    @ApiOperation(value = "根据手机获取用户信息")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "phone", value = "User telephone.Such as 13412345678", required = true, paramType = "path", dataTypeClass = String.class),
+    })
+    @GetMapping("/phone/{phone}")
+    public ResponseResult<UserDto> getUserByPhone(@PathVariable("phone") String phone) {
+        LOGGER.info("getUserByPhone:phone=" + phone);
+        //手机号码正则表达式
+        String str = "^[1][3,5,7,8][0-9]\\d{8}$";
+        Pattern patternPhtone = Pattern.compile(str);
+        Matcher matcherPhone = patternPhtone.matcher(phone);
+
+        if (StringUtils.isBlank(phone) || !matcherPhone.matches()) {
+            return ResponseResult.fail(ErrorCode.ILLEGAL_PARAM);
+        }
+
+        Optional<UserDto> userOptional = userService.getUserByPhone(phone, true);
+
+        if (userOptional.isPresent()) {
+            return ResponseResult.success(userOptional.get());
+        }
+
+        return ResponseResult.fail(ErrorCode.ILLEGAL_STATE);
+    }
+
+    @ApiOperation(value = "更新用户信息")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "userId", value = "User Id", required = true, paramType = "path", dataTypeClass = String.class),
+            @ApiImplicitParam(name = "name", value = "User name", required = true, paramType = "query", dataTypeClass = String.class),
+    })
+    @PutMapping("/{userId}")
+    public ResponseResult<String> updateUserById(@Size(min = 36, max = 36, message = "illegal user id format") @PathVariable("userId") String userId,
+                                                 @Size(min = 1, max = 32, message = "illegal name size, name size should less than 32")
+                                                 @NotNull(message = "name should not empty")
+                                                 @RequestParam(required = true) String name) {
+        LOGGER.info("updateUserById:userId=" + userId + ",name=" + name);
+
+        UserDto userDto = new UserDto();
+        userDto.setId(userId);
+        userDto.setName(name);
+        int res = 0;
+        try {
+            res = userService.updateUser(userDto);
+        } catch (DistscheduleUserException e) {
+            LOGGER.error(e.getMessage());
+            return ResponseResult.fail(e.getErrorCode());
+        }
+
+        if (res == 0) {
+            return ResponseResult.fail(ErrorCode.USER_UPDATE_FAIL);
+        } else {
+            return ResponseResult.success("success");
+        }
+    }
+}

+ 318 - 0
Server/distschedule-core/src/main/java/com/example/distschedule/listener/DeviceMsgListener.java

@@ -0,0 +1,318 @@
+/*
+ * Copyright (c) 2021 Huawei Device Co., Ltd.
+ * Licensed under the Apache License,Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ 
+package com.example.distschedule.listener;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONObject;
+import com.example.distschedule.exception.DistscheduleDeviceException;
+import com.example.distschedule.service.DeviceService;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.qpid.jms.JmsConnection;
+import org.apache.qpid.jms.JmsConnectionFactory;
+import org.apache.qpid.jms.JmsConnectionListener;
+import org.apache.qpid.jms.JmsQueue;
+import org.apache.qpid.jms.message.JmsInboundMessageDispatch;
+import org.apache.qpid.jms.transports.TransportOptions;
+import org.apache.qpid.jms.transports.TransportSupport;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.amqp.rabbit.core.RabbitTemplate;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.PostConstruct;
+import javax.jms.*;
+import javax.naming.Context;
+import javax.naming.InitialContext;
+import java.net.URI;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/**
+ * 接收设备告警消息
+ */
+@Component
+public class DeviceMsgListener {
+    protected static final Logger LOGGER = LoggerFactory.getLogger("DeviceMsgLog");
+
+    @Autowired
+    private RabbitTemplate rabbitTemplate;
+
+    @Autowired
+    private DeviceService deviceService;
+
+    @Value("${spring.rabbitmq.exchange.deviceproperty}")
+    private String devicePropertyExchange;
+
+    /**
+     * 异步线程池,参数可以根据业务特点作调整,也可以用其他异步方式来处理。
+     */
+    public final ExecutorService executorService = new ThreadPoolExecutor(Runtime.getRuntime().availableProcessors(),
+            Runtime.getRuntime().availableProcessors() * 2, 60,
+            TimeUnit.SECONDS, new LinkedBlockingQueue<>(5000));
+    /**
+     * 连接凭证接入键值
+     */
+    @Value("${cloud.iot.amqp.accessKey}")
+    private String accessKey = "";
+
+    /**
+     * 连接凭证接入码
+     */
+    @Value("${cloud.iot.amqp.password}")
+    private String password;
+
+    /**
+     * 按照qpid-jms的规范,组装连接URL。
+     */
+    @Value("${cloud.iot.amqp.connectionUrl}")
+    private String connectionUrl;
+
+
+    /**
+     * 队列名,可以使用默认队列 DefaultQueue
+     */
+    @Value("${cloud.iot.amqp.queueMsgName}")
+    private String queueName = "";
+
+    private Connection connection;
+    private Session session;
+    private MessageConsumer consumer;
+    private long lastReconnectTime = 0;
+    private AtomicBoolean isReconnecting = new AtomicBoolean(false);
+
+    @PostConstruct
+    private void initIOTAmqpListener() throws Exception {
+        LOGGER.info("Init " + this.getClass().getSimpleName());
+
+        createConsumer();
+
+        // 处理消息有两种方式
+        // 1,主动拉数据(推荐),参照receiveMessage()
+        new Thread(() -> {
+            receiveMessage();
+        }).start();
+        LOGGER.info("End Init " + this.getClass().getSimpleName());
+
+        // 2, 添加监听,参照consumer.setMessageListener(messageListener), 服务端主动推数据给客户端,但得考虑接受的数据速率是客户能力能够承受住的
+        // consumer.setMessageListener(messageListener);
+        // 防止主程序退出,这里休眠了60s,60s后程序会结束
+        // Thread.sleep( 60 * 1000L);
+        // shutdown();
+    }
+
+    private void createConsumer() throws Exception {
+        long timeStamp = System.currentTimeMillis();
+        //UserName组装方法,请参见文档:AMQP客户端接入说明。
+        String userName = "accessKey=" + accessKey + "|timestamp=" + timeStamp;
+        Hashtable<String, String> hashtable = new Hashtable<>();
+        hashtable.put("connectionfactory.HwConnectionURL", connectionUrl);
+        hashtable.put(Context.INITIAL_CONTEXT_FACTORY, "org.apache.qpid.jms.jndi.JmsInitialContextFactory");
+        Context context = new InitialContext(hashtable);
+        JmsConnectionFactory cf = (JmsConnectionFactory) context.lookup("HwConnectionURL");
+
+        //信任服务端
+        TransportOptions to = new TransportOptions();
+        to.setTrustAll(true);
+        cf.setSslContext(TransportSupport.createJdkSslContext(to));
+
+        // 创建连接
+        Connection connection = cf.createConnection(userName, password);
+        ((JmsConnection) connection).addConnectionListener(myJmsConnectionListener);
+
+        // 创建 Session
+        // Session.CLIENT_ACKNOWLEDGE: 收到消息后,需要手动调用message.acknowledge()。
+        // Session.AUTO_ACKNOWLEDGE: SDK自动ACK(推荐)。
+        session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
+        connection.start();
+        connection.setExceptionListener(exceptionListener);
+        // 创建 consumer
+        consumer = session.createConsumer(new JmsQueue(getQueueName()));
+    }
+
+    private void receiveMessage() {
+        while (true) {
+            try {
+                if (consumer != null) {
+                    Message message = consumer.receive();
+                    // 建议异步处理收到的消息,确保receiveMessage函数里没有耗时逻辑。
+                    executorService.execute(() -> processMessage(message));
+                } else {
+                    Thread.sleep(1000L);
+                }
+            } catch (Exception e) {
+                LOGGER.error("receiveMessage hand an exception: " + e.getMessage());
+                e.printStackTrace();
+            }
+        }
+    }
+
+    /**
+     * 客户端断开需要重连
+     */
+    private void reconnect() {
+        if (isReconnecting.compareAndSet(false, true)) {
+            while (true) {
+                try {
+                    // 防止重连次数太多,重连时间间隔15s
+                    if (System.currentTimeMillis() - lastReconnectTime < 15 * 1000L) {
+                        Thread.sleep(15 * 1000L);
+                    }
+                    shutdown();
+                    createConsumer();
+                    lastReconnectTime = System.currentTimeMillis();
+                    isReconnecting.set(false);
+                    break;
+                } catch (Exception e) {
+                    LOGGER.info("reconnect hand an exception: " + e.getMessage());
+                    e.printStackTrace();
+                }
+            }
+        }
+    }
+
+    private void shutdown() {
+        if (consumer != null) {
+            try {
+                consumer.close();
+            } catch (JMSException e) {
+                e.printStackTrace();
+            }
+        }
+        if (session != null) {
+            try {
+                session.close();
+            } catch (JMSException e) {
+                e.printStackTrace();
+            }
+        }
+        if (connection != null) {
+            try {
+                connection.setExceptionListener(null);
+                connection.close();
+            } catch (JMSException e) {
+                e.printStackTrace();
+            }
+        }
+    }
+
+    private ExceptionListener exceptionListener = new ExceptionListener() {
+        @Override
+        public void onException(JMSException e) {
+            e.printStackTrace();
+            LOGGER.info("connection has an exception:" + e);
+            // 链接发生异常,需要重连
+            reconnect();
+        }
+    };
+
+    private static JmsConnectionListener myJmsConnectionListener = new JmsConnectionListener() {
+        /**
+         * 连接成功建立。
+         */
+        @Override
+        public void onConnectionEstablished(URI remoteURI) {
+            LOGGER.info("onConnectionEstablished, remoteUri:" + remoteURI);
+        }
+
+        /**
+         * 尝试过最大重试次数之后,最终连接失败。
+         */
+        @Override
+        public void onConnectionFailure(Throwable error) {
+            LOGGER.info("onConnectionFailure, " + error.getMessage());
+        }
+
+        /**
+         * 连接中断。
+         */
+        @Override
+        public void onConnectionInterrupted(URI remoteURI) {
+            LOGGER.info("onConnectionInterrupted, remoteUri:" + remoteURI);
+        }
+
+        /**
+         * 连接中断后又自动重连上。
+         */
+        @Override
+        public void onConnectionRestored(URI remoteURI) {
+            LOGGER.info("onConnectionRestored, remoteUri:" + remoteURI);
+        }
+
+        @Override
+        public void onInboundMessage(JmsInboundMessageDispatch envelope) {
+            LOGGER.info("onInboundMessage, " + envelope);
+        }
+
+        @Override
+        public void onSessionClosed(Session session, Throwable cause) {
+            LOGGER.info("onSessionClosed, session=" + session + ", cause =" + cause);
+        }
+
+        @Override
+        public void onConsumerClosed(MessageConsumer consumer, Throwable cause) {
+            LOGGER.info("MessageConsumer, consumer=" + consumer + ", cause =" + cause);
+        }
+
+        @Override
+        public void onProducerClosed(MessageProducer producer, Throwable cause) {
+            LOGGER.info("MessageProducer, producer=" + producer + ", cause =" + cause);
+        }
+    };
+
+    protected String getQueueName() {
+        return this.queueName;
+    }
+
+    /**
+     * 在这里处理您收到消息后的具体业务逻辑。
+     */
+    protected void processMessage(Message message) {
+        try {
+            //{"resource":"device.status","event":"update","event_time":"20210901T012225Z","notify_data":{"header":{"app_id":"6989a30bea954354b662920f369f4503","device_id":"612779690ad1ed028666ec69_madixin","node_id":"madixin","product_id":"612779690ad1ed028666ec69","gateway_id":"612779690ad1ed028666ec69_madixin"},"body":{"status":"ONLINE","last_online_time":"20210901T012225Z"}}}
+            String body = message.getBody(String.class);
+            String content = new String(body);
+            LOGGER.info("DeviceMsgListener receive an message, the content is " + content);
+            JSONObject contentBody = JSON.parseObject(content);
+            //1.获取device_id和上报属性
+            String deviceId = contentBody.getJSONObject("notify_data").getJSONObject("header").getString("device_id");
+            String strbody = contentBody.getJSONObject("notify_data").getString("body");
+            if (StringUtils.isBlank(deviceId) || StringUtils.isBlank(strbody)) {
+                LOGGER.error("Invalid DeviceMsgListener deviceId= " + deviceId + ",strbody=" + strbody);
+                return;
+            }
+            //2.根据device_id查询user_id
+            try {
+                List<String> userIds = deviceService.getUsersByDeviceId(deviceId);
+                //3.使用user_id为routine_key路由,给rabbitmq发送消息
+                for (String userId : userIds) {
+                    rabbitTemplate.convertAndSend(this.devicePropertyExchange, userId, content);
+                }
+            } catch (DistscheduleDeviceException e) {
+                LOGGER.error("DeviceMsgListener error:" + e.getErrorCode().getDescription() + " , deviceId = " + deviceId);
+            }
+        } catch (Exception e) {
+            LOGGER.info("DeviceMsgListener processMessage occurs error: " + e.getMessage());
+            e.printStackTrace();
+        }
+    }
+}

+ 297 - 0
Server/distschedule-core/src/main/java/com/example/distschedule/listener/DevicePropertyListener.java

@@ -0,0 +1,297 @@
+/*
+ * Copyright (c) 2021 Huawei Device Co., Ltd.
+ * Licensed under the Apache License,Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ 
+package com.example.distschedule.listener;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONObject;
+import com.example.distschedule.service.DeviceService;
+import org.apache.qpid.jms.JmsConnection;
+import org.apache.qpid.jms.JmsConnectionFactory;
+import org.apache.qpid.jms.JmsConnectionListener;
+import org.apache.qpid.jms.JmsQueue;
+import org.apache.qpid.jms.message.JmsInboundMessageDispatch;
+import org.apache.qpid.jms.transports.TransportOptions;
+import org.apache.qpid.jms.transports.TransportSupport;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.PostConstruct;
+import javax.jms.*;
+import javax.naming.Context;
+import javax.naming.InitialContext;
+import java.net.URI;
+import java.util.Hashtable;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+
+/**
+ * 接收设备上报的属性消息
+ */
+@Component
+public class DevicePropertyListener{
+    protected static final Logger LOGGER = LoggerFactory.getLogger("DevicePropertyLog");
+
+    @Autowired
+    private DeviceService deviceService;
+
+    /**
+     * 异步线程池,参数可以根据业务特点作调整,也可以用其他异步方式来处理。
+     */
+    public final ExecutorService executorService = new ThreadPoolExecutor(Runtime.getRuntime().availableProcessors(),
+            Runtime.getRuntime().availableProcessors() * 2, 60,
+            TimeUnit.SECONDS, new LinkedBlockingQueue<>(5000));
+    /**
+     * 连接凭证接入键值
+     */
+    @Value("${cloud.iot.amqp.accessKey}")
+    private String accessKey = "";
+
+    /**
+     * 连接凭证接入码
+     */
+    @Value("${cloud.iot.amqp.password}")
+    private String password;
+
+    /**
+     * 按照qpid-jms的规范,组装连接URL。
+     */
+    @Value("${cloud.iot.amqp.connectionUrl}")
+    private String connectionUrl;
+
+
+    /**
+     * 队列名,可以使用默认队列 DefaultQueue
+     */
+    @Value("${cloud.iot.amqp.queuePropertyName}")
+    private String queueName = "";
+
+    private Connection connection;
+    private Session session;
+    private MessageConsumer consumer;
+    private long lastReconnectTime = 0;
+    private AtomicBoolean isReconnecting = new AtomicBoolean(false);
+
+
+    @PostConstruct
+    private void initIOTAmqpListener() throws Exception {
+        LOGGER.info("Init " + this.getClass().getSimpleName());
+
+        createConsumer();
+
+        // 处理消息有两种方式
+        // 1,主动拉数据(推荐),参照receiveMessage()
+        new Thread(() -> {
+            receiveMessage();
+        }).start();
+        LOGGER.info("End Init " + this.getClass().getSimpleName());
+
+        // 2, 添加监听,参照consumer.setMessageListener(messageListener), 服务端主动推数据给客户端,但得考虑接受的数据速率是客户能力能够承受住的
+        // consumer.setMessageListener(messageListener);
+        // 防止主程序退出,这里休眠了60s,60s后程序会结束
+        // Thread.sleep( 60 * 1000L);
+        // shutdown();
+    }
+
+    private void createConsumer() throws Exception {
+        long timeStamp = System.currentTimeMillis();
+        //UserName组装方法,请参见文档:AMQP客户端接入说明。
+        String userName = "accessKey=" + accessKey + "|timestamp=" + timeStamp;
+        Hashtable<String, String> hashtable = new Hashtable<>();
+        hashtable.put("connectionfactory.HwConnectionURL", connectionUrl);
+        hashtable.put(Context.INITIAL_CONTEXT_FACTORY, "org.apache.qpid.jms.jndi.JmsInitialContextFactory");
+        Context context = new InitialContext(hashtable);
+        JmsConnectionFactory cf = (JmsConnectionFactory) context.lookup("HwConnectionURL");
+
+        //信任服务端
+        TransportOptions to = new TransportOptions();
+        to.setTrustAll(true);
+        cf.setSslContext(TransportSupport.createJdkSslContext(to));
+
+        // 创建连接
+        Connection connection = cf.createConnection(userName, password);
+        ((JmsConnection) connection).addConnectionListener(myJmsConnectionListener);
+
+        // 创建 Session
+        // Session.CLIENT_ACKNOWLEDGE: 收到消息后,需要手动调用message.acknowledge()。
+        // Session.AUTO_ACKNOWLEDGE: SDK自动ACK(推荐)。
+        session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
+        connection.start();
+        connection.setExceptionListener(exceptionListener);
+        // 创建 consumer
+        consumer = session.createConsumer(new JmsQueue(getQueueName()));
+    }
+
+    private void receiveMessage() {
+        while (true) {
+            try {
+                if (consumer != null) {
+                    Message message = consumer.receive();
+                    // 建议异步处理收到的消息,确保receiveMessage函数里没有耗时逻辑。
+                    executorService.execute(() -> processMessage(message));
+                } else {
+                    Thread.sleep(1000L);
+                }
+            } catch (Exception e) {
+                LOGGER.error("receiveMessage hand an exception: " + e.getMessage());
+                e.printStackTrace();
+            }
+        }
+    }
+
+    /**
+     * 客户端断开需要重连
+     */
+    private void reconnect() {
+        if (isReconnecting.compareAndSet(false, true)) {
+            while (true) {
+                try {
+                    // 防止重连次数太多,重连时间间隔15s
+                    if (System.currentTimeMillis() - lastReconnectTime < 15 * 1000L) {
+                        Thread.sleep(15 * 1000L);
+                    }
+                    shutdown();
+                    createConsumer();
+                    lastReconnectTime = System.currentTimeMillis();
+                    isReconnecting.set(false);
+                    break;
+                } catch (Exception e) {
+                    LOGGER.info("reconnect hand an exception: " + e.getMessage());
+                    e.printStackTrace();
+                }
+            }
+        }
+    }
+
+    private void shutdown() {
+        if (consumer != null) {
+            try {
+                consumer.close();
+            } catch (JMSException e) {
+                e.printStackTrace();
+            }
+        }
+        if (session != null) {
+            try {
+                session.close();
+            } catch (JMSException e) {
+                e.printStackTrace();
+            }
+        }
+        if (connection != null) {
+            try {
+                connection.setExceptionListener(null);
+                connection.close();
+            } catch (JMSException e) {
+                e.printStackTrace();
+            }
+        }
+    }
+
+    private ExceptionListener exceptionListener = new ExceptionListener() {
+        @Override
+        public void onException(JMSException e) {
+            e.printStackTrace();
+            LOGGER.info("connection has an exception:" + e);
+            // 链接发生异常,需要重连
+            reconnect();
+        }
+    };
+
+    private static JmsConnectionListener myJmsConnectionListener = new JmsConnectionListener() {
+        /**
+         * 连接成功建立。
+         */
+        @Override
+        public void onConnectionEstablished(URI remoteURI) {
+            LOGGER.info("onConnectionEstablished, remoteUri:" + remoteURI);
+        }
+
+        /**
+         * 尝试过最大重试次数之后,最终连接失败。
+         */
+        @Override
+        public void onConnectionFailure(Throwable error) {
+            LOGGER.info("onConnectionFailure, " + error.getMessage());
+        }
+
+        /**
+         * 连接中断。
+         */
+        @Override
+        public void onConnectionInterrupted(URI remoteURI) {
+            LOGGER.info("onConnectionInterrupted, remoteUri:" + remoteURI);
+        }
+
+        /**
+         * 连接中断后又自动重连上。
+         */
+        @Override
+        public void onConnectionRestored(URI remoteURI) {
+            LOGGER.info("onConnectionRestored, remoteUri:" + remoteURI);
+        }
+
+        @Override
+        public void onInboundMessage(JmsInboundMessageDispatch envelope) {
+            LOGGER.info("onInboundMessage, " + envelope);
+        }
+
+        @Override
+        public void onSessionClosed(Session session, Throwable cause) {
+            LOGGER.info("onSessionClosed, session=" + session + ", cause =" + cause);
+        }
+
+        @Override
+        public void onConsumerClosed(MessageConsumer consumer, Throwable cause) {
+            LOGGER.info("MessageConsumer, consumer=" + consumer + ", cause =" + cause);
+        }
+
+        @Override
+        public void onProducerClosed(MessageProducer producer, Throwable cause) {
+            LOGGER.info("MessageProducer, producer=" + producer + ", cause =" + cause);
+        }
+    };
+
+    protected String getQueueName() {
+        return this.queueName;
+    }
+
+    /**
+     * 在这里处理您收到消息后的具体业务逻辑。
+     */
+    protected void processMessage(Message message) {
+        try {
+            String body = message.getBody(String.class);
+            String content = new String(body);
+            LOGGER.info("DevicePropertyListener receive an message, the content is " + content);
+            JSONObject contentBody = JSON.parseObject(content);
+            String deviceId = contentBody.getJSONObject("notify_data").getJSONObject("header").getString("device_id");
+            String strbody = contentBody.getJSONObject("notify_data").getString("body");
+            //更新设备属性
+            deviceService.updateDeviceProperty(deviceId, strbody);
+
+        } catch (Exception e) {
+            LOGGER.info("DevicePropertyListener processMessage occurs error: " + e.getMessage());
+            e.printStackTrace();
+        }
+    }
+}

+ 299 - 0
Server/distschedule-core/src/main/java/com/example/distschedule/listener/DeviceStatusListener.java

@@ -0,0 +1,299 @@
+/*
+ * Copyright (c) 2021 Huawei Device Co., Ltd.
+ * Licensed under the Apache License,Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ 
+package com.example.distschedule.listener;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONObject;
+import com.example.distschedule.service.DeviceService;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.qpid.jms.JmsConnection;
+import org.apache.qpid.jms.JmsConnectionFactory;
+import org.apache.qpid.jms.JmsConnectionListener;
+import org.apache.qpid.jms.JmsQueue;
+import org.apache.qpid.jms.message.JmsInboundMessageDispatch;
+import org.apache.qpid.jms.transports.TransportOptions;
+import org.apache.qpid.jms.transports.TransportSupport;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.PostConstruct;
+import javax.jms.*;
+import javax.naming.Context;
+import javax.naming.InitialContext;
+import java.net.URI;
+import java.util.Hashtable;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/**
+ * 监听设备上报状态
+ */
+@Component
+public class DeviceStatusListener{
+    protected static final Logger LOGGER = LoggerFactory.getLogger("DeviceStatusLog");
+
+    @Autowired
+    private DeviceService deviceService;
+
+    /**
+     * 异步线程池,参数可以根据业务特点作调整,也可以用其他异步方式来处理。
+     */
+    public final ExecutorService executorService = new ThreadPoolExecutor(Runtime.getRuntime().availableProcessors(),
+            Runtime.getRuntime().availableProcessors() * 2, 60,
+            TimeUnit.SECONDS, new LinkedBlockingQueue<>(5000));
+    /**
+     * 连接凭证接入键值
+     */
+    @Value("${cloud.iot.amqp.accessKey}")
+    private String accessKey = "";
+
+    /**
+     * 连接凭证接入码
+     */
+    @Value("${cloud.iot.amqp.password}")
+    private String password;
+
+    /**
+     * 按照qpid-jms的规范,组装连接URL。
+     */
+    @Value("${cloud.iot.amqp.connectionUrl}")
+    private String connectionUrl;
+
+
+    /**
+     * 队列名,可以使用默认队列 DefaultQueue
+     */
+    @Value("${cloud.iot.amqp.queueStatusName}")
+    private String queueName = "";
+
+    private Connection connection;
+    private Session session;
+    private MessageConsumer consumer;
+    private long lastReconnectTime = 0;
+    private AtomicBoolean isReconnecting = new AtomicBoolean(false);
+
+    @PostConstruct
+    private void initIOTAmqpListener() throws Exception {
+        LOGGER.info("Init " + this.getClass().getSimpleName());
+
+        createConsumer();
+
+        // 处理消息有两种方式
+        // 1,主动拉数据(推荐),参照receiveMessage()
+        new Thread(() -> {
+            receiveMessage();
+        }).start();
+        LOGGER.info("End Init " + this.getClass().getSimpleName());
+
+        // 2, 添加监听,参照consumer.setMessageListener(messageListener), 服务端主动推数据给客户端,但得考虑接受的数据速率是客户能力能够承受住的
+        // consumer.setMessageListener(messageListener);
+        // 防止主程序退出,这里休眠了60s,60s后程序会结束
+        // Thread.sleep( 60 * 1000L);
+        // shutdown();
+    }
+
+    private void createConsumer() throws Exception {
+        long timeStamp = System.currentTimeMillis();
+        //UserName组装方法,请参见文档:AMQP客户端接入说明。
+        String userName = "accessKey=" + accessKey + "|timestamp=" + timeStamp;
+        Hashtable<String, String> hashtable = new Hashtable<>();
+        hashtable.put("connectionfactory.HwConnectionURL", connectionUrl);
+        hashtable.put(Context.INITIAL_CONTEXT_FACTORY, "org.apache.qpid.jms.jndi.JmsInitialContextFactory");
+        Context context = new InitialContext(hashtable);
+        JmsConnectionFactory cf = (JmsConnectionFactory) context.lookup("HwConnectionURL");
+
+        //信任服务端
+        TransportOptions to = new TransportOptions();
+        to.setTrustAll(true);
+        cf.setSslContext(TransportSupport.createJdkSslContext(to));
+
+        // 创建连接
+        Connection connection = cf.createConnection(userName, password);
+        ((JmsConnection) connection).addConnectionListener(myJmsConnectionListener);
+
+        // 创建 Session
+        // Session.CLIENT_ACKNOWLEDGE: 收到消息后,需要手动调用message.acknowledge()。
+        // Session.AUTO_ACKNOWLEDGE: SDK自动ACK(推荐)。
+        session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
+        connection.start();
+        connection.setExceptionListener(exceptionListener);
+        // 创建 consumer
+        consumer = session.createConsumer(new JmsQueue(getQueueName()));
+    }
+
+    private void receiveMessage() {
+        while (true) {
+            try {
+                if (consumer != null) {
+                    Message message = consumer.receive();
+                    // 建议异步处理收到的消息,确保receiveMessage函数里没有耗时逻辑。
+                    executorService.execute(() -> processMessage(message));
+                } else {
+                    Thread.sleep(1000L);
+                }
+            } catch (Exception e) {
+                LOGGER.error("receiveMessage hand an exception: " + e.getMessage());
+                e.printStackTrace();
+            }
+        }
+    }
+
+    /**
+     * 客户端断开需要重连
+     */
+    private void reconnect() {
+        if (isReconnecting.compareAndSet(false, true)) {
+            while (true) {
+                try {
+                    // 防止重连次数太多,重连时间间隔15s
+                    if (System.currentTimeMillis() - lastReconnectTime < 15 * 1000L) {
+                        Thread.sleep(15 * 1000L);
+                    }
+                    shutdown();
+                    createConsumer();
+                    lastReconnectTime = System.currentTimeMillis();
+                    isReconnecting.set(false);
+                    break;
+                } catch (Exception e) {
+                    LOGGER.info("reconnect hand an exception: " + e.getMessage());
+                    e.printStackTrace();
+                }
+            }
+        }
+    }
+
+    private void shutdown() {
+        if (consumer != null) {
+            try {
+                consumer.close();
+            } catch (JMSException e) {
+                e.printStackTrace();
+            }
+        }
+        if (session != null) {
+            try {
+                session.close();
+            } catch (JMSException e) {
+                e.printStackTrace();
+            }
+        }
+        if (connection != null) {
+            try {
+                connection.setExceptionListener(null);
+                connection.close();
+            } catch (JMSException e) {
+                e.printStackTrace();
+            }
+        }
+    }
+
+    private ExceptionListener exceptionListener = new ExceptionListener() {
+        @Override
+        public void onException(JMSException e) {
+            e.printStackTrace();
+            LOGGER.info("connection has an exception:" + e);
+            // 链接发生异常,需要重连
+            reconnect();
+        }
+    };
+
+    private static JmsConnectionListener myJmsConnectionListener = new JmsConnectionListener() {
+        /**
+         * 连接成功建立。
+         */
+        @Override
+        public void onConnectionEstablished(URI remoteURI) {
+            LOGGER.info("onConnectionEstablished, remoteUri:" + remoteURI);
+        }
+
+        /**
+         * 尝试过最大重试次数之后,最终连接失败。
+         */
+        @Override
+        public void onConnectionFailure(Throwable error) {
+            LOGGER.info("onConnectionFailure, " + error.getMessage());
+        }
+
+        /**
+         * 连接中断。
+         */
+        @Override
+        public void onConnectionInterrupted(URI remoteURI) {
+            LOGGER.info("onConnectionInterrupted, remoteUri:" + remoteURI);
+        }
+
+        /**
+         * 连接中断后又自动重连上。
+         */
+        @Override
+        public void onConnectionRestored(URI remoteURI) {
+            LOGGER.info("onConnectionRestored, remoteUri:" + remoteURI);
+        }
+
+        @Override
+        public void onInboundMessage(JmsInboundMessageDispatch envelope) {
+            LOGGER.info("onInboundMessage, " + envelope);
+        }
+
+        @Override
+        public void onSessionClosed(Session session, Throwable cause) {
+            LOGGER.info("onSessionClosed, session=" + session + ", cause =" + cause);
+        }
+
+        @Override
+        public void onConsumerClosed(MessageConsumer consumer, Throwable cause) {
+            LOGGER.info("MessageConsumer, consumer=" + consumer + ", cause =" + cause);
+        }
+
+        @Override
+        public void onProducerClosed(MessageProducer producer, Throwable cause) {
+            LOGGER.info("MessageProducer, producer=" + producer + ", cause =" + cause);
+        }
+    };
+
+    protected String getQueueName() {
+        return this.queueName;
+    }
+
+    /**
+     * 在这里处理您收到消息后的具体业务逻辑。
+     */
+    protected void processMessage(Message message) {
+        try {
+            //{"resource":"device.status","event":"update","event_time":"20210901T012225Z","notify_data":{"header":{"app_id":"6989a30bea954354b662920f369f4503","device_id":"612779690ad1ed028666ec69_madixin","node_id":"madixin","product_id":"612779690ad1ed028666ec69","gateway_id":"612779690ad1ed028666ec69_madixin"},"body":{"status":"ONLINE","last_online_time":"20210901T012225Z"}}}
+            String body = message.getBody(String.class);
+            String content = new String(body);
+            LOGGER.info("DeviceStatusListener receive an message, the content is " + content);
+            JSONObject contentBody = JSON.parseObject(content);
+            String deviceId = contentBody.getJSONObject("notify_data").getJSONObject("header").getString("device_id");
+            String status = contentBody.getJSONObject("notify_data").getJSONObject("body").getString("status");
+            if (StringUtils.isBlank(deviceId) || StringUtils.isBlank(status)) {
+                LOGGER.error("Invalid DeviceStatusListener deviceId= " + deviceId + ",status=" + status);
+                return;
+            }
+            deviceService.updateDeviceStatue(deviceId, status);
+        } catch (Exception e) {
+            LOGGER.error("DeviceStatusListener processMessage occurs error: " + e.getMessage());
+            e.printStackTrace();
+        }
+    }
+}

+ 35 - 0
Server/distschedule-core/src/main/java/com/example/distschedule/rabbitmq/RabbitConf.java

@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2021 Huawei Device Co., Ltd.
+ * Licensed under the Apache License,Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ 
+package com.example.distschedule.rabbitmq;
+
+import org.springframework.amqp.core.DirectExchange;
+import org.springframework.amqp.rabbit.annotation.EnableRabbit;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+@Configuration
+@EnableRabbit
+public class RabbitConf {
+
+    @Value("${spring.rabbitmq.exchange.deviceproperty}")
+    private String devicePropertyExchange;
+
+    @Bean(name = "devicePropertyExchange")
+    public DirectExchange devicePropertyExchange() {
+        return new DirectExchange(devicePropertyExchange);
+    }
+}

+ 0 - 0
Server/distschedule-core/src/main/resources/.gitkeep


+ 74 - 0
Server/distschedule-core/src/main/resources/application-dockercompose.properties

@@ -0,0 +1,74 @@
+server.servlet.context-path=/distschedule-api
+#Encoding Configuration
+server.tomcat.uri-encoding=UTF-8
+spring.http.encoding.charset=UTF-8
+spring.http.encoding.enabled=true
+spring.http.encoding.force=true
+spring.messages.encoding=UTF-8
+# Swagger Configuration
+
+sop.swagger.enable=true
+sop.swagger.packageScan=com.example.distschedule
+sop.swagger.title=Dist Schedule API
+sop.swagger.description=Dist Schedule API
+sop.swagger.version=3.0
+
+server.max-http-header-size=10000000
+
+# Get the application info from maven
+application.version=@project.version@
+application.title=@project.artifactId@
+build.timestamp=@maven.build.timestamp@
+# Custom banner
+spring.banner.location=classpath:banner.txt
+
+
+spring.jackson.date-format = yyyy-MM-dd HH:mm:ss
+spring.jackson.time-zone=GMT+8
+
+#spring.datasource.url=${datasource_url:jdbc:mysql://100.104.16.181:3306/distschedule?useUnicode=true&characterEncoding=utf-8&useSSL=false&allowMultiQueries=true&serverTimezone=Asia/Shanghai}
+spring.datasource.url=${datasource_url:jdbc:mysql://mysql:3306/distschedule?useUnicode=true&characterEncoding=utf-8&useSSL=false&allowMultiQueries=true&serverTimezone=Asia/Shanghai}
+
+spring.datasource.username=root
+spring.datasource.password=root
+spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
+
+#flyway config
+spring.flyway.enabled=true
+spring.flyway.locations=classpath:/db/migration
+
+#MyBatis Configuration
+#mybatis.mapperLocations=classpath:mybatis/mapper/*.xml
+mybatis.typeAliasesPackage=com.example.distschedule.dao.mapper
+mybatis.configuration.map-underscore-to-camel-case=true
+
+# ���ݿ����ӳ�
+spring.datasource.hikari.connection-test-query=SELECT 1 FROM DUAL
+spring.datasource.hikari.connection-timeout=30000
+spring.datasource.hikari.maximum-pool-size=100
+spring.datasource.hikari.max-lifetime=1800000
+spring.datasource.hikari.minimum-idle=20
+
+#logging.level.com.example.distschedule.dao=debug
+
+logging.level.root: INFO
+
+# IOT������
+cloud.iot.ak=******
+cloud.iot.sk=******
+cloud.iot.appId=******
+
+cloud.iot.amqp.accessKey=******
+cloud.iot.amqp.password=******
+cloud.iot.amqp.queueStatusName=madixinQueue
+cloud.iot.amqp.queuePropertyName=madixinDevicePropertyQueue
+cloud.iot.amqp.queueMsgName=madixinDeviceMsgQueue
+cloud.iot.amqp.connectionUrl=amqps://******.iot-amqps.cn-north-4.myhuaweicloud.com:5671?amqp.vhost=default&amqp.idleTimeout=8000&amqp.saslMechanisms=PLAIN
+
+spring.rabbitmq.host=rabbitmq
+spring.rabbitmq.port=5672
+spring.rabbitmq.username=guest
+spring.rabbitmq.password=guest
+spring.rabbitmq.exchange.deviceproperty=dockercompose_deviceproperty_exchange
+
+

+ 73 - 0
Server/distschedule-core/src/main/resources/application-local.properties

@@ -0,0 +1,73 @@
+server.servlet.context-path=/distschedule-api
+#Encoding Configuration
+server.tomcat.uri-encoding=UTF-8
+spring.http.encoding.charset=UTF-8
+spring.http.encoding.enabled=true
+spring.http.encoding.force=true
+spring.messages.encoding=UTF-8
+# Swagger Configuration
+
+sop.swagger.enable=true
+sop.swagger.packageScan=com.example.distschedule
+sop.swagger.title=Dist Schedule API
+sop.swagger.description=Dist Schedule API
+sop.swagger.version=3.0
+
+server.max-http-header-size=10000000
+
+# Get the application info from maven
+application.version=@project.version@
+application.title=@project.artifactId@
+build.timestamp=@maven.build.timestamp@
+# Custom banner
+spring.banner.location=classpath:banner.txt
+
+
+spring.jackson.date-format = yyyy-MM-dd HH:mm:ss
+spring.jackson.time-zone=GMT+8
+
+spring.datasource.url=${datasource_url:jdbc:mysql://*****:3306/distschedule?useUnicode=true&characterEncoding=utf-8&useSSL=false&allowMultiQueries=true&serverTimezone=Asia/Shanghai}
+
+spring.datasource.username=root
+spring.datasource.password=harmonyos
+spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
+
+#flyway config
+spring.flyway.enabled=true
+spring.flyway.locations=classpath:/db/migration
+
+#MyBatis Configuration
+#mybatis.mapperLocations=classpath:mybatis/mapper/*.xml
+mybatis.typeAliasesPackage=com.example.distschedule.dao.mapper
+mybatis.configuration.map-underscore-to-camel-case=true
+
+# Êý¾Ý¿âÁ¬½Ó³Ø
+spring.datasource.hikari.connection-test-query=SELECT 1 FROM DUAL
+spring.datasource.hikari.connection-timeout=30000
+spring.datasource.hikari.maximum-pool-size=100
+spring.datasource.hikari.max-lifetime=1800000
+spring.datasource.hikari.minimum-idle=20
+
+#logging.level.com.example.distschedule.dao=debug
+
+logging.level.root: INFO
+
+# IOTÔÆÅäÖÃ
+cloud.iot.ak=*****
+cloud.iot.sk=*****
+cloud.iot.appId=*****
+
+cloud.iot.amqp.accessKey=*****
+cloud.iot.amqp.password=*****
+cloud.iot.amqp.queueStatusName=*****
+cloud.iot.amqp.queuePropertyName=*****
+cloud.iot.amqp.queueMsgName=*****
+cloud.iot.amqp.connectionUrl=amqps://*****.iot-amqps.cn-north-4.myhuaweicloud.com:5671?amqp.vhost=default&amqp.idleTimeout=8000&amqp.saslMechanisms=PLAIN
+
+spring.rabbitmq.host=**.**.**.***
+spring.rabbitmq.port=5672
+spring.rabbitmq.username=admin
+spring.rabbitmq.password=admin
+spring.rabbitmq.exchange.deviceproperty=madixin_deviceproperty_exchange
+
+

+ 73 - 0
Server/distschedule-core/src/main/resources/application.properties

@@ -0,0 +1,73 @@
+server.servlet.context-path=/distschedule-api
+#Encoding Configuration
+server.tomcat.uri-encoding=UTF-8
+spring.http.encoding.charset=UTF-8
+spring.http.encoding.enabled=true
+spring.http.encoding.force=true
+spring.messages.encoding=UTF-8
+# Swagger Configuration
+
+sop.swagger.enable=true
+sop.swagger.packageScan=com.example.distschedule
+sop.swagger.title=Dist Schedule API
+sop.swagger.description=Dist Schedule API
+sop.swagger.version=3.0
+
+server.max-http-header-size=10000000
+
+# Get the application info from maven
+application.version=@project.version@
+application.title=@project.artifactId@
+build.timestamp=@maven.build.timestamp@
+# Custom banner
+spring.banner.location=classpath:banner.txt
+
+
+spring.jackson.date-format = yyyy-MM-dd HH:mm:ss
+spring.jackson.time-zone=GMT+8
+
+spring.datasource.url=${datasource_url:jdbc:mysql://*****:3306/distschedule?useUnicode=true&characterEncoding=utf-8&useSSL=false&allowMultiQueries=true&serverTimezone=Asia/Shanghai}
+
+spring.datasource.username=root
+spring.datasource.password=harmonyos
+spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
+
+#flyway config
+spring.flyway.enabled=true
+spring.flyway.locations=classpath:/db/migration
+
+#MyBatis Configuration
+#mybatis.mapperLocations=classpath:mybatis/mapper/*.xml
+mybatis.typeAliasesPackage=com.example.distschedule.dao.mapper
+mybatis.configuration.map-underscore-to-camel-case=true
+
+# Êý¾Ý¿âÁ¬½Ó³Ø
+spring.datasource.hikari.connection-test-query=SELECT 1 FROM DUAL
+spring.datasource.hikari.connection-timeout=30000
+spring.datasource.hikari.maximum-pool-size=100
+spring.datasource.hikari.max-lifetime=1800000
+spring.datasource.hikari.minimum-idle=20
+
+#logging.level.com.example.distschedule.dao=debug
+
+logging.level.root: INFO
+
+# IOTÔÆÅäÖÃ
+cloud.iot.ak=*****
+cloud.iot.sk=*****
+cloud.iot.appId=*****
+
+cloud.iot.amqp.accessKey=*****
+cloud.iot.amqp.password=*****
+cloud.iot.amqp.queueStatusName=*****
+cloud.iot.amqp.queuePropertyName=*****
+cloud.iot.amqp.queueMsgName=*****
+cloud.iot.amqp.connectionUrl=amqps://*****.iot-amqps.cn-north-4.myhuaweicloud.com:5671?amqp.vhost=default&amqp.idleTimeout=8000&amqp.saslMechanisms=PLAIN
+
+spring.rabbitmq.host=**.**.**.***
+spring.rabbitmq.port=5672
+spring.rabbitmq.username=admin
+spring.rabbitmq.password=admin
+spring.rabbitmq.exchange.deviceproperty=madixin_deviceproperty_exchange
+
+

+ 15 - 0
Server/distschedule-core/src/main/resources/banner.txt

@@ -0,0 +1,15 @@
+ ${AnsiColor.BRIGHT_YELLOW}
+'########::'##::::'##:'####:'##:::::::'########:::::::::::'########::'########:::'#######::::::::'##:'########::'######::'########:
+ ##.... ##: ##:::: ##:. ##:: ##::::::: ##.... ##:::::::::: ##.... ##: ##.... ##:'##.... ##::::::: ##: ##.....::'##... ##:... ##..::
+ ##:::: ##: ##:::: ##:: ##:: ##::::::: ##:::: ##:::::::::: ##:::: ##: ##:::: ##: ##:::: ##::::::: ##: ##::::::: ##:::..::::: ##::::
+ ########:: ##:::: ##:: ##:: ##::::::: ##:::: ##:'#######: ########:: ########:: ##:::: ##::::::: ##: ######::: ##:::::::::: ##::::
+ ##.... ##: ##:::: ##:: ##:: ##::::::: ##:::: ##:........: ##.....::: ##.. ##::: ##:::: ##:'##::: ##: ##...:::: ##:::::::::: ##::::
+ ##:::: ##: ##:::: ##:: ##:: ##::::::: ##:::: ##:::::::::: ##:::::::: ##::. ##:: ##:::: ##: ##::: ##: ##::::::: ##::: ##:::: ##::::
+ ########::. #######::'####: ########: ########::::::::::: ##:::::::: ##:::. ##:. #######::. ######:: ########:. ######::::: ##::::
+........::::.......:::....::........::........::::::::::::..:::::::::..:::::..:::.......::::......:::........:::......::::::..:::::
+${AnsiColor.CYAN} ${AnsiStyle.BOLD} ${AnsiStyle.ITALIC}
+>>> ${application.title} : ${application.version}
+>>> build timestamp: ${build.timestamp}
+>>> environment: ${spring.profiles.active}
+
+${AnsiColor.DEFAULT}

+ 72 - 0
Server/distschedule-core/src/main/resources/log4j2.xml

@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Configuration status="WARN">
+    <properties>
+        <property name="LOG_HOME">/clouddragonData/log/distschedule</property>
+        <property name="FILE_NAME">distschedule</property>
+		<property name="DEVICE_MSG_FILE_NAME">DeviceMsg</property>
+		<property name="DEVICE_PROPERTY_FILE_NAME">DeviceProperty</property>
+		<property name="DEVICE_STATUS_FILE_NAME">DeviceStatus</property>
+    </properties>
+
+    <Appenders>
+        <Console name="Console" target="SYSTEM_OUT">
+            <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5p %c{1}:%L - %msg%n"/>
+        </Console>
+
+        <RollingRandomAccessFile name="RollingRandomAccessFile" fileName="${LOG_HOME}/${FILE_NAME}.log"
+                                 filePattern="${LOG_HOME}/$${date:yyyy-MM}/${FILE_NAME}-%d{yyyy-MM-dd}-%i.log">
+            <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5p %c{1}:%L - %msg%n"/>
+            <Policies>
+                <TimeBasedTriggeringPolicy interval="1"/>
+                <SizeBasedTriggeringPolicy size="50 MB"/>
+            </Policies>
+            <DefaultRolloverStrategy max="20"/>
+        </RollingRandomAccessFile>
+
+        <RollingRandomAccessFile name="DeviceMsgFile" fileName="${LOG_HOME}/${DEVICE_MSG_FILE_NAME}.log"
+                                 filePattern="${LOG_HOME}/$${date:yyyy-MM}/${DEVICE_MSG_FILE_NAME}-%d{yyyy-MM-dd}-%i.log">
+            <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5p %c{1}:%L - %msg%n"/>
+            <Policies>
+                <TimeBasedTriggeringPolicy interval="1"/>
+                <SizeBasedTriggeringPolicy size="50 MB"/>
+            </Policies>
+            <DefaultRolloverStrategy max="20"/>
+        </RollingRandomAccessFile>
+
+        <RollingRandomAccessFile name="DevicePropertyFile" fileName="${LOG_HOME}/${DEVICE_PROPERTY_FILE_NAME}.log"
+                                 filePattern="${LOG_HOME}/$${date:yyyy-MM}/${DEVICE_PROPERTY_FILE_NAME}-%d{yyyy-MM-dd}-%i.log">
+            <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5p %c{1}:%L - %msg%n"/>
+            <Policies>
+                <TimeBasedTriggeringPolicy interval="1"/>
+                <SizeBasedTriggeringPolicy size="50 MB"/>
+            </Policies>
+            <DefaultRolloverStrategy max="20"/>
+        </RollingRandomAccessFile>
+
+        <RollingRandomAccessFile name="DeviceStatusFile" fileName="${LOG_HOME}/${DEVICE_STATUS_FILE_NAME}.log"
+                                 filePattern="${LOG_HOME}/$${date:yyyy-MM}/${DEVICE_STATUS_FILE_NAME}-%d{yyyy-MM-dd}-%i.log">
+            <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5p %c{1}:%L - %msg%n"/>
+            <Policies>
+                <TimeBasedTriggeringPolicy interval="1"/>
+                <SizeBasedTriggeringPolicy size="50 MB"/>
+            </Policies>
+            <DefaultRolloverStrategy max="20"/>
+        </RollingRandomAccessFile>
+    </Appenders>
+
+    <Loggers>
+        <Root level="info">
+            <AppenderRef ref="Console"/>
+            <AppenderRef ref="RollingRandomAccessFile"/>
+        </Root>
+		<logger name="DeviceMsgLog" level="info" additivity="false">
+			<AppenderRef ref="DeviceMsgFile" />
+		</logger>
+		<logger name="DevicePropertyLog" level="info" additivity="false">
+			<AppenderRef ref="DevicePropertyFile" />
+		</logger>
+		<logger name="DeviceStatusLog" level="info" additivity="false">
+			<AppenderRef ref="DeviceStatusFile" />
+		</logger>
+    </Loggers>
+</Configuration>

+ 0 - 0
Server/distschedule-core/src/test/java/.gitkeep


+ 82 - 0
Server/distschedule-dao/pom.xml

@@ -0,0 +1,82 @@
+<?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">
+    <parent>
+        <artifactId>distschedule</artifactId>
+        <groupId>com.example.distschedule</groupId>
+        <version>1.0.0-SNAPSHOT</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>distschedule-dao</artifactId>
+    <description>数据库dao层,负责和数据库的交互, 该模块中的所有代码均通过mybatis-generator自动生成</description>
+
+    <properties>
+        <mysql-connector>8.0.18</mysql-connector>
+        <alibaba.druid>1.1.21</alibaba.druid>
+        <mybatis-spring-boot>1.3.2</mybatis-spring-boot>
+        <mybatis.generator.plugin.version>1.3.8</mybatis.generator.plugin.version>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>mysql</groupId>
+            <artifactId>mysql-connector-java</artifactId>
+            <version>${mysql-connector}</version>
+        </dependency>
+       <!--  <dependency>
+            <groupId>com.alibaba</groupId>
+            <artifactId>druid</artifactId>
+            <version>${alibaba.druid}</version>
+        </dependency> -->
+
+        <dependency>
+            <groupId>org.mybatis.spring.boot</groupId>
+            <artifactId>mybatis-spring-boot-starter</artifactId>
+            <version>${mybatis-spring-boot}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.flywaydb</groupId>
+            <artifactId>flyway-core</artifactId>
+        </dependency>
+    </dependencies>
+    <build>
+
+        <plugins>
+            <!--
+       <plugin>
+           <groupId>org.flywaydb</groupId>
+           <artifactId>flyway-maven-plugin</artifactId>
+           <version>5.1.4</version>
+           <configuration>
+               <driver>com.mysql.jdbc.Driver</driver>
+               <url>jdbc:mysql://qeesung.host.huawei.com/build_project_qeesung_as_code</url>
+               <user>root</user>
+               <password></password>
+           </configuration>
+       </plugin>
+       <plugin>
+           <groupId>org.mybatis.generator</groupId>
+           <artifactId>mybatis-generator-maven-plugin</artifactId>
+           <version>1.3.7</version>
+           <configuration>
+               <verbose>true</verbose>
+               <overwrite>true</overwrite>
+           </configuration>
+           <dependencies>
+               <dependency>
+                   <groupId>mysql</groupId>
+                   <artifactId>mysql-connector-java</artifactId>
+                   <version>${mysql-connector}</version>
+               </dependency>
+               <dependency>
+                   <groupId>com.itfsw</groupId>
+                   <artifactId>mybatis-generator-plugin</artifactId>
+                   <version>${mybatis.generator.plugin.version}</version>
+               </dependency>
+           </dependencies>
+       </plugin>-->
+        </plugins>
+    </build>
+</project>

+ 67 - 0
Server/distschedule-dao/src/main/java/com/example/distschedule/dao/mapper/DeviceMapper.java

@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2021 Huawei Device Co., Ltd.
+ * Licensed under the Apache License,Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ 
+package com.example.distschedule.dao.mapper;
+
+import com.example.distschedule.dao.model.Device;
+import com.example.distschedule.dao.model.UserDevice;
+import org.apache.ibatis.annotations.*;
+import org.springframework.stereotype.Component;
+
+import java.util.List;
+
+@Mapper
+@Component
+public interface DeviceMapper {
+
+    @Select("SELECT device.id,device.type_id,device.owner_id,device.name,device.state,device.create_time,device.update_time,device.property,user.name as owner_name,user.phone as owner_phone FROM device,user WHERE device.id = #{deviceId} and device.owner_id=user.id")
+    Device selectDeviceById(String deviceId);
+
+    @Insert("INSERT INTO `device` (`id`,`name`,`type_id`,`owner_id`,`state`) VALUES (#{device.id},#{device.name},#{device.typeId},#{device.ownerId},#{device.state})")
+    int saveDevice(@Param("device") Device device);
+
+    @Select("SELECT * FROM user_device WHERE user_id = #{userId} AND device_id = #{deviceId}")
+    UserDevice getUserDevice(@Param("userId") String userId, @Param("deviceId") String deviceId);
+
+    @Insert("INSERT INTO `user_device` (`user_id`,`device_id`) VALUES (#{userId},#{deviceId})")
+    int saveUserDevice(@Param("userId") String userId, @Param("deviceId") String deviceId);
+
+    @Delete("DELETE from user_device where user_id = #{userId} AND device_id = #{deviceId}")
+    int deleteUserDevice(@Param("userId") String userId, @Param("deviceId") String deviceId);
+
+    @Select("SELECT device.id,device.type_id,device.owner_id,device.name,device.state,device.create_time,device.update_time,device.property,user.name as owner_name,user.phone as owner_phone FROM device,user WHERE device.owner_id = #{userId} AND user.id=device.owner_id")
+    List<Device> selectCreatedDevicesByUserId(String userId);
+
+    @Select("SELECT device.id,device.type_id,device.owner_id,device.name,device.state,device.create_time,device.update_time,device.property,user.name as owner_name,user.phone as owner_phone FROM user_device,device,user WHERE user_device.user_id = #{userId} and user_device.device_id=device.id and user.id = device.owner_id")
+    List<Device> selectSharedDevicesByUserId(String userId);
+
+    @Delete("DELETE from user_device where device_id = #{deviceId}")
+    int deleteUserDeviceByDeviceId(String deviceId);
+
+    @Delete("DELETE from device where id = #{deviceId}")
+    int deleteDevice(String deviceId);
+
+    @Update("UPDATE device SET state = #{state} , update_time = NOW()  WHERE id = #{deviceId}")
+    int updateDeviceStatue(@Param("deviceId") String deviceId, @Param("state") String state);
+
+    @Select("SELECT * FROM user_device WHERE device_id = #{deviceId}")
+    List<UserDevice> selectShareUsersByDeviceId(String deviceId);
+
+    @Update("UPDATE device SET name = #{device.name} , update_time = NOW()  WHERE id = #{device.id}")
+    int updateDevice(@Param("device") Device device);
+
+    @Update("UPDATE device SET property = #{property} WHERE id = #{deviceId}")
+    int updateDeviceProperty(@Param("deviceId") String deviceId, @Param("property") String property);
+}

+ 38 - 0
Server/distschedule-dao/src/main/java/com/example/distschedule/dao/mapper/DeviceTypeMapper.java

@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2021 Huawei Device Co., Ltd.
+ * Licensed under the Apache License,Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ 
+package com.example.distschedule.dao.mapper;
+
+
+import com.example.distschedule.dao.model.DeviceType;
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Select;
+import org.springframework.stereotype.Component;
+
+import java.util.List;
+
+
+@Mapper
+@Component
+public interface DeviceTypeMapper {
+    @Select("SELECT * FROM device_type WHERE id = #{deviceTypeId}")
+    DeviceType selectDeviceTypeById(int deviceTypeId);
+
+    @Select("SELECT * FROM device_type")
+    List<DeviceType> selectDeviceTypes();
+
+    @Select("SELECT * FROM device_type WHERE product_id = #{productId}")
+    DeviceType selectDeviceTypeByProductId(String productId);
+}

+ 76 - 0
Server/distschedule-dao/src/main/java/com/example/distschedule/dao/mapper/FamilyMapper.java

@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2021 Huawei Device Co., Ltd.
+ * Licensed under the Apache License,Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ 
+package com.example.distschedule.dao.mapper;
+
+import com.example.distschedule.dao.model.Family;
+import com.example.distschedule.dao.model.UserFamily;
+import org.apache.ibatis.annotations.*;
+import org.springframework.stereotype.Component;
+
+import java.util.List;
+
+@Mapper
+@Component
+public interface FamilyMapper {
+    /**
+     * 根据用户Id,查询用户创建的家庭
+     *
+     * @return 家庭列表
+     */
+    @Select("SELECT id,name,creator_id,create_time,update_time FROM family WHERE creator_id = #{userId}")
+    List<Family> selectCreatedFamiliesByUserId(String userId);
+
+    /**
+     * 根据用户Id,查询用户加入的家庭
+     *
+     * @return 家庭列表
+     */
+    @Select("SELECT family.id,family.name,family.creator_id,family.create_time,family.update_time FROM family,user_family WHERE user_family.user_id = #{userId} and  user_family.family_id = family.id")
+    List<Family> selectJoinedFamiliesByUserId(String userId);
+
+    @Delete("DELETE from family where id = #{familyId} and creator_id = #{userId}")
+    int deleteFamily(@Param("userId") String userId, @Param("familyId") String familyId);
+
+    @Select("SELECT id,name,creator_id,create_time,update_time FROM family WHERE id = #{familyId}")
+    Family selectFamilyById(String familyId);
+
+    @Delete("DELETE from user_family where family_id = #{familyId}")
+    int deleteFamilyMembers(String familyId);
+
+    @Insert("INSERT INTO `family` (`id`,`name`,`creator_id`) VALUES (#{family.id},#{family.name},#{family.creatorId})")
+    int saveFamily(@Param("family") Family family);
+
+    @Update("UPDATE family SET name = #{name} , update_time = NOW()  WHERE id = #{id}")
+    int updateFamily(Family family);
+
+    @Select("SELECT id,user_id,family_id FROM user_family WHERE user_id = #{userId} AND family_id = #{familyId}")
+    UserFamily getUserFamily(@Param("userId")String userId, @Param("familyId")String familyId);
+
+    @Insert("INSERT INTO `user_family` (`user_id`,`family_id`,`role`) VALUES (#{userId},#{familyId},#{role})")
+    int saveUserFamily(@Param("userId")String userId, @Param("familyId")String familyId, @Param("role")String role);
+
+    @Update("UPDATE user_family SET role = #{role} WHERE user_id = #{userId} AND family_id = #{familyId}")
+    int updateUserFamily(@Param("userId")String userId, @Param("familyId")String familyId, @Param("role")String role);
+
+    @Delete("DELETE from user_family where user_id = #{userId} AND family_id = #{familyId}")
+    int deleteFamilyMember(@Param("userId")String userId, @Param("familyId")String familyId);
+
+    @Select("SELECT user_family.id,user_family.user_id,user_family.family_id,user_family.role,user.phone as user_phone,user.name as user_name FROM user_family left join user on user_family.user_id=user.id WHERE family_id = #{familyId}")
+    List<UserFamily> selectFamilyMembersById(String familyId);
+
+    @Select("SELECT family.id as family_id,user.phone as user_phone,user.name as user_name,user.id as user_id FROM family left join user on family.creator_id=user.id WHERE family.id = #{familyId}")
+    UserFamily selectCreatorFamily(String familyId);
+}

+ 74 - 0
Server/distschedule-dao/src/main/java/com/example/distschedule/dao/mapper/ScheduleMapper.java

@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2021 Huawei Device Co., Ltd.
+ * Licensed under the Apache License,Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ 
+package com.example.distschedule.dao.mapper;
+
+import com.example.distschedule.dao.model.Schedule;
+import com.example.distschedule.dao.model.UserSchedule;
+import org.apache.ibatis.annotations.*;
+import org.springframework.stereotype.Component;
+
+import java.util.Date;
+import java.util.List;
+
+@Mapper
+@Component
+public interface ScheduleMapper {
+    @Select("SELECT  schedule.`name`,schedule.id,`schedule`.device_config,`schedule`.remind_day,`schedule`.creator_id,`schedule`.scene_id,`schedule`.state_detail,`schedule`.start_time,`schedule`.end_time,`schedule`.create_time,`schedule`.update_time,GROUP_CONCAT(CONCAT_WS(',',user_schedule.user_id) SEPARATOR ',') user_ids FROM schedule,user_schedule WHERE schedule.id = #{scheduleId} and user_schedule.schedule_id=schedule.id GROUP BY `schedule`.id")
+    Schedule selectScheduleById(String scheduleId);
+
+    @Insert("INSERT INTO `schedule` (`id`,`name`,`device_config`,`remind_day`,`creator_id`,`scene_id`,`state_detail`,`start_time`,`end_time`) " +
+            "VALUES (#{schedule.id},#{schedule.name},#{schedule.deviceConfig},#{schedule.remindDay},#{schedule.creatorId},#{schedule.sceneId},#{schedule.stateDetail},#{schedule.startTime},#{schedule.endTime})")
+    int saveSchedule(@Param("schedule") Schedule schedule);
+
+    @Delete("DELETE from schedule where id = #{scheduleId}")
+    int deleteSchedule(String scheduleId);
+
+    @Update("UPDATE schedule SET name=#{schedule.name},device_config=#{schedule.deviceConfig},remind_day=#{schedule.remindDay}," +
+            "creator_id=#{schedule.creatorId},scene_id=#{schedule.sceneId},state_detail=#{schedule.stateDetail},start_time=#{schedule.startTime},end_time=#{schedule.endTime},update_time=NOW() WHERE id = #{schedule.id}")
+    int updateSchedule(@Param("schedule") Schedule schedule);
+
+    @Select("SELECT  schedule.`name`,schedule.id,`schedule`.device_config,`schedule`.remind_day,`schedule`.creator_id,`schedule`.scene_id,`schedule`.state_detail,`schedule`.start_time,`schedule`.end_time,`schedule`.create_time,`schedule`.update_time,GROUP_CONCAT(CONCAT_WS(',',user_schedule.user_id) SEPARATOR ',') user_ids FROM schedule,user_schedule WHERE schedule.remind_day='' and user_schedule.user_id = #{userId} and user_schedule.schedule_id=schedule.id and start_time>=#{startTime} and start_time<=#{endTime} GROUP BY `schedule`.id ")
+    List<Schedule> selectDefaultSchedulesByDatesAndUser(@Param("userId") String userId, @Param("startTime") Date startTime, @Param("endTime") Date endTime);
+
+    @Select("SELECT  schedule.`name`,schedule.id,`schedule`.device_config,`schedule`.remind_day,`schedule`.creator_id,`schedule`.scene_id,`schedule`.state_detail,`schedule`.start_time,`schedule`.end_time,`schedule`.create_time,`schedule`.update_time,GROUP_CONCAT(CONCAT_WS(',',user_schedule.user_id) SEPARATOR ',') user_ids FROM schedule,user_schedule WHERE remind_day!='' and user_schedule.user_id = #{userId} and user_schedule.schedule_id=schedule.id GROUP BY `schedule`.id ")
+    List<Schedule> selectNotDefaultSchedulesByUser(String userId);
+
+    @Insert({
+            "<script>",
+            "insert into user_schedule(user_id,schedule_id) values ",
+            "<foreach collection='userSchedules' item='item' index='index' separator=','>",
+            "(#{item.userId}, #{item.scheduleId})",
+            "</foreach>",
+            "</script>"
+    })
+    int saveUserSchedule(@Param("userSchedules") List<UserSchedule> userSchedules);
+
+    @Delete("DELETE from user_schedule where schedule_id = #{scheduleId}")
+    void deleteUserSchedule(@Param("scheduleId") String scheduleId);
+
+    @Select("SELECT  schedule.`name`,schedule.id,`schedule`.device_config,`schedule`.remind_day,`schedule`.creator_id,`schedule`.scene_id,`schedule`.state_detail,`schedule`.start_time,`schedule`.end_time,`schedule`.create_time,`schedule`.update_time,GROUP_CONCAT(CONCAT_WS(',',user_schedule.user_id) SEPARATOR ',') user_ids FROM schedule,user_schedule WHERE schedule.remind_day='' and user_schedule.user_id = #{userId} and user_schedule.schedule_id=schedule.id and start_time>=#{startTime} and start_time<=#{endTime} and schedule.name like concat('%',#{name},'%') GROUP BY `schedule`.id \n")
+    List<Schedule> searchDefaultSchedulesByName(@Param("userId") String userId, @Param("startTime") Date startTime, @Param("endTime") Date endTime, @Param("name") String name);
+
+    @Select("SELECT  schedule.`name`,schedule.id,`schedule`.device_config,`schedule`.remind_day,`schedule`.creator_id,`schedule`.scene_id,`schedule`.state_detail,`schedule`.start_time,`schedule`.end_time,`schedule`.create_time,`schedule`.update_time,GROUP_CONCAT(CONCAT_WS(',',user_schedule.user_id) SEPARATOR ',') user_ids FROM schedule,user_schedule WHERE remind_day!='' and creator_id = #{userId} and user_schedule.schedule_id=schedule.id and schedule.name like concat('%',#{name},'%') GROUP BY `schedule`.id")
+    List<Schedule> searchNotDefaultSchedulesByName(@Param("userId") String userId, @Param("name") String name);
+
+    @Delete("DELETE from schedule where creator_id = #{userId}")
+    void deleteSchedulesByUserId(String userId);
+
+    @Delete("DELETE from user_schedule where user_id = #{userId}")
+    void deleteUserScheduleByUserId(String userId);
+}

+ 55 - 0
Server/distschedule-dao/src/main/java/com/example/distschedule/dao/mapper/UserMapper.java

@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2021 Huawei Device Co., Ltd.
+ * Licensed under the Apache License,Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ 
+package com.example.distschedule.dao.mapper;
+
+import com.example.distschedule.dao.model.User;
+import org.apache.ibatis.annotations.*;
+import org.springframework.stereotype.Component;
+
+import java.util.List;
+
+@Mapper
+@Component
+public interface UserMapper {
+
+    /**
+     * 根据手机查询用户
+     *
+     * @return 用户列表
+     */
+    @Select("SELECT * FROM user WHERE phone = #{phone}")
+    User selectUserByPhone(@Param("phone") String phone);
+
+    /**
+     * 创建用户
+     * @param user 新用户
+     * @return 是否创建成功
+     */
+    @Insert("INSERT INTO `user` (`id`,`phone`,`name`) VALUES (#{user.id},#{user.phone},#{user.name})")
+    int saveUser(@Param("user") User user);
+
+    /**
+     * 根据用户Id查询用户
+     *
+     * @return 用户列表
+     */
+    @Select("SELECT * FROM user WHERE id = #{userId}")
+    User selectUserByPId(String userId);
+
+    @Update("UPDATE user SET name = #{name} , update_time = NOW()  WHERE id = #{id}")
+    int updateUser(User user);
+}

+ 120 - 0
Server/distschedule-dao/src/main/java/com/example/distschedule/dao/model/Device.java

@@ -0,0 +1,120 @@
+/*
+ * Copyright (c) 2021 Huawei Device Co., Ltd.
+ * Licensed under the Apache License,Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ 
+package com.example.distschedule.dao.model;
+
+import java.util.Date;
+
+public class Device {
+    private String id;
+
+    private int typeId;
+
+    private String ownerId;
+
+    private String name;
+
+    private String state;
+
+    private Date createTime;
+
+    private Date updateTime;
+
+    private String ownerName;
+
+    private String ownerPhone;
+
+    private String property;
+
+    public String getId() {
+        return id;
+    }
+
+    public void setId(String id) {
+        this.id = id;
+    }
+
+    public int getTypeId() {
+        return typeId;
+    }
+
+    public void setTypeId(int typeId) {
+        this.typeId = typeId;
+    }
+
+    public String getOwnerId() {
+        return ownerId;
+    }
+
+    public void setOwnerId(String ownerId) {
+        this.ownerId = ownerId;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public String getState() {
+        return state;
+    }
+
+    public void setState(String state) {
+        this.state = state;
+    }
+
+    public Date getCreateTime() {
+        return createTime;
+    }
+
+    public void setCreateTime(Date createTime) {
+        this.createTime = createTime;
+    }
+
+    public Date getUpdateTime() {
+        return updateTime;
+    }
+
+    public void setUpdateTime(Date updateTime) {
+        this.updateTime = updateTime;
+    }
+
+    public String getOwnerName() {
+        return ownerName;
+    }
+
+    public void setOwnerName(String ownerName) {
+        this.ownerName = ownerName;
+    }
+
+    public String getOwnerPhone() {
+        return ownerPhone;
+    }
+
+    public void setOwnerPhone(String ownerPhone) {
+        this.ownerPhone = ownerPhone;
+    }
+
+    public String getProperty() {
+        return property;
+    }
+
+    public void setProperty(String property) {
+        this.property = property;
+    }
+}

+ 85 - 0
Server/distschedule-dao/src/main/java/com/example/distschedule/dao/model/DeviceType.java

@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2021 Huawei Device Co., Ltd.
+ * Licensed under the Apache License,Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ 
+package com.example.distschedule.dao.model;
+
+/**
+ * 设备类型
+ */
+public class DeviceType {
+    private int id;
+    private String name;
+    private String nameEn;
+    private String fields;
+    private String serviceId;
+    private String productId;
+    private int category;
+
+    public int getId() {
+        return id;
+    }
+
+    public void setId(int id) {
+        this.id = id;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public String getNameEn() {
+        return nameEn;
+    }
+
+    public void setNameEn(String nameEn) {
+        this.nameEn = nameEn;
+    }
+
+    public String getFields() {
+        return fields;
+    }
+
+    public void setFields(String fields) {
+        this.fields = fields;
+    }
+
+    public String getServiceId() {
+        return serviceId;
+    }
+
+    public void setServiceId(String serviceId) {
+        this.serviceId = serviceId;
+    }
+
+    public String getProductId() {
+        return productId;
+    }
+
+    public void setProductId(String productId) {
+        this.productId = productId;
+    }
+
+    public int getCategory() {
+        return category;
+    }
+
+    public void setCategory(int category) {
+        this.category = category;
+    }
+}

+ 70 - 0
Server/distschedule-dao/src/main/java/com/example/distschedule/dao/model/Family.java

@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2021 Huawei Device Co., Ltd.
+ * Licensed under the Apache License,Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ 
+package com.example.distschedule.dao.model;
+
+import java.util.Date;
+
+public class Family {
+    private String id;
+
+    private String name;
+
+    private String creatorId;
+
+    private Date createTime;
+
+    private Date updateTime;
+
+    public String getId() {
+        return id;
+    }
+
+    public void setId(String id) {
+        this.id = id;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public String getCreatorId() {
+        return creatorId;
+    }
+
+    public void setCreatorId(String creatorId) {
+        this.creatorId = creatorId;
+    }
+
+    public Date getCreateTime() {
+        return createTime;
+    }
+
+    public void setCreateTime(Date createTime) {
+        this.createTime = createTime;
+    }
+
+    public Date getUpdateTime() {
+        return updateTime;
+    }
+
+    public void setUpdateTime(Date updateTime) {
+        this.updateTime = updateTime;
+    }
+}

+ 140 - 0
Server/distschedule-dao/src/main/java/com/example/distschedule/dao/model/Schedule.java

@@ -0,0 +1,140 @@
+/*
+ * Copyright (c) 2021 Huawei Device Co., Ltd.
+ * Licensed under the Apache License,Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ 
+package com.example.distschedule.dao.model;
+
+import java.util.Date;
+
+public class Schedule {
+    private String id;
+
+    private String name;
+
+    private String deviceConfig;
+
+    private String remindDay;
+
+    private String creatorId;
+
+    private String sceneId;
+
+    private String stateDetail;
+
+    private Date startTime;
+
+    private Date endTime;
+
+    private Date createTime;
+
+    private Date updateTime;
+
+    private String userIds;
+
+    public String getId() {
+        return id;
+    }
+
+    public void setId(String id) {
+        this.id = id;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public String getDeviceConfig() {
+        return deviceConfig;
+    }
+
+    public void setDeviceConfig(String deviceConfig) {
+        this.deviceConfig = deviceConfig;
+    }
+
+    public String getRemindDay() {
+        return remindDay;
+    }
+
+    public void setRemindDay(String remindDay) {
+        this.remindDay = remindDay;
+    }
+
+    public String getCreatorId() {
+        return creatorId;
+    }
+
+    public void setCreatorId(String creatorId) {
+        this.creatorId = creatorId;
+    }
+
+    public String getSceneId() {
+        return sceneId;
+    }
+
+    public void setSceneId(String sceneId) {
+        this.sceneId = sceneId;
+    }
+
+    public String getStateDetail() {
+        return stateDetail;
+    }
+
+    public void setStateDetail(String stateDetail) {
+        this.stateDetail = stateDetail;
+    }
+
+    public Date getStartTime() {
+        return startTime;
+    }
+
+    public void setStartTime(Date startTime) {
+        this.startTime = startTime;
+    }
+
+    public Date getEndTime() {
+        return endTime;
+    }
+
+    public void setEndTime(Date endTime) {
+        this.endTime = endTime;
+    }
+
+    public Date getCreateTime() {
+        return createTime;
+    }
+
+    public void setCreateTime(Date createTime) {
+        this.createTime = createTime;
+    }
+
+    public Date getUpdateTime() {
+        return updateTime;
+    }
+
+    public void setUpdateTime(Date updateTime) {
+        this.updateTime = updateTime;
+    }
+
+    public String getUserIds() {
+        return userIds;
+    }
+
+    public void setUserIds(String userIds) {
+        this.userIds = userIds;
+    }
+}

+ 70 - 0
Server/distschedule-dao/src/main/java/com/example/distschedule/dao/model/User.java

@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2021 Huawei Device Co., Ltd.
+ * Licensed under the Apache License,Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ 
+package com.example.distschedule.dao.model;
+
+import java.util.Date;
+
+public class User {
+    private String id;
+
+    private String phone;
+
+    private String name;
+
+    private Date createTime;
+
+    private Date updateTime;
+
+    public String getId() {
+        return id;
+    }
+
+    public void setId(String id) {
+        this.id = id;
+    }
+
+    public String getPhone() {
+        return phone;
+    }
+
+    public void setPhone(String phone) {
+        this.phone = phone;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public Date getCreateTime() {
+        return createTime;
+    }
+
+    public void setCreateTime(Date createTime) {
+        this.createTime = createTime;
+    }
+
+    public Date getUpdateTime() {
+        return updateTime;
+    }
+
+    public void setUpdateTime(Date updateTime) {
+        this.updateTime = updateTime;
+    }
+}

+ 48 - 0
Server/distschedule-dao/src/main/java/com/example/distschedule/dao/model/UserDevice.java

@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2021 Huawei Device Co., Ltd.
+ * Licensed under the Apache License,Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ 
+package com.example.distschedule.dao.model;
+
+public class UserDevice {
+    private int id;
+
+    private String userId;
+
+    private String deviceId;
+
+    public int getId() {
+        return id;
+    }
+
+    public void setId(int id) {
+        this.id = id;
+    }
+
+    public String getUserId() {
+        return userId;
+    }
+
+    public void setUserId(String userId) {
+        this.userId = userId;
+    }
+
+    public String getDeviceId() {
+        return deviceId;
+    }
+
+    public void setDeviceId(String deviceId) {
+        this.deviceId = deviceId;
+    }
+}

+ 73 - 0
Server/distschedule-dao/src/main/java/com/example/distschedule/dao/model/UserFamily.java

@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2021 Huawei Device Co., Ltd.
+ * Licensed under the Apache License,Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ 
+package com.example.distschedule.dao.model;
+
+public class UserFamily {
+    private int id;
+    private String userId;
+    private String familyId;
+    private String role;
+    private String userPhone;
+    private String userName;
+
+    public int getId() {
+        return id;
+    }
+
+    public void setId(int id) {
+        this.id = id;
+    }
+
+    public String getUserId() {
+        return userId;
+    }
+
+    public void setUserId(String userId) {
+        this.userId = userId;
+    }
+
+    public String getFamilyId() {
+        return familyId;
+    }
+
+    public void setFamilyId(String familyId) {
+        this.familyId = familyId;
+    }
+
+    public String getRole() {
+        return role;
+    }
+
+    public void setRole(String role) {
+        this.role = role;
+    }
+
+    public String getUserPhone() {
+        return userPhone;
+    }
+
+    public void setUserPhone(String userPhone) {
+        this.userPhone = userPhone;
+    }
+
+    public String getUserName() {
+        return userName;
+    }
+
+    public void setUserName(String userName) {
+        this.userName = userName;
+    }
+}

+ 55 - 0
Server/distschedule-dao/src/main/java/com/example/distschedule/dao/model/UserSchedule.java

@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2021 Huawei Device Co., Ltd.
+ * Licensed under the Apache License,Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ 
+package com.example.distschedule.dao.model;
+
+public class UserSchedule {
+    private int id;
+    private String userId;
+    private String scheduleId;
+
+    public UserSchedule() {
+
+    }
+
+    public UserSchedule(String userId, String scheduleId) {
+        this.userId = userId;
+        this.scheduleId = scheduleId;
+    }
+
+    public int getId() {
+        return id;
+    }
+
+    public void setId(int id) {
+        this.id = id;
+    }
+
+    public String getUserId() {
+        return userId;
+    }
+
+    public void setUserId(String userId) {
+        this.userId = userId;
+    }
+
+    public String getScheduleId() {
+        return scheduleId;
+    }
+
+    public void setScheduleId(String scheduleId) {
+        this.scheduleId = scheduleId;
+    }
+}

+ 21 - 0
Server/distschedule-dao/src/main/resources/dao.properties

@@ -0,0 +1,21 @@
+#flyway config
+spring.flyway.enabled=true
+spring.flyway.locations=classpath:/db/migration
+
+#MyBatis Configuration
+mybatis.mapperLocations=classpath:mybatis/mapper/*.xml
+mybatis.typeAliasesPackage=com.huawei.build.dao.mapper
+mybatis.configuration.map-underscore-to-camel-case=true
+
+# Êý¾Ý¿âÁ¬½Ó³Ø
+spring.datasource.hikari.connection-test-query=SELECT 1 FROM DUAL
+spring.datasource.hikari.connection-timeout=30000
+spring.datasource.hikari.maximum-pool-size=100
+spring.datasource.hikari.max-lifetime=1800000
+spring.datasource.hikari.minimum-idle=20
+
+
+
+
+
+

+ 193 - 0
Server/distschedule-dao/src/main/resources/db/migration/V1__CreateTables.sql

@@ -0,0 +1,193 @@
+/*
+ * Copyright (c) 2021 Huawei Device Co., Ltd.
+ * Licensed under the Apache License,Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ 
+-- 用户表
+CREATE TABLE `user`
+(
+    `id`          CHAR(36)    NOT NULL,
+    `phone`       VARCHAR(16) NOT NULL,
+    `name`        VARCHAR(32),
+    `create_time` DATETIME DEFAULT NOW(),
+    `update_time` DATETIME DEFAULT NOW(),
+    UNIQUE KEY (phone),
+    PRIMARY KEY (`id`)
+)
+    COMMENT ='用户信息表'
+    ENGINE = InnoDB
+    DEFAULT CHARSET = utf8;
+
+-- 家庭表
+CREATE TABLE `family`
+(
+    `id`          CHAR(36) NOT NULL,
+    `name`        VARCHAR(32),
+    `creator_id`  CHAR(36) NOT NULL,
+    `create_time` DATETIME DEFAULT NOW(),
+    `update_time` DATETIME DEFAULT NOW(),
+    INDEX (creator_id),
+    PRIMARY KEY (`id`)
+)
+    COMMENT ='家庭信息表'
+    ENGINE = InnoDB
+    DEFAULT CHARSET = utf8;
+
+-- 用户家庭表
+CREATE TABLE `user_family`
+(
+    `id`        int         NOT NULL AUTO_INCREMENT,
+    `user_id`   CHAR(36)    NOT NULL,
+    `family_id` CHAR(36)    NOT NULL,
+    `role`      VARCHAR(32) NOT NULL,
+    INDEX (user_id),
+    INDEX (family_id),
+    PRIMARY KEY (`id`)
+)
+    COMMENT ='用户家庭表'
+    ENGINE = InnoDB
+    DEFAULT CHARSET = utf8;
+
+-- 设备表
+CREATE TABLE `device`
+(
+    `id`          VARCHAR(70) NOT NULL,
+    `type_id`     int         NOT NULL,
+    `owner_id`    CHAR(36)    NOT NULL,
+    `name`        VARCHAR(32) NOT NULL,
+    `state`       VARCHAR(32) NOT NULL,
+    `property`    VARCHAR(2048),
+    `create_time` DATETIME DEFAULT NOW(),
+    `update_time` DATETIME DEFAULT NOW(),
+    INDEX (type_id),
+    INDEX (owner_id),
+    PRIMARY KEY (`id`)
+)
+    COMMENT ='设备表'
+    ENGINE = InnoDB
+    DEFAULT CHARSET = utf8;
+
+-- 用户设备表
+CREATE TABLE `user_device`
+(
+    `id`        int      NOT NULL AUTO_INCREMENT,
+    `user_id`   CHAR(36) NOT NULL,
+    `device_id` VARCHAR(70) NOT NULL,
+    INDEX (user_id),
+    INDEX (device_id),
+    PRIMARY KEY (`id`)
+)
+    COMMENT ='用户设备表'
+    ENGINE = InnoDB
+    DEFAULT CHARSET = utf8;
+
+-- 设备类型表
+CREATE TABLE `device_type`
+(
+    `id`         int         NOT NULL AUTO_INCREMENT,
+    `name`       VARCHAR(32) NOT NULL,
+    `name_en`    VARCHAR(32) NOT NULL,
+    `fields`     VARCHAR(32),
+    `service_id` VARCHAR(32) NOT NULL,
+    `product_id` VARCHAR(32) NOT NULL,
+    `category`   int         NOT NULL,
+    PRIMARY KEY (`id`)
+)
+    COMMENT ='设备类型表'
+    ENGINE = InnoDB
+    DEFAULT CHARSET = utf8;
+
+-- 场景表
+CREATE TABLE `scene`
+(
+    `id`          CHAR(36) NOT NULL,
+    `name`        VARCHAR(32),
+    `create_time` DATETIME DEFAULT NOW(),
+    `update_time` DATETIME DEFAULT NOW(),
+    PRIMARY KEY (`id`)
+)
+    COMMENT ='场景表'
+    ENGINE = InnoDB
+    DEFAULT CHARSET = utf8;
+
+-- 日程表
+CREATE TABLE `schedule`
+(
+    `id`            CHAR(36) NOT NULL,
+    `name`          VARCHAR(32),
+    `start_time`    DATETIME NOT NULL,
+    `end_time`      DATETIME NOT NULL,
+    `device_config` TEXT,
+    `remind_day`    VARCHAR(16),
+    `state_detail`  TEXT,
+    `scene_id`      CHAR(36) NOT NULL,
+    `creator_id`    CHAR(36) NOT NULL,
+    `create_time`   DATETIME DEFAULT NOW(),
+    `update_time`   DATETIME DEFAULT NOW(),
+    INDEX (creator_id),
+    PRIMARY KEY (`id`)
+)
+    COMMENT ='日程表'
+    ENGINE = InnoDB
+    DEFAULT CHARSET = utf8;
+
+-- 用户日程表
+CREATE TABLE `user_schedule`
+(
+    `id`          int      NOT NULL AUTO_INCREMENT,
+    `user_id`     CHAR(36) NOT NULL,
+    `schedule_id` CHAR(36) NOT NULL,
+    INDEX (user_id),
+    INDEX (schedule_id),
+    PRIMARY KEY (`id`)
+)
+    COMMENT ='用户日程表'
+    ENGINE = InnoDB
+    DEFAULT CHARSET = utf8;
+
+INSERT INTO device_type (`name`, `name_en`, `service_id`, `product_id`, `category`)
+values ('台灯', 'lamp', 'SmartLamp', '6128c7b60ad1ed0286680f19', 1);
+INSERT INTO device_type (`name`, `name_en`, `service_id`, `product_id`, `category`)
+values ('扫地机器人', 'sweepingRobot', 'CleanData', '61371e952cce4f0286262229', 1);
+INSERT INTO device_type (`name`, `name_en`, `service_id`, `product_id`, `category`)
+values ('自动浇花', 'smartWater', 'SmartWatering', '6135e8fc2cce4f028625ccc9', 1);
+INSERT INTO device_type (`name`, `name_en`, `service_id`, `product_id`, `category`)
+values ('自动窗帘', 'smartCurtain', 'SmartCurtain', '6136ceba0ad1ed02866fa3b2', 1);
+INSERT INTO device_type (`name`, `name_en`, `service_id`, `product_id`, `category`)
+values ('手机', 'smartPhone', 'smartPhone', '00000', 0);
+INSERT INTO device_type (`name`, `name_en`, `service_id`, `product_id`, `category`)
+values ('手表', 'smartWatch', 'smartWatch', '00000', 0);
+INSERT INTO device_type (`name`, `name_en`, `service_id`, `product_id`, `category`)
+values ('平板', 'smartTablet', 'smartTablet', '00000', 0);
+INSERT INTO device_type (`name`, `name_en`, `service_id`, `product_id`, `category`)
+values ('烟感报警器', 'smokeDetector', 'SmartSmokeSensation', '6128bdd12cce4f02861e6d98', 3);
+INSERT INTO device_type (`name`, `name_en`, `service_id`, `product_id`, `category`)
+values ('可燃气体报警器', 'flammableGas', 'GasDetection', '61445c41f74139027d2233e5', 3);
+INSERT INTO device_type (`name`, `name_en`, `service_id`, `product_id`, `category`)
+values ('甲醛报警器', 'formaldehydeAlarm', 'TVOCDetection', '61445cdadcbb87027db095d1', 3);
+INSERT INTO device_type (`name`, `name_en`, `service_id`, `product_id`, `category`)
+values ('智能垃圾箱', 'smartTrashCan', 'Trashcan', '614456cff74139027d2230ec', 3);
+INSERT INTO device_type (`name`, `name_en`, `service_id`, `product_id`, `category`)
+values ('儿童手表', 'childrenWatch', 'SmartWatch', '613ad28b0109930287315230', 0);
+INSERT INTO device_type (`name`, `name_en`, `service_id`, `product_id`, `category`)
+values ('智能风扇', 'fan', 'SmartFan', '6150601d88056b027dd2ca47', 1);
+INSERT INTO device_type (`name`, `name_en`, `service_id`, `product_id`, `category`)
+values ('智能门锁', 'SmartLock', 'SmartLock', '61b2b4514d9b020287fcf9b2', 4);
+INSERT INTO device_type (`name`, `name_en`, `service_id`, `product_id`, `category`)
+values ('电子称', 'ElectronicScale', 'SmartElectronicScale', '61b738902b2aa20288b92ad4', 4);
+INSERT INTO device_type (`name`, `name_en`, `service_id`, `product_id`, `category`)
+values ('智能加湿器', 'SmartHumidifier', 'SmartHumidifier', '61b953b52b2aa20288c07fa9', 4);
+INSERT INTO device_type (`name`, `name_en`, `service_id`, `product_id`, `category`)
+values ('智能门铃', 'SmartDoorBell', 'DoorBell', '61a9b55ad28ce30288415f95', 3);
+INSERT INTO device_type (`name`, `name_en`, `service_id`, `product_id`, `category`)
+values ('一氧化碳气体报警器', 'coSensation', 'SmartCoSensation', '61de758bc7fb24029b0be6f0', 3);

+ 119 - 0
Server/distschedule-dao/src/main/resources/generatorConfig.xml

@@ -0,0 +1,119 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE generatorConfiguration
+        PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
+        "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
+<generatorConfiguration>
+    <context id="test" targetRuntime="MyBatis3">
+        <plugin type="org.mybatis.generator.plugins.EqualsHashCodePlugin"/>
+        <plugin type="org.mybatis.generator.plugins.SerializablePlugin"/>
+        <plugin type="org.mybatis.generator.plugins.ToStringPlugin"/>
+        <!-- 数据Model属性对应Column获取插件 -->
+        <plugin type="com.itfsw.mybatis.generator.plugins.ModelColumnPlugin"/>
+        <!-- Mapper注解插件 -->
+        <plugin type="com.itfsw.mybatis.generator.plugins.MapperAnnotationPlugin">
+            <!-- @Mapper 默认开启 -->
+            <property name="@Mapper" value="true"/>
+            <!-- @Repository 默认关闭,开启后解决IDEA工具@Autowired报错 -->
+            <property name="@Repository" value="true"/>
+        </plugin>
+        <!-- 乐观锁插件 -->
+        <plugin type="com.itfsw.mybatis.generator.plugins.OptimisticLockerPlugin">
+            <!-- 是否启用自定义nextVersion,默认不启用(插件会默认使用sql的 set column = column + 1) -->
+            <property name="customizedNextVersion" value="false"/>
+        </plugin>
+        <!-- MySQL分页插件 -->
+        <plugin type="com.itfsw.mybatis.generator.plugins.LimitPlugin">
+            <!-- 通过配置startPage影响Example中的page方法开始分页的页码,默认分页从0开始 -->
+            <property name="startPage" value="0"/>
+        </plugin>
+        <!-- 数据Model链式构建插件 -->
+        <plugin type="com.itfsw.mybatis.generator.plugins.ModelBuilderPlugin"/>
+        <!-- Example Criteria 增强插件 -->
+        <plugin type="com.itfsw.mybatis.generator.plugins.ExampleEnhancedPlugin">
+            <!-- 是否支持已经过时的andIf方法(推荐使用when代替),默认支持 -->
+            <property name="enableAndIf" value="true"/>
+        </plugin>
+        <!-- 批量插入插件 -->
+        <plugin type="com.itfsw.mybatis.generator.plugins.BatchInsertPlugin">
+            <!--
+            开启后可以实现官方插件根据属性是否为空决定是否插入该字段功能
+            !需开启allowMultiQueries=true多条sql提交操作,所以不建议使用!插件默认不开启
+            -->
+            <property name="allowMultiQueries" value="false"/>
+        </plugin>
+        <!-- 查询结果选择性返回插件 -->
+        <plugin type="com.itfsw.mybatis.generator.plugins.SelectSelectivePlugin"/>
+        <!-- 查询单条数据插件 -->
+        <plugin type="com.itfsw.mybatis.generator.plugins.SelectOneByExamplePlugin"/>
+        <!-- 增量插件 -->
+        <plugin type="com.itfsw.mybatis.generator.plugins.IncrementPlugin"/>
+        <commentGenerator>
+            <!-- 这个元素用来去除指定生成的注释中是否包含生成的日期 false:表示保护 -->
+            <!-- 如果生成日期,会造成即使修改一个字段,整个实体类所有属性都会发生变化,不利于版本控制,所以设置为true -->
+            <property name="suppressDate" value="true"/>
+            <!-- 是否去除自动生成的注释 true:是 : false:否 -->
+            <property name="suppressAllComments" value="true"/>
+        </commentGenerator>
+        <!--数据库链接URL,用户名、密码 -->
+        <jdbcConnection
+                driverClass="com.mysql.jdbc.Driver"
+                connectionURL="jdbc:mysql://qeesung.host.huawei.com/build_project_qeesung_as_code"
+                userId="root" password="">
+            <property name="nullCatalogMeansCurrent" value="true"/>
+        </jdbcConnection>
+        <javaTypeResolver>
+            <!-- This property is used to specify whether MyBatis Generator should
+                force the use of java.math.BigDecimal for DECIMAL and NUMERIC fields, -->
+            <property name="forceBigDecimals" value="false"/>
+        </javaTypeResolver>
+        <!-- 生成模型的包名和位置 -->
+        <javaModelGenerator targetPackage="com.huawei.build.dao.model" targetProject="target">
+            <property name="enableSubPackages" value="true"/>
+            <property name="trimStrings" value="true"/>
+        </javaModelGenerator>
+        <!-- 生成映射文件的包名和位置 -->
+        <sqlMapGenerator targetPackage="com.huawei.build.dao.dao" targetProject="target">
+            <property name="enableSubPackages" value="true"/>
+        </sqlMapGenerator>
+        <!-- 生成DAO的包名和位置 -->
+        <javaClientGenerator type="XMLMAPPER" targetPackage="com.huawei.build.dao.mapper" targetProject="target">
+            <property name="enableSubPackages" value="true"/>
+        </javaClientGenerator>
+
+        <table tableName="build_record">
+            <property name="versionColumn" value="version"/>
+            <property name="rootClass" value="com.huawei.build.dao.model.BaseRecord"/>
+            <columnOverride column="trigger_script" jdbcType="VARCHAR"/>
+            <columnOverride column="execution_script" jdbcType="VARCHAR"/>
+            <columnOverride column="parameters" jdbcType="VARCHAR"/>
+            <columnOverride column="err_msg" jdbcType="VARCHAR"/>
+        </table>
+        <table tableName="build_record_env">
+        </table>
+        <table tableName="build_record_depend">
+        </table>
+        <table tableName="build_step_record">
+            <property name="versionColumn" value="version"/>
+            <property name="rootClass" value="com.huawei.build.dao.model.BaseRecord"/>
+            <columnOverride column="parameters" jdbcType="VARCHAR"/>
+        </table>
+        <table tableName="build_stage_record">
+            <property name="versionColumn" value="version"/>
+            <property name="rootClass" value="com.huawei.build.dao.model.BaseRecord"/>
+        </table>
+        <table tableName="project">
+            <!-- 指定版本列 -->
+            <property name="versionColumn" value="cas_version"/>
+            <property name="incrementColumns" value="build_no"/>
+        </table>
+        <table tableName="project_history_msg">
+            <columnOverride column="content" jdbcType="VARCHAR"/>
+        </table>
+        <table tableName="build_record_playbook">
+        </table>
+        <table tableName="build_record_mr">
+        </table>
+        <table tableName="project_starred">
+        </table>
+    </context>
+</generatorConfiguration>

+ 0 - 0
Server/distschedule-dao/src/test/java/.gitkeep


+ 101 - 0
Server/distschedule-service/pom.xml

@@ -0,0 +1,101 @@
+<?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">
+    <parent>
+        <artifactId>distschedule</artifactId>
+        <groupId>com.example.distschedule</groupId>
+        <version>1.0.0-SNAPSHOT</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>distschedule-service</artifactId>
+    <description>服务模块,该模块主要负责对业务逻辑的处理</description>
+
+    <properties>
+        <jackson.version>2.10.4</jackson.version>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-aop</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-thymeleaf</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-web</artifactId>
+        </dependency>
+<!--
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-security</artifactId>
+        </dependency>-->
+        <dependency>
+            <groupId>com.example.distschedule</groupId>
+            <artifactId>distschedule-dao</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.httpcomponents</groupId>
+            <artifactId>httpclient</artifactId>
+            <version>4.5.10</version>
+        </dependency>
+        <!--        only added for test-->
+        <dependency>
+            <groupId>org.apache.httpcomponents</groupId>
+            <artifactId>httpclient</artifactId>
+            <version>4.5.10</version>
+            <classifier>tests</classifier>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>com.fasterxml.jackson.core</groupId>
+            <artifactId>jackson-databind</artifactId>
+            <version>${jackson.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>com.fasterxml.jackson.core</groupId>
+            <artifactId>jackson-core</artifactId>
+            <version>${jackson.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>com.fasterxml.jackson.core</groupId>
+            <artifactId>jackson-annotations</artifactId>
+            <version>${jackson.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>com.fasterxml.jackson.dataformat</groupId>
+            <artifactId>jackson-dataformat-yaml</artifactId>
+            <version>${jackson.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.commons</groupId>
+            <artifactId>commons-jexl3</artifactId>
+            <version>3.1</version>
+        </dependency>
+        <dependency>
+            <groupId>ognl</groupId>
+            <artifactId>ognl</artifactId>
+            <version>3.1.19</version>
+        </dependency>
+        <dependency>
+            <groupId>org.dom4j</groupId>
+            <artifactId>dom4j</artifactId>
+            <version>2.1.3</version>
+        </dependency>
+
+        <dependency>
+            <groupId>com.huaweicloud.sdk</groupId>
+            <artifactId>huaweicloud-sdk-core</artifactId>
+            <version>3.0.57</version>
+        </dependency>
+        <dependency>
+            <groupId>com.huaweicloud.sdk</groupId>
+            <artifactId>huaweicloud-sdk-iotda</artifactId>
+            <version>3.0.57</version>
+        </dependency>
+    </dependencies>
+</project>

+ 166 - 0
Server/distschedule-service/src/main/java/com/example/distschedule/config/RestClient.java

@@ -0,0 +1,166 @@
+/*
+ * Copyright (c) 2021 Huawei Device Co., Ltd.
+ * Licensed under the Apache License,Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ 
+package com.example.distschedule.config;
+
+
+import org.apache.http.HttpEntityEnclosingRequest;
+import org.apache.http.HttpRequest;
+import org.apache.http.NoHttpResponseException;
+import org.apache.http.client.HttpRequestRetryHandler;
+import org.apache.http.client.config.RequestConfig;
+import org.apache.http.client.protocol.HttpClientContext;
+import org.apache.http.config.Registry;
+import org.apache.http.config.RegistryBuilder;
+import org.apache.http.conn.socket.ConnectionSocketFactory;
+import org.apache.http.conn.socket.PlainConnectionSocketFactory;
+import org.apache.http.conn.ssl.NoopHostnameVerifier;
+import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClients;
+import org.apache.http.impl.client.StandardHttpRequestRetryHandler;
+import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
+import org.apache.http.ssl.SSLContextBuilder;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.http.MediaType;
+import org.springframework.http.client.ClientHttpRequestFactory;
+import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
+import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
+import org.springframework.web.client.RestTemplate;
+
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLException;
+import javax.net.ssl.SSLHandshakeException;
+import java.io.InterruptedIOException;
+import java.net.UnknownHostException;
+import java.security.GeneralSecurityException;
+import java.util.ArrayList;
+import java.util.List;
+
+@Configuration
+public class RestClient {
+
+    @Value("${service.maxTotal:2000}")
+    private int maxTotal = 2000;
+
+    @Value("${service.defaultMaxPerRoute:400}")
+    private int defaultMaxPerRoute = 400;
+
+    private static final Logger logger = LoggerFactory.getLogger(RestClient.class);
+
+    // 默认连接超时,10秒
+    @Value("${service.connectionTimeout:300000}")
+    private int CONNECT_TIMEOUT = 300000;
+
+    // 默认读取数据超时,修改为120秒
+    @Value("${service.readTimeout:12000000}")
+    private int READ_TIMEOUT = 120000;
+
+    @Bean(name = "httpsRestTemplate")
+    public RestTemplate serviceHttpsRestTemplate() {
+        RestTemplate restTemplate = new RestTemplate(clientHttpRequestFactory(true));
+        restTemplate.getMessageConverters().add(new WxMappingJackson2HttpMessageConverter());
+        return restTemplate;
+    }
+
+    @Bean(name = "httpRestTemplate")
+    public RestTemplate serviceHttpRestTemplate() {
+        return new RestTemplate(clientHttpRequestFactory(false));
+    }
+
+    private class WxMappingJackson2HttpMessageConverter extends MappingJackson2HttpMessageConverter {
+        WxMappingJackson2HttpMessageConverter() {
+            List<MediaType> mediaTypes = new ArrayList<>();
+            mediaTypes.add(MediaType.APPLICATION_JSON_UTF8);
+            setSupportedMediaTypes(mediaTypes);
+        }
+    }
+
+    private ClientHttpRequestFactory clientHttpRequestFactory(boolean useHttps) {
+        HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory();
+        factory.setReadTimeout(READ_TIMEOUT);
+        factory.setConnectTimeout(CONNECT_TIMEOUT);
+
+        if (useHttps) {
+            factory.setHttpClient(createHttpsClient());
+        }
+        return factory;
+    }
+
+    private CloseableHttpClient createHttpsClient() {
+        try {
+            SSLContext sslContext = new SSLContextBuilder().loadTrustMaterial(null, (chain, authType) -> true).build();
+
+            SSLConnectionSocketFactory sslsf =
+                    new SSLConnectionSocketFactory(sslContext, new String[]{"TLSv1.2", "TLSv1.1", "TLSv1", "SSLv3"}, null, NoopHostnameVerifier.INSTANCE);
+
+            Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory>create()
+                    .register("http", PlainConnectionSocketFactory.getSocketFactory()).register("https", sslsf).build();
+
+            PoolingHttpClientConnectionManager pccm = new PoolingHttpClientConnectionManager(socketFactoryRegistry);
+            logger.info("set maxTotal:" + this.maxTotal);
+            logger.info("set defaultMaxPerRoute:" + this.defaultMaxPerRoute);
+            // 连接池最大并发连接数
+            pccm.setMaxTotal(this.maxTotal);
+            // 单路由最大并发数
+            pccm.setDefaultMaxPerRoute(this.defaultMaxPerRoute);
+            pccm.setValidateAfterInactivity(2000);
+            RequestConfig params = RequestConfig.custom().setConnectTimeout(CONNECT_TIMEOUT)
+                    .setConnectionRequestTimeout(5000).setSocketTimeout(READ_TIMEOUT).build();
+
+            HttpRequestRetryHandler httpRequestRetryHandler = (exception, executionCount, context) -> {
+
+                // 如果已经重试了3次,就放弃
+                if (executionCount >= 1) {
+                    return false;
+                }
+                // 如果服务器丢掉了连接,那么就重试
+                if (exception instanceof NoHttpResponseException) {
+                    return true;
+                }
+                // 不要重试SSL握手异常
+                if (exception instanceof SSLHandshakeException) {
+                    return false;
+                }
+                // 超时
+                if (exception instanceof InterruptedIOException) {
+                    return false;
+                }
+                // 目标服务器不可达
+                if (exception instanceof UnknownHostException) {
+                    return false;
+                }
+                // ssl握手异常
+                if (exception instanceof SSLException) {
+                    return false;
+                }
+
+                HttpClientContext clientContext = HttpClientContext.adapt(context);
+                HttpRequest request = clientContext.getRequest();
+                // 如果请求是幂等的,就再次尝试
+                return !(request instanceof HttpEntityEnclosingRequest);
+            };
+            StandardHttpRequestRetryHandler standardHttpRequestRetryHandler = new StandardHttpRequestRetryHandler();
+            return HttpClients.custom().setConnectionManager(pccm).setDefaultRequestConfig(params)
+                    .setRetryHandler(standardHttpRequestRetryHandler).setConnectionManagerShared(true).build();
+        } catch (GeneralSecurityException e) {
+            throw new RuntimeException("create https client error.", e);
+        }
+    }
+}

+ 58 - 0
Server/distschedule-service/src/main/java/com/example/distschedule/config/ServiceConfig.java

@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2021 Huawei Device Co., Ltd.
+ * Licensed under the Apache License,Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ 
+package com.example.distschedule.config;
+
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Profile;
+import org.springframework.context.annotation.PropertySource;
+
+@Configuration
+public class ServiceConfig {
+    @Configuration
+    @PropertySource("classpath:service.properties")
+    static class Defaults {
+    }
+
+    @Configuration
+    @Profile("dev")
+    @PropertySource({"classpath:service.properties", "classpath:service-dev.properties"})
+    static class Dev {
+    }
+
+    @Configuration
+    @Profile("alpha")
+    @PropertySource({"classpath:service.properties", "classpath:service-alpha.properties"})
+    static class Alpha {
+    }
+
+    @Configuration
+    @Profile("beta")
+    @PropertySource({"classpath:service.properties", "classpath:service-beta.properties"})
+    static class Beta {
+    }
+
+    @Configuration
+    @Profile("gamma")
+    @PropertySource({"classpath:service.properties", "classpath:service-gamma.properties"})
+    static class Gamma {
+    }
+
+    @Configuration
+    @Profile("prod")
+    @PropertySource({"classpath:service.properties", "classpath:service-prod.properties"})
+    static class Prod {
+    }
+}

+ 69 - 0
Server/distschedule-service/src/main/java/com/example/distschedule/dto/CreateDeviceDto.java

@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2021 Huawei Device Co., Ltd.
+ * Licensed under the Apache License,Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ 
+package com.example.distschedule.dto;
+
+
+public class CreateDeviceDto {
+    private String id;
+
+    private int typeId;
+
+    private String ownerId;
+
+    private String name;
+
+    private String secret;
+
+    public String getId() {
+        return id;
+    }
+
+    public void setId(String id) {
+        this.id = id;
+    }
+
+    public int getTypeId() {
+        return typeId;
+    }
+
+    public void setTypeId(int typeId) {
+        this.typeId = typeId;
+    }
+
+    public String getOwnerId() {
+        return ownerId;
+    }
+
+    public void setOwnerId(String ownerId) {
+        this.ownerId = ownerId;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public String getSecret() {
+        return secret;
+    }
+
+    public void setSecret(String secret) {
+        this.secret = secret;
+    }
+}

+ 110 - 0
Server/distschedule-service/src/main/java/com/example/distschedule/dto/CreateScheduleDto.java

@@ -0,0 +1,110 @@
+/*
+ * Copyright (c) 2021 Huawei Device Co., Ltd.
+ * Licensed under the Apache License,Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ 
+package com.example.distschedule.dto;
+
+import java.util.Date;
+
+public class CreateScheduleDto {
+    private String name;
+
+    private String userIds;
+
+    private String deviceConfig;
+
+    private String remindDay;
+
+    private String creatorId;
+
+    private String sceneId;
+
+    private String stateDetail;
+
+    private Date startTime;
+
+    private Date endTime;
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public String getUserIds() {
+        return userIds;
+    }
+
+    public void setUserIds(String userIds) {
+        this.userIds = userIds;
+    }
+
+    public String getDeviceConfig() {
+        return deviceConfig;
+    }
+
+    public void setDeviceConfig(String deviceConfig) {
+        this.deviceConfig = deviceConfig;
+    }
+
+    public String getRemindDay() {
+        return remindDay;
+    }
+
+    public void setRemindDay(String remindDay) {
+        this.remindDay = remindDay;
+    }
+
+    public String getCreatorId() {
+        return creatorId;
+    }
+
+    public void setCreatorId(String creatorId) {
+        this.creatorId = creatorId;
+    }
+
+    public String getSceneId() {
+        return sceneId;
+    }
+
+    public void setSceneId(String sceneId) {
+        this.sceneId = sceneId;
+    }
+
+    public String getStateDetail() {
+        return stateDetail;
+    }
+
+    public void setStateDetail(String stateDetail) {
+        this.stateDetail = stateDetail;
+    }
+
+    public Date getStartTime() {
+        return startTime;
+    }
+
+    public void setStartTime(Date startTime) {
+        this.startTime = startTime;
+    }
+
+    public Date getEndTime() {
+        return endTime;
+    }
+
+    public void setEndTime(Date endTime) {
+        this.endTime = endTime;
+    }
+}

+ 51 - 0
Server/distschedule-service/src/main/java/com/example/distschedule/dto/DeviceCommandDto.java

@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2021 Huawei Device Co., Ltd.
+ * Licensed under the Apache License,Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ 
+package com.example.distschedule.dto;
+
+/**
+ * 给IOT云发送命令
+ */
+public class DeviceCommandDto {
+    private String commandName;
+
+    private String serviceId;
+
+    private String value;
+
+    public String getCommandName() {
+        return commandName;
+    }
+
+    public void setCommandName(String commandName) {
+        this.commandName = commandName;
+    }
+
+    public String getServiceId() {
+        return serviceId;
+    }
+
+    public void setServiceId(String serviceId) {
+        this.serviceId = serviceId;
+    }
+
+    public String getValue() {
+        return value;
+    }
+
+    public void setValue(String value) {
+        this.value = value;
+    }
+}

+ 138 - 0
Server/distschedule-service/src/main/java/com/example/distschedule/dto/DeviceDto.java

@@ -0,0 +1,138 @@
+/*
+ * Copyright (c) 2021 Huawei Device Co., Ltd.
+ * Licensed under the Apache License,Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ 
+package com.example.distschedule.dto;
+
+import com.example.distschedule.dao.model.Device;
+
+import java.util.Date;
+
+public class DeviceDto {
+    private String id;
+
+    private int typeId;
+
+    private String ownerId;
+
+    private String name;
+
+    private String state;
+
+    private Date createTime;
+
+    private Date updateTime;
+
+    private String ownerName;
+
+    private String ownerPhone;
+
+    private String property;
+
+    public DeviceDto() {
+    }
+
+    public DeviceDto(Device device) {
+        this.id = device.getId();
+        this.name = device.getName();
+        this.ownerId = device.getOwnerId();
+        this.state = device.getState();
+        this.typeId = device.getTypeId();
+        this.createTime = device.getCreateTime();
+        this.updateTime = device.getUpdateTime();
+        this.ownerName = device.getOwnerName();
+        this.ownerPhone = device.getOwnerPhone();
+        this.property = device.getProperty();
+    }
+
+    public String getId() {
+        return id;
+    }
+
+    public void setId(String id) {
+        this.id = id;
+    }
+
+    public int getTypeId() {
+        return typeId;
+    }
+
+    public void setTypeId(int typeId) {
+        this.typeId = typeId;
+    }
+
+    public String getOwnerId() {
+        return ownerId;
+    }
+
+    public void setOwnerId(String ownerId) {
+        this.ownerId = ownerId;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public String getState() {
+        return state;
+    }
+
+    public void setState(String state) {
+        this.state = state;
+    }
+
+    public Date getCreateTime() {
+        return createTime;
+    }
+
+    public void setCreateTime(Date createTime) {
+        this.createTime = createTime;
+    }
+
+    public Date getUpdateTime() {
+        return updateTime;
+    }
+
+    public void setUpdateTime(Date updateTime) {
+        this.updateTime = updateTime;
+    }
+
+    public String getOwnerName() {
+        return ownerName;
+    }
+
+    public void setOwnerName(String ownerName) {
+        this.ownerName = ownerName;
+    }
+
+    public String getOwnerPhone() {
+        return ownerPhone;
+    }
+
+    public void setOwnerPhone(String ownerPhone) {
+        this.ownerPhone = ownerPhone;
+    }
+
+    public String getProperty() {
+        return property;
+    }
+
+    public void setProperty(String property) {
+        this.property = property;
+    }
+}

+ 98 - 0
Server/distschedule-service/src/main/java/com/example/distschedule/dto/DeviceTypeDto.java

@@ -0,0 +1,98 @@
+/*
+ * Copyright (c) 2021 Huawei Device Co., Ltd.
+ * Licensed under the Apache License,Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ 
+package com.example.distschedule.dto;
+
+import com.example.distschedule.dao.model.DeviceType;
+
+public class DeviceTypeDto {
+    private int id;
+    private String name;
+    private String nameEn;
+    private String fields;
+    private String serviceId;
+    private String productId;
+    private int category;
+
+    public DeviceTypeDto() {
+
+    }
+
+    public DeviceTypeDto(DeviceType deviceType) {
+        this.id = deviceType.getId();
+        this.name = deviceType.getName();
+        this.nameEn = deviceType.getNameEn();
+        this.fields = deviceType.getFields();
+        this.serviceId = deviceType.getServiceId();
+        this.productId = deviceType.getProductId();
+        this.category = deviceType.getCategory();
+    }
+
+    public int getId() {
+        return id;
+    }
+
+    public void setId(int id) {
+        this.id = id;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public String getNameEn() {
+        return nameEn;
+    }
+
+    public void setNameEn(String nameEn) {
+        this.nameEn = nameEn;
+    }
+
+    public String getFields() {
+        return fields;
+    }
+
+    public void setFields(String fields) {
+        this.fields = fields;
+    }
+
+    public String getServiceId() {
+        return serviceId;
+    }
+
+    public void setServiceId(String serviceId) {
+        this.serviceId = serviceId;
+    }
+
+    public String getProductId() {
+        return productId;
+    }
+
+    public void setProductId(String productId) {
+        this.productId = productId;
+    }
+
+    public int getCategory() {
+        return category;
+    }
+
+    public void setCategory(int category) {
+        this.category = category;
+    }
+}

+ 31 - 0
Server/distschedule-service/src/main/java/com/example/distschedule/dto/FamilyCreateDto.java

@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2021 Huawei Device Co., Ltd.
+ * Licensed under the Apache License,Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ 
+package com.example.distschedule.dto;
+
+import javax.validation.constraints.Size;
+
+public class FamilyCreateDto {
+    @Size(min = 1, max = 32, message = "illegal name size")
+    private String name;
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+}

+ 93 - 0
Server/distschedule-service/src/main/java/com/example/distschedule/dto/FamilyDto.java

@@ -0,0 +1,93 @@
+/*
+ * Copyright (c) 2021 Huawei Device Co., Ltd.
+ * Licensed under the Apache License,Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ 
+package com.example.distschedule.dto;
+
+import com.example.distschedule.dao.model.Family;
+
+import java.util.Date;
+
+public class FamilyDto {
+    private String id;
+
+    private String name;
+
+    private String creatorId;
+
+    private Date createTime;
+
+    private Date updateTime;
+
+    private boolean isCreator;
+
+    public FamilyDto() {
+    }
+
+    public FamilyDto(Family family) {
+        this.id = family.getId();
+        this.name = family.getName();
+        this.creatorId = family.getCreatorId();
+        this.createTime = family.getCreateTime();
+        this.updateTime = family.getUpdateTime();
+    }
+
+    public String getId() {
+        return id;
+    }
+
+    public void setId(String id) {
+        this.id = id;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public String getCreatorId() {
+        return creatorId;
+    }
+
+    public void setCreatorId(String creatorId) {
+        this.creatorId = creatorId;
+    }
+
+    public Date getCreateTime() {
+        return createTime;
+    }
+
+    public void setCreateTime(Date createTime) {
+        this.createTime = createTime;
+    }
+
+    public Date getUpdateTime() {
+        return updateTime;
+    }
+
+    public void setUpdateTime(Date updateTime) {
+        this.updateTime = updateTime;
+    }
+
+    public boolean isCreator() {
+        return isCreator;
+    }
+
+    public void setCreator(boolean creator) {
+        isCreator = creator;
+    }
+}

+ 70 - 0
Server/distschedule-service/src/main/java/com/example/distschedule/dto/ResponseResult.java

@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2021 Huawei Device Co., Ltd.
+ * Licensed under the Apache License,Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ 
+package com.example.distschedule.dto;
+
+import com.example.distschedule.error.ErrorCode;
+import com.example.distschedule.exception.DistscheduleBaseException;
+import com.example.distschedule.exception.DistscheduleFamilyException;
+
+public class ResponseResult<T> {
+    private final boolean success;
+    private final String message;
+    private final String errCode;
+    private final T result;
+
+    private ResponseResult(boolean success, String message, String errCode, T result) {
+        this.success = success;
+        this.message = message;
+        this.errCode = errCode;
+        this.result = result;
+    }
+
+    public static <E> ResponseResult<E> success(E results) {
+        return new ResponseResult<>(true, "", "", results);
+    }
+
+    public static <E> ResponseResult<E> fail(String message) {
+        return new ResponseResult<>(false, message, ErrorCode.UNKNOWN.toString(), null);
+    }
+
+    public static <E> ResponseResult<E> fail(String message, ErrorCode code) {
+        return new ResponseResult<>(false, message, code.toString(), null);
+    }
+
+    public static <E> ResponseResult<E> fail(ErrorCode code) {
+        return new ResponseResult<>(false, code.getDescription(), code.toString(), null);
+    }
+
+    public static ResponseResult<String> fail(DistscheduleBaseException e) {
+        return new ResponseResult<>(false, e.getErrorCode().getDescription(), e.getErrorCode().toString(), null);
+    }
+
+    public boolean isSuccess() {
+        return success;
+    }
+
+    public String getMessage() {
+        return message;
+    }
+
+    public T getResult() {
+        return result;
+    }
+
+    public String getErrCode() {
+        return errCode;
+    }
+}

+ 165 - 0
Server/distschedule-service/src/main/java/com/example/distschedule/dto/ScheduleDto.java

@@ -0,0 +1,165 @@
+/*
+ * Copyright (c) 2021 Huawei Device Co., Ltd.
+ * Licensed under the Apache License,Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.distschedule.dto;
+
+import com.example.distschedule.dao.model.Schedule;
+
+import java.util.Date;
+
+public class ScheduleDto implements Comparable<ScheduleDto>{
+    private String id;
+
+    private String name;
+
+    private String userIds;
+
+    private String deviceConfig;
+
+    private String remindDay;
+
+    private String creatorId;
+
+    private String sceneId;
+
+    private String stateDetail;
+
+    private Date startTime;
+
+    private Date endTime;
+
+    private Date createTime;
+
+    private Date updateTime;
+
+    public ScheduleDto() {
+    }
+
+    public ScheduleDto(Schedule schedule) {
+        this.id = schedule.getId();
+        this.name = schedule.getName();
+        this.deviceConfig = schedule.getDeviceConfig();
+        this.remindDay = schedule.getRemindDay();
+        this.creatorId = schedule.getCreatorId();
+        this.sceneId = schedule.getSceneId();
+        this.stateDetail = schedule.getStateDetail();
+        this.startTime = schedule.getStartTime();
+        this.endTime = schedule.getEndTime();
+        this.createTime = schedule.getCreateTime();
+        this.updateTime = schedule.getUpdateTime();
+        this.userIds = schedule.getUserIds();
+    }
+
+    public String getId() {
+        return id;
+    }
+
+    public void setId(String id) {
+        this.id = id;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public String getUserIds() {
+        return userIds;
+    }
+
+    public void setUserIds(String userIds) {
+        this.userIds = userIds;
+    }
+
+    public String getDeviceConfig() {
+        return deviceConfig;
+    }
+
+    public void setDeviceConfig(String deviceConfig) {
+        this.deviceConfig = deviceConfig;
+    }
+
+    public String getRemindDay() {
+        return remindDay;
+    }
+
+    public void setRemindDay(String remindDay) {
+        this.remindDay = remindDay;
+    }
+
+    public String getCreatorId() {
+        return creatorId;
+    }
+
+    public void setCreatorId(String creatorId) {
+        this.creatorId = creatorId;
+    }
+
+    public String getSceneId() {
+        return sceneId;
+    }
+
+    public void setSceneId(String sceneId) {
+        this.sceneId = sceneId;
+    }
+
+    public String getStateDetail() {
+        return stateDetail;
+    }
+
+    public void setStateDetail(String stateDetail) {
+        this.stateDetail = stateDetail;
+    }
+
+    public Date getStartTime() {
+        return startTime;
+    }
+
+    public void setStartTime(Date startTime) {
+        this.startTime = startTime;
+    }
+
+    public Date getEndTime() {
+        return endTime;
+    }
+
+    public void setEndTime(Date endTime) {
+        this.endTime = endTime;
+    }
+
+    public Date getCreateTime() {
+        return createTime;
+    }
+
+    public void setCreateTime(Date createTime) {
+        this.createTime = createTime;
+    }
+
+    public Date getUpdateTime() {
+        return updateTime;
+    }
+
+    public void setUpdateTime(Date updateTime) {
+        this.updateTime = updateTime;
+    }
+
+    @Override
+    public int compareTo(ScheduleDto scheduleDto) {
+        return this.startTime.compareTo(scheduleDto.getStartTime());
+    }
+}

+ 28 - 0
Server/distschedule-service/src/main/java/com/example/distschedule/dto/UpdateDeviceDto.java

@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2021 Huawei Device Co., Ltd.
+ * Licensed under the Apache License,Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ 
+package com.example.distschedule.dto;
+
+public class UpdateDeviceDto {
+    private String name;
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+}

+ 28 - 0
Server/distschedule-service/src/main/java/com/example/distschedule/dto/UpdateFamilyDto.java

@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2021 Huawei Device Co., Ltd.
+ * Licensed under the Apache License,Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ 
+package com.example.distschedule.dto;
+
+public class UpdateFamilyDto {
+    private String name;
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+}

+ 69 - 0
Server/distschedule-service/src/main/java/com/example/distschedule/dto/UserDeviceDto.java

@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2021 Huawei Device Co., Ltd.
+ * Licensed under the Apache License,Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ 
+package com.example.distschedule.dto;
+
+import com.example.distschedule.dao.model.UserDevice;
+
+/**
+ * 用户分享设备
+ */
+public class UserDeviceDto {
+    private String userId;
+    private String phone;
+    private String deviceId;
+    private String userName;
+
+    public UserDeviceDto() {
+
+    }
+
+    public UserDeviceDto(UserDevice userDevice) {
+        this.userId = userDevice.getUserId();
+        this.deviceId = userDevice.getDeviceId();
+    }
+
+    public String getUserId() {
+        return userId;
+    }
+
+    public void setUserId(String userId) {
+        this.userId = userId;
+    }
+
+    public String getDeviceId() {
+        return deviceId;
+    }
+
+    public void setDeviceId(String deviceId) {
+        this.deviceId = deviceId;
+    }
+
+    public String getPhone() {
+        return phone;
+    }
+
+    public void setPhone(String phone) {
+        this.phone = phone;
+    }
+
+    public String getUserName() {
+        return userName;
+    }
+
+    public void setUserName(String userName) {
+        this.userName = userName;
+    }
+}

+ 84 - 0
Server/distschedule-service/src/main/java/com/example/distschedule/dto/UserDto.java

@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 2021 Huawei Device Co., Ltd.
+ * Licensed under the Apache License,Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ 
+package com.example.distschedule.dto;
+
+import com.example.distschedule.dao.model.User;
+
+import java.util.Date;
+
+public class UserDto {
+    private String id;
+
+    private String phone;
+
+    private String name;
+
+    private Date createTime;
+
+    private Date updateTime;
+
+    public UserDto() {
+
+    }
+
+    public UserDto(User user) {
+        this.id = user.getId();
+        this.name = user.getName();
+        this.phone = user.getPhone();
+        this.createTime = user.getCreateTime();
+        this.updateTime = user.getUpdateTime();
+    }
+
+    public String getId() {
+        return id;
+    }
+
+    public void setId(String id) {
+        this.id = id;
+    }
+
+    public String getPhone() {
+        return phone;
+    }
+
+    public void setPhone(String phone) {
+        this.phone = phone;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public Date getCreateTime() {
+        return createTime;
+    }
+
+    public void setCreateTime(Date createTime) {
+        this.createTime = createTime;
+    }
+
+    public Date getUpdateTime() {
+        return updateTime;
+    }
+
+    public void setUpdateTime(Date updateTime) {
+        this.updateTime = updateTime;
+    }
+}

+ 87 - 0
Server/distschedule-service/src/main/java/com/example/distschedule/dto/UserFamilyDto.java

@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 2021 Huawei Device Co., Ltd.
+ * Licensed under the Apache License,Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ 
+package com.example.distschedule.dto;
+
+import com.example.distschedule.dao.model.UserFamily;
+
+public class UserFamilyDto {
+    private int id;
+    private String userId;
+    private String userPhone;
+    private String userName;
+    private String familyId;
+    private String role;
+
+    public UserFamilyDto() {
+    }
+
+    public UserFamilyDto(UserFamily userFamily) {
+        this.id = userFamily.getId();
+        this.userId = userFamily.getUserId();
+        this.userPhone = userFamily.getUserPhone();
+        this.familyId = userFamily.getFamilyId();
+        this.role = userFamily.getRole();
+        this.userName = userFamily.getUserName();
+    }
+
+    public int getId() {
+        return id;
+    }
+
+    public void setId(int id) {
+        this.id = id;
+    }
+
+    public String getUserId() {
+        return userId;
+    }
+
+    public void setUserId(String userId) {
+        this.userId = userId;
+    }
+
+    public String getUserPhone() {
+        return userPhone;
+    }
+
+    public void setUserPhone(String userPhone) {
+        this.userPhone = userPhone;
+    }
+
+    public String getFamilyId() {
+        return familyId;
+    }
+
+    public void setFamilyId(String familyId) {
+        this.familyId = familyId;
+    }
+
+    public String getRole() {
+        return role;
+    }
+
+    public void setRole(String role) {
+        this.role = role;
+    }
+
+    public String getUserName() {
+        return userName;
+    }
+
+    public void setUserName(String userName) {
+        this.userName = userName;
+    }
+}

+ 23 - 0
Server/distschedule-service/src/main/java/com/example/distschedule/enums/DeviceState.java

@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2021 Huawei Device Co., Ltd.
+ * Licensed under the Apache License,Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ 
+package com.example.distschedule.enums;
+
+/**
+ * 设备状态
+ */
+public enum DeviceState {
+    ONLINE, OFFLINE;
+}

+ 60 - 0
Server/distschedule-service/src/main/java/com/example/distschedule/enums/HandDeviceType.java

@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2021 Huawei Device Co., Ltd.
+ * Licensed under the Apache License,Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ 
+package com.example.distschedule.enums;
+
+/**
+ * 手持设备类型
+ */
+public enum HandDeviceType {
+
+    PHONE("Phone", 5), WATCH("Watch", 6), TABLET("Tablet", 7);
+    // 成员变量
+    private String name;
+    private int index;
+
+    // 构造方法
+    private HandDeviceType(String name, int index) {
+        this.name = name;
+        this.index = index;
+    }
+
+    // 普通方法
+    public static String getName(int index) {
+        for (HandDeviceType c : HandDeviceType.values()) {
+            if (c.getIndex() == index) {
+                return c.name;
+            }
+        }
+        return null;
+    }
+
+    // get set 方法
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public int getIndex() {
+        return index;
+    }
+
+    public void setIndex(int index) {
+        this.index = index;
+    }
+}

+ 23 - 0
Server/distschedule-service/src/main/java/com/example/distschedule/enums/ScheduleComandType.java

@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2021 Huawei Device Co., Ltd.
+ * Licensed under the Apache License,Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ 
+package com.example.distschedule.enums;
+
+/**
+ * 定时命令操作类型:添加,更新,删除
+ */
+public enum ScheduleComandType {
+    A,U,D;
+}

+ 181 - 0
Server/distschedule-service/src/main/java/com/example/distschedule/error/ErrorCode.java

@@ -0,0 +1,181 @@
+/*
+ * Copyright (c) 2021 Huawei Device Co., Ltd.
+ * Licensed under the Apache License,Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ 
+package com.example.distschedule.error;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+public enum ErrorCode {
+    /**
+     * 通用错误码.
+     */
+    ILLEGAL_PARAM(Level.SERVER, Module.COMMON, "00", "illegal parameter"),
+    ILLEGAL_STATE(Level.SERVER, Module.COMMON, "01", "illegal state"),
+    UNSUPPORTED_OPERATION(Level.SERVER, Module.COMMON, "02", "unsupported operation"),
+    ILLEGAL_PERMISSION(Level.SERVER, Module.COMMON, "03", "illegal permission"),
+
+    /**
+     * 用户错误码.
+     */
+    USER_NOT_FOUND(Level.SERVER, Module.USER, "00", "can not find user"),
+    USER_UPDATE_FAIL(Level.SERVER, Module.USER, "01", "user update failed"),
+
+    /**
+     * 家庭错误码.
+     */
+    FAMILY_NOT_FOUND(Level.SERVER, Module.FAMILY, "00", "can not find family"),
+    FAMILY_UPDATE_FAIL(Level.SERVER, Module.FAMILY, "01", "family update failed"),
+    FAMILY_DELETE_FAIL(Level.SERVER, Module.FAMILY, "02", "family delete failed"),
+    FAMILY_MEMBER_SAVE_FAIL(Level.SERVER, Module.FAMILY, "03", "family member save failed"),
+    FAMILY_MEMBER_DELETE_FAIL(Level.SERVER, Module.FAMILY, "04", "family member delete failed"),
+    FAMILY_MEMBER_CANNOT_CREATOR_FAIL(Level.SERVER, Module.FAMILY, "05", "family member can not creator failed"),
+    FAMILY_EXIST(Level.SERVER, Module.FAMILY, "06", "Current user already have a family"),
+
+    /**
+     * 设备错误码.
+     */
+    DEVICE_NOT_FOUND(Level.SERVER, Module.DEVICE, "00", "can not find device"),
+    DEVICE_UPDATE_FAIL(Level.SERVER, Module.DEVICE, "01", "device update failed"),
+    DEVICE_DELETE_FAIL(Level.SERVER, Module.DEVICE, "02", "device delete failed"),
+    DEVICE_EXIST_FAIL(Level.SERVER, Module.DEVICE, "03", "device has existed falied"),
+    DEVICE_SHARE_FAIL(Level.SERVER, Module.DEVICE, "04", "device share failed"),
+    DEVICE_UNSHARE_FAIL(Level.SERVER, Module.DEVICE, "05", "device unshare failed"),
+    DEVICE_SHARE_OWNDEVICE_FAIL(Level.SERVER, Module.DEVICE, "06", "fail to share own device"),
+    DEVICE_SAVE_FAIL(Level.SERVER, Module.DEVICE, "07", "device save failed"),
+    DEVICE_ADDIOTCLOUD_FAIL(Level.SERVER, Module.DEVICE, "08", "add device to iot cloud failed"),
+    DEVICE_TYPE_NOT_EXIST_FAIL(Level.SERVER, Module.DEVICE, "09", "device type not existed failed"),
+    DEVICE_DELETEIOTCLOUD_FAIL(Level.SERVER, Module.DEVICE, "10", "delete device to iot cloud failed"),
+    DEVICE_SEND_COMMAND_FAIL(Level.SERVER, Module.DEVICE, "11", "device send iot command failed"),
+
+    /**
+     * 设备错误码.
+     */
+    SCHEDULE_NOT_FOUND(Level.SERVER, Module.SCHEDULE, "00", "can not find schedule"),
+    SCHEDULE_UPDATE_FAIL(Level.SERVER, Module.SCHEDULE, "01", "device update failed"),
+    SCHEDULE_DELETE_FAIL(Level.SERVER, Module.SCHEDULE, "02", "device schedule failed"),
+    SCHEDULE_SEND_COMMAND_FAIL(Level.SERVER, Module.DEVICE, "03", "schedule send iot command failed"),
+    SCHEDULE_USER_SAVE_FAIL(Level.SERVER, Module.SCHEDULE, "04", "user schedule save failed"),
+
+    /**
+     * 数据库操作异常.
+     */
+    DATABASE(Level.SYSTEM, Module.DATABASE, "00", "database error"),
+
+    /**
+     * 未知错误.
+     */
+    UNKNOWN(Level.SYSTEM, Module.UNKNOWN, "00", "unknown error");
+
+    private final String level;
+    private final String module;
+    private final String code;
+    private final String description;
+
+    ErrorCode(String level, String module, String code, String description) {
+        this.level = level;
+        this.module = module;
+        this.code = code;
+        this.description = description;
+    }
+
+    public String getLevel() {
+        return level;
+    }
+
+    public String getModule() {
+        return module;
+    }
+
+    public String getCode() {
+        return code;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+
+    @Override
+    public String toString() {
+        return String.format(Locale.ENGLISH, "E%s%s%s", this.level, this.module, this.code);
+    }
+
+    public String toDetailString() {
+        return String.format(Locale.ENGLISH, "%-20s %s   %s", Module.getModuleName(this.getModule()), this.toString(), this.description);
+    }
+
+    public static String dumpErrorCodes() {
+        return Stream.of(values())
+                .map(ErrorCode::toString)
+                .collect(Collectors.joining("\n"));
+    }
+
+    public static String dumpDetailErrorCodes() {
+        return Stream.of(values())
+                .map(ErrorCode::toDetailString)
+                .collect(Collectors.joining("\n"));
+    }
+
+    /**
+     * 错误级别定义.
+     */
+    public static class Level {
+        /**
+         * 系统级错误, 比如IO请求失败,服务请求拒绝,多为不可预测的错误.
+         */
+        static final String SYSTEM = "1";
+        /**
+         * 服务级错误,多为业务逻辑不满足要求,比如工程不存在,没有权限等等.
+         */
+        static final String SERVER = "2";
+    }
+
+    /**
+     * 错误模块定义.
+     */
+    @SuppressWarnings("serial")
+    public static class Module {
+        public static final String USER = "00";
+        public static final String FAMILY = "01";
+        public static final String DEVICE = "02";
+        public static final String SCHEDULE = "03";
+        public static final String SCENCE = "04";
+        public static final String DATABASE = "05";
+        public static final String IOT_DATABASE = "06";
+        public static final String COMMON = "10";
+
+        public static final String UNKNOWN = "99";
+
+        public static final Map<String, String> MODULE_NAMES = Collections.unmodifiableMap(new HashMap<String, String>() {{
+            put(USER, "user");
+            put(FAMILY, "family");
+            put(DEVICE, "device");
+            put(SCHEDULE, "schedule");
+            put(SCENCE, "scence");
+            put(IOT_DATABASE, "iot_database");
+            put(DATABASE, "database");
+            put(COMMON, "common");
+            put(UNKNOWN, "unknown");
+        }});
+
+        public static String getModuleName(String moduleCode) {
+            return MODULE_NAMES.getOrDefault(moduleCode, "unknown");
+        }
+    }
+}

+ 50 - 0
Server/distschedule-service/src/main/java/com/example/distschedule/exception/DistscheduleBaseException.java

@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2021 Huawei Device Co., Ltd.
+ * Licensed under the Apache License,Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ 
+package com.example.distschedule.exception;
+
+import com.example.distschedule.error.ErrorCode;
+
+public class DistscheduleBaseException extends Exception {
+    private final ErrorCode errorCode;
+
+    public DistscheduleBaseException(ErrorCode errorCode) {
+        this.errorCode = errorCode;
+    }
+
+    public DistscheduleBaseException(String message, ErrorCode errorCode) {
+        super(message);
+        this.errorCode = errorCode;
+    }
+
+    public DistscheduleBaseException(String message, Throwable cause, ErrorCode errorCode) {
+        super(message, cause);
+        this.errorCode = errorCode;
+    }
+
+    public DistscheduleBaseException(Throwable cause, ErrorCode errorCode) {
+        super(cause);
+        this.errorCode = errorCode;
+    }
+
+    public DistscheduleBaseException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace, ErrorCode errorCode) {
+        super(message, cause, enableSuppression, writableStackTrace);
+        this.errorCode = errorCode;
+    }
+
+    public ErrorCode getErrorCode() {
+        return errorCode;
+    }
+}

+ 28 - 0
Server/distschedule-service/src/main/java/com/example/distschedule/exception/DistscheduleDeviceException.java

@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2021 Huawei Device Co., Ltd.
+ * Licensed under the Apache License,Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ 
+package com.example.distschedule.exception;
+
+import com.example.distschedule.error.ErrorCode;
+
+public class DistscheduleDeviceException extends DistscheduleBaseException {
+    public DistscheduleDeviceException(ErrorCode errorCode) {
+        super(errorCode);
+    }
+
+    public DistscheduleDeviceException(String message, ErrorCode errorCode) {
+        super(message, errorCode);
+    }
+}

+ 28 - 0
Server/distschedule-service/src/main/java/com/example/distschedule/exception/DistscheduleFamilyException.java

@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2021 Huawei Device Co., Ltd.
+ * Licensed under the Apache License,Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ 
+package com.example.distschedule.exception;
+
+import com.example.distschedule.error.ErrorCode;
+
+public class DistscheduleFamilyException extends DistscheduleBaseException {
+    public DistscheduleFamilyException(ErrorCode errorCode) {
+        super(errorCode);
+    }
+
+    public DistscheduleFamilyException(String message, ErrorCode errorCode) {
+        super(message, errorCode);
+    }
+}

+ 28 - 0
Server/distschedule-service/src/main/java/com/example/distschedule/exception/DistschedulePermissionException.java

@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2021 Huawei Device Co., Ltd.
+ * Licensed under the Apache License,Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ 
+package com.example.distschedule.exception;
+
+import com.example.distschedule.error.ErrorCode;
+
+public class DistschedulePermissionException extends DistscheduleBaseException{
+    public DistschedulePermissionException(ErrorCode errorCode) {
+        super(errorCode);
+    }
+
+    public DistschedulePermissionException(String message, ErrorCode errorCode) {
+        super(message, errorCode);
+    }
+}

+ 28 - 0
Server/distschedule-service/src/main/java/com/example/distschedule/exception/DistscheduleScheduleException.java

@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2021 Huawei Device Co., Ltd.
+ * Licensed under the Apache License,Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ 
+package com.example.distschedule.exception;
+
+import com.example.distschedule.error.ErrorCode;
+
+public class DistscheduleScheduleException extends DistscheduleBaseException {
+    public DistscheduleScheduleException(ErrorCode errorCode) {
+        super(errorCode);
+    }
+
+    public DistscheduleScheduleException(String message, ErrorCode errorCode) {
+        super(message, errorCode);
+    }
+}

+ 28 - 0
Server/distschedule-service/src/main/java/com/example/distschedule/exception/DistscheduleUserException.java

@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2021 Huawei Device Co., Ltd.
+ * Licensed under the Apache License,Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ 
+package com.example.distschedule.exception;
+
+import com.example.distschedule.error.ErrorCode;
+
+public class DistscheduleUserException extends DistscheduleBaseException {
+    public DistscheduleUserException(ErrorCode errorCode) {
+        super(errorCode);
+    }
+
+    public DistscheduleUserException(String message, ErrorCode errorCode) {
+        super(message, errorCode);
+    }
+}

+ 362 - 0
Server/distschedule-service/src/main/java/com/example/distschedule/service/DeviceService.java

@@ -0,0 +1,362 @@
+/*
+ * Copyright (c) 2021 Huawei Device Co., Ltd.
+ * Licensed under the Apache License,Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ 
+package com.example.distschedule.service;
+
+import com.alibaba.fastjson.JSONObject;
+import com.example.distschedule.dao.mapper.DeviceMapper;
+import com.example.distschedule.dao.mapper.DeviceTypeMapper;
+import com.example.distschedule.dao.model.Device;
+import com.example.distschedule.dao.model.DeviceType;
+import com.example.distschedule.dao.model.UserDevice;
+import com.example.distschedule.dto.*;
+import com.example.distschedule.enums.HandDeviceType;
+import com.example.distschedule.error.ErrorCode;
+import com.example.distschedule.exception.DistscheduleDeviceException;
+import com.huaweicloud.sdk.core.exception.ConnectionException;
+import com.huaweicloud.sdk.core.exception.RequestTimeoutException;
+import com.huaweicloud.sdk.core.exception.ServiceResponseException;
+import com.huaweicloud.sdk.iotda.v5.IoTDAClient;
+import com.huaweicloud.sdk.iotda.v5.model.*;
+import org.apache.commons.lang3.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Optional;
+
+@Service
+public class DeviceService {
+    private static final Logger LOGGER = LoggerFactory.getLogger(DeviceService.class);
+    @Value("${cloud.iot.appId}")
+    private String cloudIotAppId;
+    @Autowired
+    private DeviceMapper deviceMapper;
+    @Autowired
+    private DeviceTypeMapper deviceTypeMapper;
+    @Autowired
+    private UserService userService;
+    @Autowired
+    private IOTCloudService iotCloudService;
+
+    public Optional<DeviceDto> getDeviceById(String deviceId) {
+        Device device = deviceMapper.selectDeviceById(deviceId);
+
+        if (device != null) {
+            return Optional.of(new DeviceDto(device));
+        }
+        return Optional.empty();
+    }
+
+    @Transactional(rollbackFor = DistscheduleDeviceException.class)
+    public String saveDevice(CreateDeviceDto createDeviceDto) throws DistscheduleDeviceException {
+        //1.验证用户是否存在
+        Optional<UserDto> creatorDto = userService.getUserById(createDeviceDto.getOwnerId());
+
+        if (!creatorDto.isPresent()) {
+            throw new DistscheduleDeviceException(ErrorCode.USER_NOT_FOUND);
+        }
+
+        //2. 获取设备类型
+        DeviceType deviceType = deviceTypeMapper.selectDeviceTypeById(createDeviceDto.getTypeId());
+        if (deviceType == null) {
+            throw new DistscheduleDeviceException(ErrorCode.DEVICE_TYPE_NOT_EXIST_FAIL);
+        }
+
+        // 保存手持设备
+        if (this.isHandDevice(createDeviceDto.getTypeId())) {
+            return saveHandDevice(createDeviceDto);
+        }
+
+        //3.验证设备是否存在
+        Device device = deviceMapper.selectDeviceById(createDeviceDto.getId());
+
+        if (device != null) {
+            throw new DistscheduleDeviceException(ErrorCode.DEVICE_EXIST_FAIL);
+        }
+
+        device = new Device();
+        device.setId(createDeviceDto.getId());
+        device.setName(createDeviceDto.getName());
+        device.setOwnerId(createDeviceDto.getOwnerId());
+        device.setTypeId(createDeviceDto.getTypeId());
+        device.setState("NOTACTIVE");
+
+        int res = deviceMapper.saveDevice(device);
+        if (res == 0) {
+            throw new DistscheduleDeviceException(ErrorCode.DEVICE_SAVE_FAIL);
+        }
+
+        //4.不是手持设备,则调用iot云保存设备
+        AddDeviceRequest request = new AddDeviceRequest();
+        AddDevice body = new AddDevice();
+
+        body.setDeviceId(createDeviceDto.getId());
+        body.setDeviceName(createDeviceDto.getId() + "_" + deviceType.getProductId());
+        body.setNodeId(createDeviceDto.getId());
+        body.setProductId(deviceType.getProductId());
+        body.setAppId(cloudIotAppId);
+        AuthInfo authInfo = new AuthInfo();
+        authInfo.setAuthType("SECRET");//秘钥接入
+        authInfo.setSecret(createDeviceDto.getSecret());
+        body.setAuthInfo(authInfo);
+        request.setBody(body);
+
+        try {
+            // 创建设备
+            AddDeviceResponse response = iotCloudService.getIoTDAClient().addDevice(request);
+            LOGGER.info(response.toString());
+        } catch (ConnectionException e) {
+            throw new DistscheduleDeviceException(e.getMessage(), ErrorCode.DEVICE_ADDIOTCLOUD_FAIL);
+        } catch (RequestTimeoutException e) {
+            throw new DistscheduleDeviceException(e.getMessage(), ErrorCode.DEVICE_ADDIOTCLOUD_FAIL);
+        } catch (ServiceResponseException e) {
+            throw new DistscheduleDeviceException(e.getMessage(), ErrorCode.DEVICE_ADDIOTCLOUD_FAIL);
+        }
+        return device.getId();
+    }
+
+    /**
+     * 保存手持设备
+     *
+     * @return
+     */
+    private String saveHandDevice(CreateDeviceDto createDeviceDto) throws DistscheduleDeviceException {
+        //3.验证设备是否存在
+        Device device = deviceMapper.selectDeviceById(createDeviceDto.getId());
+
+        if (device != null) {
+            if (StringUtils.equals(device.getOwnerId(),createDeviceDto.getOwnerId())){
+                return device.getId();
+            }
+            // 删除之前的设备
+            deviceMapper.deleteDevice(createDeviceDto.getId());
+        }
+
+        device = new Device();
+        device.setId(createDeviceDto.getId());
+        device.setName(createDeviceDto.getName());
+        device.setOwnerId(createDeviceDto.getOwnerId());
+        device.setTypeId(createDeviceDto.getTypeId());
+        device.setState("");
+
+        int res = deviceMapper.saveDevice(device);
+        if (res == 0) {
+            throw new DistscheduleDeviceException(ErrorCode.DEVICE_SAVE_FAIL);
+        }
+        return device.getId();
+    }
+
+    public int shareDevice(String userId, String shareUserId, String deviceId) throws DistscheduleDeviceException {
+        //0.自己的设备不能分享
+        if (StringUtils.equals(userId, shareUserId)) {
+            throw new DistscheduleDeviceException(ErrorCode.DEVICE_SHARE_OWNDEVICE_FAIL);
+        }
+
+        //1.获取设备
+        Device device = deviceMapper.selectDeviceById(deviceId);
+        if (device == null) {
+            throw new DistscheduleDeviceException(ErrorCode.DEVICE_NOT_FOUND);
+        }
+        //2.权限校验
+        if (!StringUtils.equals(device.getOwnerId(), userId)) {
+            throw new DistscheduleDeviceException(ErrorCode.ILLEGAL_PERMISSION);
+        }
+
+        //3.查询是否已关联
+        UserDevice userDevice = deviceMapper.getUserDevice(shareUserId, deviceId);
+        if (userDevice != null) {
+            return 1;//表示已存在,关联成功
+        }
+
+        //4.保存关联
+        return deviceMapper.saveUserDevice(shareUserId, deviceId);
+    }
+
+    public int unshareDevice(String userId, String shareUserId, String deviceId) throws DistscheduleDeviceException {
+        //1.获取设备
+        Device device = deviceMapper.selectDeviceById(deviceId);
+        if (device == null) {
+            throw new DistscheduleDeviceException(ErrorCode.DEVICE_NOT_FOUND);
+        }
+        //2.权限校验
+        if (!StringUtils.equals(device.getOwnerId(), userId)) {
+            throw new DistscheduleDeviceException(ErrorCode.ILLEGAL_PERMISSION);
+        }
+        //3.查询是否已关联
+        UserDevice userDevice = deviceMapper.getUserDevice(shareUserId, deviceId);
+        if (userDevice == null) {
+            return 1;//表示已不存在,取消关联成功
+        }
+        //4.取消关联
+        return deviceMapper.deleteUserDevice(shareUserId, deviceId);
+    }
+
+    public List<DeviceDto> getDevicesByUserId(String userId) {
+        List<Device> creartedDevices = deviceMapper.selectCreatedDevicesByUserId(userId);
+        List<Device> sharedDevices = deviceMapper.selectSharedDevicesByUserId(userId);
+
+        List<DeviceDto> devicesDtos = new LinkedList<>();
+
+        for (Device device : creartedDevices) {
+            DeviceDto deviceDto = new DeviceDto(device);
+            devicesDtos.add(deviceDto);
+        }
+
+        for (Device device : sharedDevices) {
+            DeviceDto deviceDto = new DeviceDto(device);
+            devicesDtos.add(deviceDto);
+        }
+        return devicesDtos;
+    }
+
+    @Transactional(rollbackFor = DistscheduleDeviceException.class)
+    public int deleteDevice(String userId, String deviceId) throws DistscheduleDeviceException {
+        //1. 查设备
+        Device device = deviceMapper.selectDeviceById(deviceId);
+        if (device == null) {
+            return 1;//找不到设备,删除成功,保证幂等性
+        }
+        //2. 验证权限
+        if (!StringUtils.equals(device.getOwnerId(), userId)) {
+            throw new DistscheduleDeviceException(ErrorCode.ILLEGAL_PERMISSION);
+        }
+        //3.删除设备分享
+        deviceMapper.deleteUserDeviceByDeviceId(deviceId);
+
+        //4.删除设备
+        int res = deviceMapper.deleteDevice(deviceId);
+        if (res == 0) {
+            throw new DistscheduleDeviceException(ErrorCode.DEVICE_DELETE_FAIL);
+        }
+
+        //5.调用IOT云,删除设备
+        if (!this.isHandDevice(device.getTypeId())) {
+            IoTDAClient client = iotCloudService.getIoTDAClient();
+            DeleteDeviceRequest request = new DeleteDeviceRequest();
+            request.setDeviceId(deviceId);
+
+            try {
+                // 删除设备
+                DeleteDeviceResponse response = client.deleteDevice(request);
+                LOGGER.info("DeleteDeviceResponse:" + response.toString());
+            } catch (ConnectionException e) {
+                throw new DistscheduleDeviceException(e.getMessage(), ErrorCode.DEVICE_DELETEIOTCLOUD_FAIL);
+            } catch (RequestTimeoutException e) {
+                throw new DistscheduleDeviceException(e.getMessage(), ErrorCode.DEVICE_DELETEIOTCLOUD_FAIL);
+            } catch (ServiceResponseException e) {
+                if (!StringUtils.equals(e.getErrorCode(), "IOTDA.014000")) {//设备已不存在
+                    throw new DistscheduleDeviceException(e.getMessage(), ErrorCode.DEVICE_DELETEIOTCLOUD_FAIL);
+                }
+            }
+        }
+        return 1;
+    }
+
+    public int sendCommand(String deviceId, DeviceCommandDto deviceCommandDto) throws DistscheduleDeviceException {
+        CreateCommandRequest request = new CreateCommandRequest();
+        DeviceCommandRequest body = new DeviceCommandRequest();
+        body.setCommandName(deviceCommandDto.getCommandName());
+        body.setServiceId(deviceCommandDto.getServiceId());
+        try {
+            body.setParas(JSONObject.parse(deviceCommandDto.getValue()));
+        } catch (Exception e) {
+            LOGGER.error(e.getMessage());
+            throw new DistscheduleDeviceException("Invalid command value", ErrorCode.DEVICE_SEND_COMMAND_FAIL);
+        }
+
+        request.setDeviceId(deviceId);
+        request.setBody(body);
+
+        try {
+            // 创建异步命令
+            CreateCommandResponse response = iotCloudService.getIoTDAClient().createCommand(request);
+            LOGGER.info("createCommand response= " + response.toString());
+        } catch (ConnectionException e) {
+            throw new DistscheduleDeviceException(e.getMessage(), ErrorCode.DEVICE_SEND_COMMAND_FAIL);
+        } catch (RequestTimeoutException e) {
+            throw new DistscheduleDeviceException(e.getMessage(), ErrorCode.DEVICE_SEND_COMMAND_FAIL);
+        } catch (ServiceResponseException e) {
+            throw new DistscheduleDeviceException(e.getMessage(), ErrorCode.DEVICE_SEND_COMMAND_FAIL);
+        }
+        return 0;
+    }
+
+    public int updateDeviceStatue(String deviceId, String status) {
+        return deviceMapper.updateDeviceStatue(deviceId, status);
+    }
+
+    public List<UserDeviceDto> getShareUsersByDeviceId(String deviceId) {
+        List<UserDeviceDto> userDeviceDtos = new LinkedList<>();
+        List<UserDevice> userDevices = deviceMapper.selectShareUsersByDeviceId(deviceId);
+        for (UserDevice userDevice : userDevices) {
+            UserDeviceDto userDeviceDto = new UserDeviceDto(userDevice);
+            Optional<UserDto> userDto = userService.getUserById(userDeviceDto.getUserId());
+            if (userDto.isPresent()) {
+                userDeviceDto.setPhone(userDto.get().getPhone());
+                userDeviceDto.setUserName(userDto.get().getName());
+            }
+            userDeviceDtos.add(userDeviceDto);
+        }
+        return userDeviceDtos;
+    }
+
+    public int updateDeviceById(String userId, String deviceId, UpdateDeviceDto updateDeviceDto) throws DistscheduleDeviceException {
+        Device oldDevice = deviceMapper.selectDeviceById(deviceId);
+        if (oldDevice == null) {
+            throw new DistscheduleDeviceException(ErrorCode.DEVICE_UPDATE_FAIL);
+        }
+
+        if (!StringUtils.equals(oldDevice.getOwnerId(), userId)) {
+            throw new DistscheduleDeviceException(ErrorCode.ILLEGAL_PERMISSION);
+        }
+
+        oldDevice.setName(updateDeviceDto.getName());
+        return deviceMapper.updateDevice(oldDevice);
+    }
+
+    public List<String> getUsersByDeviceId(String deviceId) throws DistscheduleDeviceException {
+        List<String> userIds = new LinkedList<>();
+        Device device = deviceMapper.selectDeviceById(deviceId);
+        if (device == null) {
+            throw new DistscheduleDeviceException(ErrorCode.DEVICE_NOT_FOUND);
+        }
+        userIds.add(device.getOwnerId());
+        List<UserDevice> userDevices = deviceMapper.selectShareUsersByDeviceId(deviceId);
+        for (UserDevice userDevice : userDevices) {
+            userIds.add(userDevice.getUserId());
+        }
+        return userIds;
+    }
+
+    public int updateDeviceProperty(String deviceId, String property) {
+        return deviceMapper.updateDeviceProperty(deviceId, property);
+    }
+
+    /**
+     * 判断是否为手持设备
+     *
+     * @param type 设备类型
+     * @return 是否为手持设备
+     */
+    private boolean isHandDevice(int type) {
+        return HandDeviceType.getName(type) != null;
+    }
+}

+ 57 - 0
Server/distschedule-service/src/main/java/com/example/distschedule/service/DeviceTypeService.java

@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2021 Huawei Device Co., Ltd.
+ * Licensed under the Apache License,Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ 
+package com.example.distschedule.service;
+
+import com.example.distschedule.dao.mapper.DeviceTypeMapper;
+import com.example.distschedule.dao.model.DeviceType;
+import com.example.distschedule.dto.DeviceTypeDto;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+import java.util.Optional;
+import java.util.stream.Collectors;
+
+@Service
+public class DeviceTypeService {
+    private static final Logger LOGGER = LoggerFactory.getLogger(DeviceTypeService.class);
+
+    @Autowired
+    private DeviceTypeMapper deviceTypeMapper;
+
+    public List<DeviceTypeDto> getDeviceTypes() {
+        List<DeviceType> deviceTypes = deviceTypeMapper.selectDeviceTypes();
+        return deviceTypes.stream().map(deviceType -> new DeviceTypeDto(deviceType)).collect(Collectors.toList());
+    }
+
+    public Optional<DeviceTypeDto> getDeviceTypesByProductId(String productId) {
+        DeviceType deviceType = deviceTypeMapper.selectDeviceTypeByProductId(productId);
+        if (deviceType == null) {
+            return Optional.empty();
+        }
+        return Optional.of(new DeviceTypeDto(deviceType));
+    }
+
+    public Optional<DeviceTypeDto> getDeviceTypeById(int deviceTypeId) {
+        DeviceType deviceType = deviceTypeMapper.selectDeviceTypeById(deviceTypeId);
+        if (deviceType == null) {
+            return Optional.empty();
+        }
+        return Optional.of(new DeviceTypeDto(deviceType));
+    }
+}

+ 211 - 0
Server/distschedule-service/src/main/java/com/example/distschedule/service/FamilyService.java

@@ -0,0 +1,211 @@
+/*
+ * Copyright (c) 2021 Huawei Device Co., Ltd.
+ * Licensed under the Apache License,Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.distschedule.service;
+
+import com.example.distschedule.dao.mapper.FamilyMapper;
+import com.example.distschedule.dao.model.Family;
+import com.example.distschedule.dao.model.UserFamily;
+import com.example.distschedule.dto.*;
+import com.example.distschedule.error.ErrorCode;
+import com.example.distschedule.exception.DistscheduleFamilyException;
+import com.example.distschedule.exception.DistschedulePermissionException;
+import com.example.distschedule.exception.DistscheduleUserException;
+import org.apache.commons.lang3.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Optional;
+import java.util.UUID;
+
+@Service
+public class FamilyService {
+    private static final Logger LOGGER = LoggerFactory.getLogger(FamilyService.class);
+    @Autowired
+    private FamilyMapper familyMapper;
+    @Autowired
+    private UserService userService;
+
+    public List<FamilyDto> getFamiliesByUserId(String userId) {
+        List<Family> createdFamilies = familyMapper.selectCreatedFamiliesByUserId(userId);
+        List<Family> memberFamilies = familyMapper.selectJoinedFamiliesByUserId(userId);
+
+        List<FamilyDto> familyDtos = new LinkedList<>();
+
+        for (Family family : createdFamilies) {
+            FamilyDto familyDto = new FamilyDto(family);
+            familyDto.setCreator(true);
+            familyDtos.add(familyDto);
+        }
+
+        for (Family family : memberFamilies) {
+            FamilyDto familyDto = new FamilyDto(family);
+            familyDto.setCreator(false);
+            familyDtos.add(familyDto);
+        }
+
+        return familyDtos;
+    }
+
+    @Transactional
+    public int deleteFamily(String userId, String familyId) throws DistscheduleFamilyException {
+        //1.校验家庭是否存在,不存在表示删除成功
+        Family family = familyMapper.selectFamilyById(familyId);
+        if (family == null) {
+            return 1;
+        }
+        //2.验证权限
+        if (!StringUtils.equals(family.getCreatorId(), userId)) {
+            throw new DistscheduleFamilyException(ErrorCode.ILLEGAL_PERMISSION);
+        }
+
+        //3.删除家庭成员,并验证原子性
+        familyMapper.deleteFamilyMembers(familyId);
+
+        //4.删除家庭
+        return familyMapper.deleteFamily(userId, familyId);
+    }
+
+    public String saveFamily(String userId, FamilyCreateDto familyCreateDto) throws DistscheduleUserException {
+        Optional<UserDto> creatorDto = userService.getUserById(userId);
+
+        if (!creatorDto.isPresent()) {
+            throw new DistscheduleUserException(ErrorCode.USER_NOT_FOUND);
+        }
+        // 当前禁止一个人有多个家庭
+        List<Family> createdFamilies = familyMapper.selectCreatedFamiliesByUserId(userId);
+        List<Family> memberFamilies = familyMapper.selectJoinedFamiliesByUserId(userId);
+        if (createdFamilies.size() > 0 || memberFamilies.size() > 0) {
+            throw new DistscheduleUserException(ErrorCode.FAMILY_EXIST);
+        }
+
+        Family family = new Family();
+        family.setId(UUID.randomUUID().toString());
+        family.setCreatorId(userId);
+        family.setName(familyCreateDto.getName());
+
+        int res = familyMapper.saveFamily(family);
+        if (res != 0) {
+            return family.getId();
+        }
+        return "";
+    }
+
+    public Optional<FamilyDto> getFamilyById(String familyId) {
+        Family family = familyMapper.selectFamilyById(familyId);
+        if (family == null) {
+            return Optional.empty();
+        }
+        return Optional.of(new FamilyDto(family));
+    }
+
+    public int updateFamily(String userId, String familyId, UpdateFamilyDto familyDto) throws DistschedulePermissionException, DistscheduleFamilyException {
+        Family oldFamily = familyMapper.selectFamilyById(familyId);
+        if (oldFamily == null) {
+            throw new DistscheduleFamilyException(ErrorCode.FAMILY_NOT_FOUND);
+        }
+
+        if (!StringUtils.equals(oldFamily.getCreatorId(), userId)) {
+            throw new DistschedulePermissionException(ErrorCode.ILLEGAL_PERMISSION);
+        }
+
+        Family newFamily = new Family();
+        newFamily.setId(familyId);
+        newFamily.setName(familyDto.getName());
+        newFamily.setCreatorId(userId);
+        return familyMapper.updateFamily(newFamily);
+    }
+
+    public int saveFamilyMember(String userId, String familyId, String phone, String role) throws DistscheduleUserException {
+        Optional<UserDto> userOptional = userService.getUserByPhone(phone, true);
+
+        if (!userOptional.isPresent()) {
+            throw new DistscheduleUserException(ErrorCode.USER_NOT_FOUND);
+        }
+        UserDto user = userOptional.get();
+
+        Optional<FamilyDto> familyOptional = this.getFamilyById(familyId);
+
+        if (!familyOptional.isPresent()) {
+            throw new DistscheduleUserException(ErrorCode.FAMILY_NOT_FOUND);
+        }
+        FamilyDto family = familyOptional.get();
+
+        //权限校验
+        if (!StringUtils.equals(userId, family.getCreatorId())) {
+            throw new DistscheduleUserException(ErrorCode.ILLEGAL_PERMISSION);
+        }
+
+        // 创建者不能再建立关系
+        if (StringUtils.equals(user.getId(), family.getCreatorId())) {
+            throw new DistscheduleUserException(ErrorCode.FAMILY_MEMBER_CANNOT_CREATOR_FAIL);
+        }
+
+        UserFamily userFamily = familyMapper.getUserFamily(user.getId(), familyId);
+
+        if (userFamily == null) {
+            return familyMapper.saveUserFamily(user.getId(), familyId, role);
+        } else {
+            return familyMapper.updateUserFamily(user.getId(), familyId, role);
+        }
+    }
+
+    public int deleteFamilyMember(String userId, String familyMemberId, String familyId) throws DistscheduleUserException {
+        Optional<FamilyDto> familyOptional = this.getFamilyById(familyId);
+
+        if (!familyOptional.isPresent()) {
+            throw new DistscheduleUserException(ErrorCode.FAMILY_NOT_FOUND);
+        }
+        FamilyDto family = familyOptional.get();
+
+        //权限校验
+        if (!StringUtils.equals(userId, family.getCreatorId())) {
+            throw new DistscheduleUserException(ErrorCode.ILLEGAL_PERMISSION);
+        }
+
+        UserFamily userFamily = familyMapper.getUserFamily(familyMemberId, familyId);
+        if (userFamily == null) {
+            return 1;
+        } else {
+            return familyMapper.deleteFamilyMember(familyMemberId, familyId);
+        }
+    }
+
+    public List<UserFamilyDto> getFamilyMembersById(String familyId) throws DistscheduleUserException {
+        UserFamily creatorFamily = familyMapper.selectCreatorFamily(familyId);
+
+        if (creatorFamily == null) {
+            throw new DistscheduleUserException(ErrorCode.FAMILY_NOT_FOUND);
+        }
+        List<UserFamily> userFamilies = familyMapper.selectFamilyMembersById(familyId);
+
+        List<UserFamilyDto> userFamilyDtos = new LinkedList<>();
+        UserFamilyDto creatorUserFamilyDto = new UserFamilyDto(creatorFamily);
+        creatorUserFamilyDto.setRole("Creator");
+        userFamilyDtos.add(creatorUserFamilyDto);
+
+        for (UserFamily userFamily : userFamilies) {
+            UserFamilyDto userFamilyDto = new UserFamilyDto(userFamily);
+            userFamilyDtos.add(userFamilyDto);
+        }
+
+        return userFamilyDtos;
+    }
+}

+ 54 - 0
Server/distschedule-service/src/main/java/com/example/distschedule/service/IOTCloudService.java

@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2021 Huawei Device Co., Ltd.
+ * Licensed under the Apache License,Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ 
+package com.example.distschedule.service;
+
+import com.huaweicloud.sdk.core.auth.BasicCredentials;
+import com.huaweicloud.sdk.core.auth.ICredential;
+import com.huaweicloud.sdk.iotda.v5.IoTDAClient;
+import com.huaweicloud.sdk.iotda.v5.region.IoTDARegion;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Service;
+
+@Service
+public class IOTCloudService {
+    private static final Logger LOGGER = LoggerFactory.getLogger(IOTCloudService.class);
+    @Value("${cloud.iot.ak}")
+    private String cloudIotAk;
+    @Value("${cloud.iot.sk}")
+    private String cloudIotSk;
+    @Value("${cloud.iot.appId}")
+    private String cloudIotAppId;
+
+    /**
+     * 使用华为IOT云SDK,获取IOT客户端
+     *
+     * @return client
+     */
+    public IoTDAClient getIoTDAClient() {
+        // 创建认证
+        ICredential auth = new BasicCredentials()
+                .withAk(cloudIotAk)
+                .withSk(cloudIotSk);
+        // 创建IoTDAClient实例并初始化
+        IoTDAClient client = IoTDAClient.newBuilder()
+                .withCredential(auth)
+                .withRegion(IoTDARegion.CN_NORTH_4)
+                .build();
+        return client;
+    }
+}

+ 443 - 0
Server/distschedule-service/src/main/java/com/example/distschedule/service/ScheduleService.java

@@ -0,0 +1,443 @@
+/*
+ * Copyright (c) 2021 Huawei Device Co., Ltd.
+ * Licensed under the Apache License,Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.example.distschedule.service;
+
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
+import com.example.distschedule.dao.mapper.ScheduleMapper;
+import com.example.distschedule.dao.model.Schedule;
+import com.example.distschedule.dao.model.UserSchedule;
+import com.example.distschedule.dto.CreateScheduleDto;
+import com.example.distschedule.dto.DeviceDto;
+import com.example.distschedule.dto.DeviceTypeDto;
+import com.example.distschedule.dto.ScheduleDto;
+import com.example.distschedule.enums.ScheduleComandType;
+import com.example.distschedule.error.ErrorCode;
+import com.example.distschedule.exception.DistscheduleScheduleException;
+import com.huaweicloud.sdk.core.exception.ConnectionException;
+import com.huaweicloud.sdk.core.exception.RequestTimeoutException;
+import com.huaweicloud.sdk.core.exception.ServiceResponseException;
+import com.huaweicloud.sdk.iotda.v5.model.CreateCommandRequest;
+import com.huaweicloud.sdk.iotda.v5.model.CreateCommandResponse;
+import com.huaweicloud.sdk.iotda.v5.model.DeviceCommandRequest;
+import org.apache.commons.lang3.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.text.SimpleDateFormat;
+import java.util.*;
+
+@Service
+public class ScheduleService {
+    private static final Logger LOGGER = LoggerFactory.getLogger(ScheduleService.class);
+    @Autowired
+    private IOTCloudService iotCloudService;
+    @Autowired
+    private DeviceService deviceService;
+    @Autowired
+    private DeviceTypeService deviceTypeService;
+    @Autowired
+    private ScheduleMapper scheduleMapper;
+
+    public Optional<ScheduleDto> getScheduleById(String scheduleId) {
+        Schedule schedule = scheduleMapper.selectScheduleById(scheduleId);
+
+        if (schedule != null) {
+            return Optional.of(new ScheduleDto(schedule));
+        }
+        return Optional.empty();
+    }
+
+    @Transactional(rollbackFor = DistscheduleScheduleException.class)
+    public String saveSchedule(CreateScheduleDto createScheduleDto) throws DistscheduleScheduleException {
+        Schedule schedule = new Schedule();
+        schedule.setId(UUID.randomUUID().toString());
+        schedule.setName(createScheduleDto.getName());
+        schedule.setDeviceConfig(createScheduleDto.getDeviceConfig());
+        schedule.setRemindDay(createScheduleDto.getRemindDay());
+        schedule.setCreatorId(createScheduleDto.getCreatorId());
+        schedule.setSceneId("default");
+        schedule.setStateDetail(createScheduleDto.getStateDetail());
+        schedule.setStartTime(createScheduleDto.getStartTime());
+        schedule.setEndTime(createScheduleDto.getEndTime());
+        schedule.setUserIds(createScheduleDto.getUserIds());
+
+        int res = scheduleMapper.saveSchedule(schedule);
+        if (res == 0) {
+            return "";
+        }
+
+        //保存用户日程信息
+        List<UserSchedule> userSchedules = new LinkedList<>();
+        if (StringUtils.isNotBlank(createScheduleDto.getUserIds())) {
+            String[] userIds = createScheduleDto.getUserIds().split(",");
+            for (String userId : userIds) {
+                userSchedules.add(new UserSchedule(userId, schedule.getId()));
+            }
+        }
+        res = scheduleMapper.saveUserSchedule(userSchedules);
+        if (res == 0) {
+            throw new DistscheduleScheduleException(ErrorCode.SCHEDULE_USER_SAVE_FAIL);
+        }
+
+        // 根据客户端传上来的设备列表,向IOT云发送定时命令
+        if (StringUtils.isNotBlank(createScheduleDto.getDeviceConfig())) {
+            JSONArray deviceJsonArray = JSON.parseArray(createScheduleDto.getDeviceConfig());
+
+            for (Iterator<Object> iterator = deviceJsonArray.iterator(); iterator.hasNext(); ) {
+                JSONObject next = (JSONObject) iterator.next();
+                try {
+                    sendScheduleComand(next.getString("deviceId"), ScheduleComandType.A, schedule, next.getJSONObject("command"));
+                } catch (DistscheduleScheduleException e) {
+                    LOGGER.warn("Fail to send iot command for device " + next.getString("deviceId"), e);
+                }
+            }
+        }
+
+        // 给联系人的儿童手表通知日程
+        sendChildWatchSchedule(ScheduleComandType.A, schedule);
+
+        return schedule.getId();
+    }
+
+    /**
+     * 给儿童手表发送日程
+     *
+     * @param deviceId           设备ID
+     * @param scheduleComandType 命令类型
+     * @param schedule           日程信息
+     */
+    private void sendChildWatchScheduleComand(String deviceId, ScheduleComandType scheduleComandType, Schedule schedule) throws DistscheduleScheduleException {
+        CreateCommandRequest request = new CreateCommandRequest();
+        DeviceCommandRequest body = new DeviceCommandRequest();
+        body.setCommandName("SetSchedule");//目前写死定时命令
+        Optional<DeviceDto> deviceDto = deviceService.getDeviceById(deviceId);
+        if (!deviceDto.isPresent()) {
+            throw new DistscheduleScheduleException(ErrorCode.DEVICE_NOT_FOUND);
+        }
+        Optional<DeviceTypeDto> deviceTypeDto = deviceTypeService.getDeviceTypeById(deviceDto.get().getTypeId());
+        if (!deviceTypeDto.isPresent()) {
+            throw new DistscheduleScheduleException(ErrorCode.DEVICE_TYPE_NOT_EXIST_FAIL);
+        }
+        body.setServiceId(deviceTypeDto.get().getServiceId());
+        try {
+            /*
+          {
+    "service_id":"SmartWatch",// 产品服务ID,固定
+    "command_name":"SetSchedule",// 日程预约命令名字,固定
+    "paras":{
+        "ScheduleID":"0", // 日程ID号(int),取值为0-65535, 用于服务端对设备日程的增、删、改
+        "option":"A", // 日程操作类型(string),取值为“A”(增加)、“D”(删除)、“U”(更新)
+        "Day":"1,2,3,4,5,6,7",// 日程的天(星期几)类型(string),已逗号区分的数字,“1,2,3,4,5,6,7”,或者下划线连接的数字,比方“1-7”标识周一到周日
+        "StartHour":11, // 日程的起始时辰,类型(int),取值为0-23
+        "StartMinute":59, // 日程的起始分,类型(int), 取值为0-59
+        "Command":{"MessageType":1}, // 提醒的消息类别,类型(int),取值为0 -- 未定义的日程提醒; 1 -- 起床;2 -- 上学; 3 -- 做作业; 4 -- 喝                                         水时间; 5 -- 睡觉时间
+        "DurationMinutes":5// 日程的预约提醒持续时间,类型(int)
+    }int
+}
+             */
+            JSONObject paras = new JSONObject();
+            paras.put("ScheduleID", schedule.getId());
+            paras.put("Option", scheduleComandType.name());
+            if (schedule.getStartTime() != null && schedule.getEndTime() != null) {
+                Calendar startCalendar = Calendar.getInstance();
+                startCalendar.setTime(schedule.getStartTime());
+                paras.put("StartHour", startCalendar.get(Calendar.HOUR_OF_DAY));
+                paras.put("StartMinute", startCalendar.get(Calendar.MINUTE));
+                paras.put("DurationMinutes", (schedule.getEndTime().getTime() - schedule.getStartTime().getTime()) / 1000 / 60);//持续时间,按分钟
+            } else {
+                paras.put("StartHour", 0);
+                paras.put("StartMinute", 0);
+            }
+            if (StringUtils.isNotBlank(schedule.getRemindDay())) {
+                paras.put("Day", schedule.getRemindDay());
+            } else {
+                paras.put("Day", "0");//设备默认一次性日程
+            }
+            JSONObject command = new JSONObject();
+            command.put("name", schedule.getName());
+            paras.put("Command", command);
+            body.setParas(paras);
+        } catch (Exception e) {
+            LOGGER.error(e.getMessage());
+            throw new DistscheduleScheduleException("Invalid command value", ErrorCode.SCHEDULE_SEND_COMMAND_FAIL);
+        }
+
+        request.setDeviceId(deviceId);
+        request.setBody(body);
+
+        try {
+            // 创建异步命令
+            CreateCommandResponse response = iotCloudService.getIoTDAClient().createCommand(request);
+            LOGGER.info("createCommand response= " + response.toString());
+        } catch (ConnectionException e) {
+            throw new DistscheduleScheduleException(e.getMessage(), ErrorCode.SCHEDULE_SEND_COMMAND_FAIL);
+        } catch (RequestTimeoutException e) {
+            throw new DistscheduleScheduleException(e.getMessage(), ErrorCode.SCHEDULE_SEND_COMMAND_FAIL);
+        } catch (ServiceResponseException e) {
+            throw new DistscheduleScheduleException(e.getMessage(), ErrorCode.SCHEDULE_SEND_COMMAND_FAIL);
+        }
+    }
+
+    /**
+     * 发送定时命令
+     *
+     * @param deviceId    设备ID
+     * @param commandType 命令类型
+     * @param schedule    日程
+     */
+    private void sendScheduleComand(String deviceId, ScheduleComandType commandType, Schedule schedule, JSONObject command) throws DistscheduleScheduleException {
+
+        CreateCommandRequest request = new CreateCommandRequest();
+        DeviceCommandRequest body = new DeviceCommandRequest();
+        body.setCommandName("SetSchedule");//目前写死定时命令
+        Optional<DeviceDto> deviceDto = deviceService.getDeviceById(deviceId);
+        if (!deviceDto.isPresent()) {
+            throw new DistscheduleScheduleException(ErrorCode.DEVICE_NOT_FOUND);
+        }
+        Optional<DeviceTypeDto> deviceTypeDto = deviceTypeService.getDeviceTypeById(deviceDto.get().getTypeId());
+        if (!deviceTypeDto.isPresent()) {
+            throw new DistscheduleScheduleException(ErrorCode.DEVICE_TYPE_NOT_EXIST_FAIL);
+        }
+        body.setServiceId(deviceTypeDto.get().getServiceId());
+        try {
+            /*
+            {
+	"service_id": "SmartLamp",            // 产品服务ID,固定为SmartLamp
+	"command_name": "SetLampShedule",     // 命令名称,固定为SetLampShedule
+	"paras":
+ 	{
+ 		"ScheduleID":0			// 日程ID号(类型int): 0 - 65535
+ 		"option":"A"			// 日程操作(类型string):长度为1,暂指定3个操作:A(新增日程),U(更新日程),D(删除日程)
+		"Day": "1,3,5",   		// 开始亮灯的日期,以星期为单位
+								// 取值(类型string): "1,2,3,4,5,6,7", 1-7代表周一到周日,每天之间用逗号(,)隔开,可以只设1天,也可以设置多天
+		"StartHour": 18,     	// 开始亮灯的小时时间(类型int) : 0 - 23
+		"StartMinute": 12,   	// 开始亮灯的分钟时间(类型int) : 0-59
+		"DurationMinutes": 1 	// 亮灯持续时间(类型int) : 0 - 1440
+	}
+}
+             */
+            JSONObject paras = new JSONObject();
+            paras.put("ScheduleID", schedule.getId());
+            paras.put("Option", commandType.name());
+            if (schedule.getStartTime() != null && schedule.getEndTime() != null) {
+                Calendar startCalendar = Calendar.getInstance();
+                startCalendar.setTime(schedule.getStartTime());
+                paras.put("StartHour", startCalendar.get(Calendar.HOUR_OF_DAY));
+                paras.put("StartMinute", startCalendar.get(Calendar.MINUTE));
+                paras.put("DurationMinutes", (schedule.getEndTime().getTime() - schedule.getStartTime().getTime()) / 1000 / 60);//持续时间,按分钟
+            } else {
+                paras.put("StartHour", 0);
+                paras.put("StartMinute", 0);
+            }
+            if (StringUtils.isNotBlank(schedule.getRemindDay())) {
+                paras.put("Day", schedule.getRemindDay());
+            } else {
+                paras.put("Day", "0");//设备默认一次性日程
+            }
+            paras.put("Command", command);
+            body.setParas(paras);
+        } catch (Exception e) {
+            LOGGER.error(e.getMessage());
+            throw new DistscheduleScheduleException("Invalid command value", ErrorCode.SCHEDULE_SEND_COMMAND_FAIL);
+        }
+
+        request.setDeviceId(deviceId);
+        request.setBody(body);
+
+        try {
+            // 创建异步命令
+            CreateCommandResponse response = iotCloudService.getIoTDAClient().createCommand(request);
+            LOGGER.info("createCommand response= " + response.toString());
+        } catch (ConnectionException e) {
+            throw new DistscheduleScheduleException(e.getMessage(), ErrorCode.SCHEDULE_SEND_COMMAND_FAIL);
+        } catch (RequestTimeoutException e) {
+            throw new DistscheduleScheduleException(e.getMessage(), ErrorCode.SCHEDULE_SEND_COMMAND_FAIL);
+        } catch (ServiceResponseException e) {
+            throw new DistscheduleScheduleException(e.getMessage(), ErrorCode.SCHEDULE_SEND_COMMAND_FAIL);
+        }
+    }
+
+    @Transactional(rollbackFor = DistscheduleScheduleException.class)
+    public int deleteSchedule(String userId, String scheduleId) throws DistscheduleScheduleException {
+        //1. 查设备
+        Schedule schedule = scheduleMapper.selectScheduleById(scheduleId);
+        if (schedule == null) {
+            return 1;//找不到日程,删除成功,保证幂等性
+        }
+        //2. 验证权限
+        if (!StringUtils.equals(schedule.getCreatorId(), userId)) {
+            throw new DistscheduleScheduleException(ErrorCode.ILLEGAL_PERMISSION);
+        }
+
+        //3.删除日程
+        scheduleMapper.deleteUserSchedule(scheduleId);
+        int res = scheduleMapper.deleteSchedule(scheduleId);
+
+
+        //4.删除IOT定时任务
+        if (StringUtils.isNotBlank(schedule.getDeviceConfig())) {
+            JSONArray deviceJsonArray = JSON.parseArray(schedule.getDeviceConfig());
+
+            for (Iterator<Object> iterator = deviceJsonArray.iterator(); iterator.hasNext(); ) {
+                JSONObject next = (JSONObject) iterator.next();
+                try {
+                    sendScheduleComand(next.getString("deviceId"), ScheduleComandType.D, schedule, new JSONObject());
+                } catch (DistscheduleScheduleException e) {
+                    LOGGER.error(e.getMessage(), e);
+                    //即使发送不成功也允许删除设备
+                }
+            }
+        }
+
+        // 给联系人的儿童手表通知日程
+        sendChildWatchSchedule(ScheduleComandType.D, schedule);
+        return res;
+    }
+
+    public int updateSchedule(String userId, String scheduleId, CreateScheduleDto updateScheduleDto) throws DistscheduleScheduleException {
+        Schedule schedule = scheduleMapper.selectScheduleById(scheduleId);
+        if (schedule == null) {
+            throw new DistscheduleScheduleException(ErrorCode.SCHEDULE_NOT_FOUND);
+        }
+
+        if (!StringUtils.equals(schedule.getCreatorId(), userId)) {
+            throw new DistscheduleScheduleException(ErrorCode.ILLEGAL_PERMISSION);
+        }
+
+        schedule.setName(updateScheduleDto.getName());
+        schedule.setDeviceConfig(updateScheduleDto.getDeviceConfig());
+        schedule.setRemindDay(updateScheduleDto.getRemindDay());
+        //schedule.setCreatorId(updateScheduleDto.getCreatorId());暂不修改该属性
+        //schedule.setSceneId(updateScheduleDto.getSceneId());暂不修改该属性
+        schedule.setStateDetail(updateScheduleDto.getStateDetail());
+        schedule.setStartTime(updateScheduleDto.getStartTime());
+        schedule.setEndTime(updateScheduleDto.getEndTime());
+
+        // 更新用户联系人~~后续优化
+        scheduleMapper.deleteUserSchedule(scheduleId);
+        List<UserSchedule> userSchedules = new LinkedList<>();
+        if (StringUtils.isNotBlank(updateScheduleDto.getUserIds())) {
+            String[] userIds = updateScheduleDto.getUserIds().split(",");
+            for (String scheduleUserId : userIds) {
+                userSchedules.add(new UserSchedule(scheduleUserId, schedule.getId()));
+            }
+        }
+        scheduleMapper.saveUserSchedule(userSchedules);
+        int res = scheduleMapper.updateSchedule(schedule);
+
+        // 通知IOT设备,发送失败后,暂不回滚。后续再改成异步,并处理异常。
+        if (StringUtils.isNotBlank(updateScheduleDto.getDeviceConfig())) {
+            JSONArray deviceJsonArray = JSON.parseArray(updateScheduleDto.getDeviceConfig());
+
+            for (Iterator<Object> iterator = deviceJsonArray.iterator(); iterator.hasNext(); ) {
+                JSONObject next = (JSONObject) iterator.next();
+                try {
+                    sendScheduleComand(next.getString("deviceId"), ScheduleComandType.U, schedule, next.getJSONObject("command"));
+                } catch (DistscheduleScheduleException e) {
+                    LOGGER.error(e.getMessage(), e);
+                    //即使发送不成功也允许更新设备
+                }
+            }
+        }
+
+        // 给联系人的儿童手表通知日程
+        sendChildWatchSchedule(ScheduleComandType.U, schedule);
+
+        return res;
+    }
+
+    public List<ScheduleDto> queryScheduleByUser(String userId, Date startTime, Date endTime) {
+        List<ScheduleDto> scheduleDtos = new LinkedList<>();
+        // 1.按起始时间查询日程
+        List<Schedule> defaultSchedules = scheduleMapper.selectDefaultSchedulesByDatesAndUser(userId, startTime, endTime);
+        for (Schedule defaultSchedule : defaultSchedules) {
+            ScheduleDto scheduleDto = new ScheduleDto(defaultSchedule);
+            scheduleDtos.add(scheduleDto);
+        }
+
+        // 2.查询非普通日程
+        List<Schedule> noDefaultSchedules = scheduleMapper.selectNotDefaultSchedulesByUser(userId);
+        for (Schedule noDefaultSchedule : noDefaultSchedules) {
+            ScheduleDto scheduleDto = new ScheduleDto(noDefaultSchedule);
+            scheduleDtos.add(scheduleDto);
+        }
+        Collections.sort(scheduleDtos);
+        return scheduleDtos;
+    }
+
+    public List<ScheduleDto> searchSchedulesByName(String userId, String name) {
+        // 查询前后30天的日程
+        Calendar calendarStartTime = Calendar.getInstance();
+        calendarStartTime.setTime(new Date());
+        calendarStartTime.add(Calendar.DATE, -30);
+
+        Calendar calendarEndTime = Calendar.getInstance();
+        calendarEndTime.setTime(new Date());
+        calendarEndTime.add(Calendar.DATE, +30);
+
+        List<ScheduleDto> scheduleDtos = new LinkedList<>();
+        // 1.按起始时间查询日程
+        List<Schedule> defaultSchedules = scheduleMapper.searchDefaultSchedulesByName(userId, calendarStartTime.getTime(), calendarEndTime.getTime(), name);
+        for (Schedule defaultSchedule : defaultSchedules) {
+            ScheduleDto scheduleDto = new ScheduleDto(defaultSchedule);
+            scheduleDtos.add(scheduleDto);
+        }
+
+        // 2.查询非普通日程
+        List<Schedule> noDefaultSchedules = scheduleMapper.searchNotDefaultSchedulesByName(userId, name);
+        for (Schedule noDefaultSchedule : noDefaultSchedules) {
+            ScheduleDto scheduleDto = new ScheduleDto(noDefaultSchedule);
+            scheduleDtos.add(scheduleDto);
+        }
+        return scheduleDtos;
+    }
+
+    @Transactional(rollbackFor = Exception.class)
+    public void deleteUserAllSchedules(String userId) {
+        scheduleMapper.deleteSchedulesByUserId(userId);
+        scheduleMapper.deleteUserScheduleByUserId(userId);
+    }
+
+    /**
+     * 给儿童手表发送命令
+     *
+     * @param scheduleComandType 命令类型
+     * @param schedule           日程
+     */
+    private void sendChildWatchSchedule(ScheduleComandType scheduleComandType, Schedule schedule) {
+        if (StringUtils.isNotBlank(schedule.getUserIds())) {
+            String[] userIds = schedule.getUserIds().split(",");
+            for (String userId : userIds) {
+                // 查到手表,给手表发送日程信息
+                List<DeviceDto> deviceInfos = deviceService.getDevicesByUserId(userId);
+                for (DeviceDto deviceInfo : deviceInfos) {
+                    if (deviceInfo.getTypeId() == 12) {//儿童手表的类型号
+                        try {
+                            sendChildWatchScheduleComand(deviceInfo.getId(), scheduleComandType, schedule);
+                        } catch (DistscheduleScheduleException e) {
+                            LOGGER.warn("Fail to send iot command for device " + deviceInfo.getId(), e);
+                        }
+                    }
+                }
+            }
+        }
+    }
+}

+ 96 - 0
Server/distschedule-service/src/main/java/com/example/distschedule/service/UserService.java

@@ -0,0 +1,96 @@
+/*
+ * Copyright (c) 2021 Huawei Device Co., Ltd.
+ * Licensed under the Apache License,Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ 
+package com.example.distschedule.service;
+
+import com.example.distschedule.dao.mapper.UserMapper;
+import com.example.distschedule.dao.model.User;
+import com.example.distschedule.dto.ResponseResult;
+import com.example.distschedule.dto.UserDeviceDto;
+import com.example.distschedule.dto.UserDto;
+import com.example.distschedule.error.ErrorCode;
+import com.example.distschedule.exception.DistscheduleUserException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+import java.util.Optional;
+import java.util.UUID;
+
+@Service
+public class UserService {
+    private static final Logger logger = LoggerFactory.getLogger(UserService.class);
+
+    @Autowired
+    private UserMapper userMapper;
+
+    public Optional<UserDto> getUserByPhone(String phone) {
+        return getUserByPhone(phone, false);
+    }
+
+    public Optional<UserDto> getUserByPhone(String phone, Boolean isAutoCreate) {
+        User user = userMapper.selectUserByPhone(phone);
+
+        if (user != null) {
+            return Optional.of(new UserDto(user));
+        }
+
+        //自动创建用户
+        if (isAutoCreate) {
+            User newUser = new User();
+            newUser.setId(UUID.randomUUID().toString());
+            newUser.setName(phone);
+            newUser.setPhone(phone);
+            userMapper.saveUser(newUser);
+
+            return this.getUserByPhone(phone);
+        }
+
+        return Optional.empty();
+    }
+
+
+    public int saveUser(UserDto newUserDto) {
+        User user = new User();
+        user.setId(UUID.randomUUID().toString());
+        user.setPhone(newUserDto.getPhone());
+        user.setName(newUserDto.getName());
+        return userMapper.saveUser(user);
+    }
+
+    public Optional<UserDto> getUserById(String userId) {
+        User user = userMapper.selectUserByPId(userId);
+        if (user == null) {
+            return Optional.empty();
+        }
+        return Optional.of(new UserDto(user));
+    }
+
+    public int updateUser(UserDto userDto) throws DistscheduleUserException {
+        User oldUser = userMapper.selectUserByPId(userDto.getId());
+
+        if (oldUser == null) {
+            throw new DistscheduleUserException(ErrorCode.USER_NOT_FOUND);
+        }
+
+        User user = new User();
+        user.setId(oldUser.getId());
+        user.setName(userDto.getName());
+        user.setPhone(oldUser.getPhone());//暂不更新手机
+        return userMapper.updateUser(user);
+    }
+}

+ 0 - 0
Server/distschedule-service/src/main/resources/.gitkeep


+ 0 - 0
Server/distschedule-service/src/main/resources/service.properties


+ 0 - 0
Server/distschedule-service/src/test/java/.gitkeep


+ 22 - 0
Server/docker-compose.yml

@@ -0,0 +1,22 @@
+version: "3.9"
+services:
+  mysql:
+    image: mysql:5.7
+    environment:
+      - MYSQL_ROOT_PASSWORD=root
+      - MYSQL_DATABASE=distschedule
+    ports:
+      - "3306:3306"
+  rabbitmq:
+    image: rabbitmq:3.6.10-management-alpine
+    ports:
+      - "15672:15672"
+  distschedule:
+    image: distschedule
+    environment:
+      - SPRING_PROFILES_ACTIVE=dockercompose
+    ports:
+      - "8080:8080"
+    depends_on:
+      - mysql
+      - rabbitmq

+ 235 - 0
Server/pom.xml

@@ -0,0 +1,235 @@
+<?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>
+    <packaging>pom</packaging>
+    <modules>
+        <module>distschedule-dao</module>
+        <module>distschedule-service</module>
+        <module>distschedule-core</module>
+    </modules>
+    <parent>
+        <groupId>org.springframework.boot</groupId>
+        <artifactId>spring-boot-starter-parent</artifactId>
+        <!--<version>2.1.6.RELEASE</version>-->
+        <version>2.2.9.RELEASE</version>
+        <relativePath/> <!-- lookup parent from repository -->
+    </parent>
+    <groupId>com.example.distschedule</groupId>
+    <artifactId>distschedule</artifactId>
+    <version>1.0.0-SNAPSHOT</version>
+    <name>distschedule</name>
+    <description>Distschedule Server</description>
+
+    <properties>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
+        <kubernetes-client.version>4.1.3</kubernetes-client.version>
+        <springfox-swagger.version>3.0.0</springfox-swagger.version>
+        <fastjson.version>1.2.70</fastjson.version>
+        <guava.version>27.0.1-jre</guava.version>
+        <code.findbugs>3.0.2</code.findbugs>
+        <spock.version>1.3-groovy-2.5</spock.version>
+        <commons-io.version>2.6</commons-io.version>
+        <commons-lang.version>3.9</commons-lang.version>
+        <java.version>1.8</java.version>
+        <maven.compiler.source>1.8</maven.compiler.source>
+        <maven.compiler.target>1.8</maven.compiler.target>
+        <jasypt.version>2.1.2</jasypt.version>
+        <gitlab4j.version>4.12.0</gitlab4j.version>
+        <qpid.jms.client>0.50.0</qpid.jms.client>
+
+        <!--<groovy.version>2.5.7</groovy.version>-->
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-devtools</artifactId>
+            <scope>runtime</scope>
+            <optional>true</optional>
+        </dependency>
+        <!-- exclude logback , add log4j2 -->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter</artifactId>
+            <exclusions>
+                <exclusion>
+                    <groupId>org.springframework.boot</groupId>
+                    <artifactId>spring-boot-starter-logging</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-log4j2</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-test</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>com.alibaba</groupId>
+            <artifactId>fastjson</artifactId>
+            <version>${fastjson.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>com.google.guava</groupId>
+            <artifactId>guava</artifactId>
+            <version>${guava.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>com.google.code.findbugs</groupId>
+            <artifactId>jsr305</artifactId>
+            <version>${code.findbugs}</version>
+        </dependency>
+        <!--
+        <dependency>
+            <groupId>org.spockframework</groupId>
+            <artifactId>spock-core</artifactId>
+            <version>${spock.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.spockframework</groupId>
+            <artifactId>spock-spring</artifactId>
+            <version>${spock.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.codehaus.groovy</groupId>
+            <artifactId>groovy</artifactId>
+            <version>${groovy.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.codehaus.groovy</groupId>
+            <artifactId>groovy-macro</artifactId>
+            <version>${groovy.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.codehaus.groovy</groupId>
+            <artifactId>groovy-nio</artifactId>
+            <version>${groovy.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.codehaus.groovy</groupId>
+            <artifactId>groovy-sql</artifactId>
+            <version>${groovy.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.codehaus.groovy</groupId>
+            <artifactId>groovy-templates</artifactId>
+            <version>${groovy.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.codehaus.groovy</groupId>
+            <artifactId>groovy-test</artifactId>
+            <version>${groovy.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.codehaus.groovy</groupId>
+            <artifactId>groovy-xml</artifactId>
+            <version>${groovy.version}</version>
+        </dependency>
+        -->
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <version>4.13</version>
+        </dependency>
+        <dependency>
+            <groupId>commons-io</groupId>
+            <artifactId>commons-io</artifactId>
+            <version>${commons-io.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.commons</groupId>
+            <artifactId>commons-lang3</artifactId>
+            <version>${commons-lang.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>com.github.ulisesbocchio</groupId>
+            <artifactId>jasypt-spring-boot-starter</artifactId>
+            <version>${jasypt.version}</version>
+        </dependency>
+    </dependencies>
+    <dependencyManagement>
+        <dependencies>
+            <dependency>
+                <groupId>com.example.distschedule</groupId>
+                <artifactId>distschedule-service</artifactId>
+                <version>${project.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>com.example.distschedule</groupId>
+                <artifactId>distschedule-dao</artifactId>
+                <version>${project.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>com.example.distschedule</groupId>
+                <artifactId>distschedule-core</artifactId>
+                <version>${project.version}</version>
+            </dependency>
+        </dependencies>
+    </dependencyManagement>
+
+    <build>
+        <resources>
+            <resource>
+                <directory>src/main/resources</directory>
+                <includes>
+                    <include>**/*.properties</include>
+                    <include>**/*.xml</include>
+                    <include>**/*.sql</include>
+                    <include>**/*.json</include>
+                    <include>**/*.txt</include>
+                    <include>**/*.jks</include>
+                    <include>**/*.cfg</include>
+                    <include>**/*.html</include>
+                </includes>
+                <filtering>false</filtering>
+            </resource>
+        </resources>
+        <plugins>
+            <plugin>
+                <groupId>org.codehaus.gmavenplus</groupId>
+                <artifactId>gmavenplus-plugin</artifactId>
+                <version>1.7.1</version>
+                <executions>
+                    <execution>
+                        <goals>
+                            <goal>compile</goal>
+                            <goal>compileTests</goal>
+                        </goals>
+                    </execution>
+                </executions>
+                <configuration>
+                    <testSources>
+                        <testSource>
+                            <directory>${project.basedir}/src/test/java</directory>
+                            <includes>
+                                <include>**/*Test.groovy</include>
+                            </includes>
+                        </testSource>
+                    </testSources>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <version>3.1</version>
+                <configuration>
+                    <encoding>utf-8</encoding>
+                    <source>1.8</source>
+                    <target>1.8</target>
+                    <showWarnings>true</showWarnings>
+                    <compilerArgs>
+                        <arg>-Xlint:all</arg>
+                    </compilerArgs>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+</project>

+ 6 - 0
Server/share.md

@@ -0,0 +1,6 @@
+# 数字管家服务端-开发者分享
+
+- 小小白:[<数字管家>服务器端](https://xiaokaijun.gitee.io/2022/01/06/04-server/)
+- 小小白:[<数字管家>华为云端配置](https://xiaokaijun.gitee.io/2022/01/06/03-cloud/)
+- 小源同学:[总结数字管家服务端配置过程的经验](https://developer.huawei.com/consumer/cn/forum/topic/0201779217710480339?fid=0102342714178070498)
+- 西瓜didi在路上:[学生党上手数字管家——服务端配置详细步骤](https://gitee.com/niuyouguodidi/shuziguanjiafuwuduan/blob/master/%E6%95%B0%E5%AD%97%E7%AE%A1%E5%AE%B6%E6%9C%8D%E5%8A%A1%E7%AB%AF%E6%90%AD%E5%BB%BA%E8%AF%A6%E7%BB%86%E8%AF%B4%E6%98%8E%E2%80%94%E2%80%94%E6%9C%89%E6%89%8B%E5%B0%B1%E5%8F%AF%E6%90%AD%E5%BB%BA%E7%B3%BB%E5%88%97.md)