新疆后端项目

This commit is contained in:
DESKTOP-BLB5287\FP
2025-06-30 14:31:09 +08:00
commit 0e52aff1f1
2596 changed files with 261030 additions and 0 deletions
+41
View File
@@ -0,0 +1,41 @@
// For format details, see https://aka.ms/devcontainer.json. For config options, see the
// README at: https://github.com/devcontainers/templates/tree/main/src/java
{
"name": "Java",
// Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile
"image": "mcr.microsoft.com/devcontainers/java:1-8-bullseye",
"features": {
"ghcr.io/devcontainers/features/java:1": {
"version": "none",
"installMaven": "true",
"installGradle": "false"
}
},
"runArgs": [
"--network",
"dev",
"-v",
"dev-maven-repo:/home/vscode/.m2"
],
"containerEnv": {
"NACOS_SERVER_ADDR": "nacos:8848",
"NACOS_USERNAME": "cqyt",
"NACOS_PASSWORD": "Aa123456",
"NACOS_NAMESPACE": "2639a2d4-7b64-4408-98c9-b97e0bdf4c1f"
},
remoteEnv: {
"PROFILE_NAME": "dev",
"NACOS_GROUP": "dev"
},
"build": {
"options": [
"-t", "cqyt-platform-dev-image"
]
},
"customizations": {
"jetbrains": {
"backend": "IntelliJ"
}
},
}
+5
View File
@@ -0,0 +1,5 @@
*.js linguist-language=Java
*.css linguist-language=Java
*.html linguist-language=Java
*.vue linguist-language=Java
*.sql linguist-language=Java
+16
View File
@@ -0,0 +1,16 @@
## ide
**/.idea
*.iml
rebel.xml
uploadFile
## backend
**/target
**/logs
## front
**/*.lock
## Fast Request Plugin
**/.fastRequest
**/test
+18
View File
@@ -0,0 +1,18 @@
FROM eclipse-temurin:8-jdk
#添加时区环境变量
ENV TZ Asia/Shanghai
#修改apt使用国内源
RUN sed -i 's/archive.ubuntu.com/mirrors.ustc.edu.cn/g' /etc/apt/sources.list \
&& sed -i 's/security.ubuntu.com/mirrors.ustc.edu.cn/g' /etc/apt/sources.list
#为镜像设置时区和字体包
RUN apt-get update \
&& apt-get install -y fonts-dejavu \
&& cp /usr/share/zoneinfo/${TZ} /etc/localtime \
&& echo ${TZ} > /etc/timezone
#将打包好的jar包封装进镜像
ARG JAR_NAME=health-evaluate
COPY ${JAR_NAME}.jar app.jar
#镜像运行命令
ENTRYPOINT [ "java", "-jar", "/app.jar" ]
#ENV LOG4J_CONTEXT_SELECTOR org.apache.logging.log4j.core.async.AsyncLoggerContextSelector
EXPOSE 10001
+273
View File
@@ -0,0 +1,273 @@
def FeiShu(status, serviceName){
def jobNameParts = JOB_NAME.tokenize('/') as String[]
def folderName = jobNameParts[0]
String statusText = ""
if (status == 1) {
statusText = "<font color=\\\"green\\\">成功<font>"
} else if (status == 2) {
statusText = "<font color=\\\"yellow\\\">不稳定<font>"
} else {
statusText = "<font color=\\\"red\\\">失败<font>"
}
env.COMMIT_SHORT_HASH = "${sh(script:'git log --pretty=format:\"%h\" --no-merges|head -1', returnStdout: true)}".trim()
env.COMMIT_MESSAGE = "${sh(script:'git log --pretty=format:\"%s\" --no-merges|head -1', returnStdout: true)}".trim()
env.COMMIT_AUTHOR = "${sh(script:'git log --pretty=format:\"%cn\" --no-merges|head -1', returnStdout: true)}".trim()
sh """
curl --location --request POST 'https://open.feishu.cn/open-apis/bot/v2/hook/1babaf75-a3e4-4bf0-b236-ee9112b5f68f' \
--header 'Content-Type: application/json' \
--data '{
"msg_type": "interactive",
"card": {
"config": {
"update_multi": true,
"enable_forward": true
},
"header": {
"template": "blue",
"title": {
"content": "项目 四合一后端 构建报告",
"tag": "plain_text"
}
},
"card_link": {
"url": "${JENKINS_URL}/blue/organizations/jenkins/${folderName}/detail/${BRANCH_NAME}/${BUILD_NUMBER}/pipeline",
"pc_url": "",
"android_url": "",
"ios_url": ""
},
"elements": [
{
"tag": "markdown",
"content": "**构建分支**${BRANCH_NAME}\\n**构建编号**${BUILD_NUMBER}\\n**提交成员**${COMMIT_AUTHOR}\\n**提交信息**${COMMIT_MESSAGE}\\n**构建服务**${serviceName}\\n**构建状态**${statusText}"
}
]
}
}'
"""
}
pipeline {
agent any
triggers {
pollSCM('*/5 * * * *')
}
environment {
PROJECT_NAME = 'cqyt_system'
}
stages{
stage("Update All Check") {
when {
anyOf {
branch "develop"
branch "test"
branch "master"
}
}
agent {
docker {
image "ubuntu:22.04"
args "-v hash_storage:/hash"
}
}
steps {
dir("${env.WORKSPACE}/jeecg-boot-base-core") {
sh '''#!/bin/bash -xe
cp -r /hash/${PROJECT_NAME}/env ./
buildFIle="./isBuild"
sumPath="/hash/${PROJECT_NAME}/jeecg-boot-base-core"
sumFile="${sumPath}/${BRANCH_NAME}"
checksum=$(find . -type f -exec md5sum {} + | LC_ALL=C sort | md5sum)
checksum=${checksum:0:32}
if [[ ! -d ${sumPath} ]]
then
mkdir -p ${sumPath}
fi
if [[ ! -f ${sumFile} ]]
then
echo ${checksum} > ${sumFile}
echo "TRUE" > ${buildFIle}
else
if [[ "${checksum}" = "$(cat ${sumFile})" ]]
then
echo "FALSE" > ${buildFIle}
else
echo ${checksum} > ${sumFile}
echo "TRUE" > ${buildFIle}
fi
fi
'''
script {
env.IS_BUILD = readFile('./isBuild').trim()
env.NACOS_SERVER_ADDR = readFile("./env/NACOS_SERVER_ADDR").trim()
env.NACOS_USERNAME = readFile("./env/NACOS_USERNAME").trim()
env.NACOS_PASSWORD = readFile("./env/NACOS_PASSWORD").trim()
env.NACOS_NAMESPACE = readFile("./env/NACOS_NAMESPACE").trim()
if ( env.BRANCH_NAME == 'master' ) {
env.NETWORK_NAME = 'stage'
} else
if ( env.BRANCH_NAME == 'test' ) {
env.NETWORK_NAME = 'test'
} else {
env.NETWORK_NAME = 'dev'
}
env.PROFILE_NAME = env.NETWORK_NAME
env.NACOS_GROUP = env.NETWORK_NAME
}
sh "rm -f ./isBuild"
}
}
}
stage("Microservice CI") {
when {
anyOf {
branch "develop"
branch "test"
branch "master"
}
}
matrix {
axes {
axis {
name 'SERVICE_NAME'
values 'jeecg-system', 'health-im', 'health-medical', 'health-emergency', 'health-consultation'
}
}
stages {
stage("Diff Check") {
agent {
docker {
image "ubuntu:22.04"
args "-v hash_storage:/hash"
}
}
steps {
dir("${env.WORKSPACE}/${SERVICE_NAME}") {
sh '''#!/bin/bash -xe
buildFIle="./isBuild"
if [[ "${IS_BUILD}" = "TRUE" ]]
then
echo "TRUE" > ${buildFIle}
exit 0
fi
sumPath="/hash/${PROJECT_NAME}/${SERVICE_NAME}"
sumFile="${sumPath}/${BRANCH_NAME}"
checksum=$(find . -type f -exec md5sum {} + | LC_ALL=C sort | md5sum)
checksum=${checksum:0:32}
if [[ ! -d ${sumPath} ]]
then
mkdir -p ${sumPath}
fi
if [[ ! -f ${sumFile} ]]
then
echo ${checksum} > ${sumFile}
echo "TRUE" > ${buildFIle}
else
if [[ "${checksum}" = "$(cat ${sumFile})" ]]
then
echo "FALSE" > ${buildFIle}
else
echo ${checksum} > ${sumFile}
echo "TRUE" > ${buildFIle}
fi
fi
'''
script {
env."${SERVICE_NAME}_IS_BUILD" = readFile('./isBuild').trim()
}
sh "rm -f ./isBuild"
}
}
}
stage("Maven Package") {
when {
environment name: "${SERVICE_NAME}_IS_BUILD", value: "TRUE"
}
agent {
docker {
image "maven:3-eclipse-temurin-8"
args "-v maven-repo:/root/.m2 -v maven-conf:/usr/share/maven/conf"
}
}
steps {
dir("${env.WORKSPACE}") {
echo "Package ${SERVICE_NAME}"
sh "mvn -pl health-system-boms,${SERVICE_NAME}/${SERVICE_NAME}-start -e -B -U -am clean install"
}
dir("${env.WORKSPACE}/${SERVICE_NAME}/${SERVICE_NAME}-start/target") {
echo "Copy ${SERVICE_NAME} JAR"
stash(name: "${SERVICE_NAME}_jar", includes: "${SERVICE_NAME}-start.jar")
}
dir("${env.WORKSPACE}") {
echo "Clean Build"
sh "mvn clean"
}
}
}
stage("Docker Build & Run") {
when {
environment name: "${SERVICE_NAME}_IS_BUILD", value: "TRUE"
}
agent any
environment {
REGISTER_HOST = "registry.cn-shanghai.aliyuncs.com"
REGISTER_REPO = "yg7709"
IMAGE_NAME = "${PROJECT_NAME}_${SERVICE_NAME}"
IMAGE_TAG_LATEST = "${BRANCH_NAME}-latest"
IMAGE_TAG_COMMIT = "${BRANCH_NAME}-${GIT_COMMIT}"
}
steps {
dir("${env.WORKSPACE}/.jenkins") {
unstash("${SERVICE_NAME}_jar")
echo "Docker Build ${SERVICE_NAME}"
sh "docker compose -f docker-compose.build.yaml build"
echo "Docker Push ${SERVICE_NAME}"
sh "docker push ${REGISTER_HOST}/${REGISTER_REPO}/${IMAGE_NAME}:${IMAGE_TAG_LATEST}"
sh "docker push ${REGISTER_HOST}/${REGISTER_REPO}/${IMAGE_NAME}:${IMAGE_TAG_COMMIT}"
echo "Docker Deploy ${SERVICE_NAME}"
sh "docker compose -p ${PROJECT_NAME}_${PROFILE_NAME} up -d ${SERVICE_NAME}"
}
}
post {
success {
FeiShu(1,SERVICE_NAME)
}
}
}
}
post {
unstable {
FeiShu(2,SERVICE_NAME)
}
failure {
FeiShu(3,SERVICE_NAME)
}
}
}
}
}
}
+14
View File
@@ -0,0 +1,14 @@
version: "3.9"
services:
ms-service:
build:
dockerfile: Dockerfile
context: .
tags:
- "${IMAGE_NAME}:${IMAGE_TAG_LATEST}"
- "${IMAGE_NAME}:${IMAGE_TAG_COMMIT}"
- "${REGISTER_HOST}/${REGISTER_REPO}/${IMAGE_NAME}:${IMAGE_TAG_LATEST}"
- "${REGISTER_HOST}/${REGISTER_REPO}/${IMAGE_NAME}:${IMAGE_TAG_COMMIT}"
args:
JAR_NAME: ${SERVICE_NAME}-start
+102
View File
@@ -0,0 +1,102 @@
#版本
version: '3.9'
x-template:
service: &service_tmp
environment:
- NACOS_SERVER_ADDR=${NACOS_SERVER_ADDR}
- NACOS_USERNAME=${NACOS_USERNAME}
- NACOS_PASSWORD=${NACOS_PASSWORD}
- NACOS_NAMESPACE=${NACOS_NAMESPACE}
- NACOS_GROUP=${NACOS_GROUP}
- PROFILE_NAME=${PROFILE_NAME}
restart: always
tty: true
deploy:
resources:
limits:
cpus: '4'
memory: 4G
reservations:
cpus: '1'
memory: 512M
extra_hosts:
- "yg.dt.io:192.168.1.5"
#服务配置
services:
jeecg-system:
<<: *service_tmp
container_name: system-${PROFILE_NAME}
hostname: system-${PROFILE_NAME}
image: ${REGISTER_HOST}/${REGISTER_REPO}/${PROJECT_NAME}_jeecg-system:${IMAGE_TAG_LATEST}
healthcheck:
test: [ "CMD", "curl", "-f", "http://localhost:7001/actuator/health" ]
interval: 2m
timeout: 2s
retries: 5
start_period: 5m
health-consultation:
<<: *service_tmp
container_name: consultation-${PROFILE_NAME}
hostname: consultation-${PROFILE_NAME}
image: ${REGISTER_HOST}/${REGISTER_REPO}/${PROJECT_NAME}_health-consultation:${IMAGE_TAG_LATEST}
health-emergency:
<<: *service_tmp
container_name: emergency-${PROFILE_NAME}
hostname: emergency-${PROFILE_NAME}
image: ${REGISTER_HOST}/${REGISTER_REPO}/${PROJECT_NAME}_health-emergency:${IMAGE_TAG_LATEST}
health-im:
<<: *service_tmp
container_name: im-${PROFILE_NAME}
hostname: im-${PROFILE_NAME}
image: ${REGISTER_HOST}/${REGISTER_REPO}/${PROJECT_NAME}_health-im:${IMAGE_TAG_LATEST}
health-medical:
<<: *service_tmp
container_name: medical-${PROFILE_NAME}
hostname: medical-${PROFILE_NAME}
image: ${REGISTER_HOST}/${REGISTER_REPO}/${PROJECT_NAME}_health-medical:${IMAGE_TAG_LATEST}
health-medical-gi:
<<: *service_tmp
container_name: medical-gi-${PROFILE_NAME}
hostname: medical-gi-${PROFILE_NAME}
image: ${REGISTER_HOST}/${REGISTER_REPO}/${PROJECT_NAME}_health-medical-gi:${IMAGE_TAG_LATEST}
health-archives:
<<: *service_tmp
container_name: archives-${PROFILE_NAME}
hostname: archives-${PROFILE_NAME}
image: ${REGISTER_HOST}/${REGISTER_REPO}/${PROJECT_NAME}_health-archives:${IMAGE_TAG_LATEST}
health-watch:
<<: *service_tmp
container_name: watch-${PROFILE_NAME}
hostname: watch-${PROFILE_NAME}
image: ${REGISTER_HOST}/${REGISTER_REPO}/${PROJECT_NAME}_health-watch:${IMAGE_TAG_LATEST}
health-intervene:
<<: *service_tmp
container_name: intervene-${PROFILE_NAME}
hostname: intervene-${PROFILE_NAME}
image: ${REGISTER_HOST}/${REGISTER_REPO}/${PROJECT_NAME}_health-intervene:${IMAGE_TAG_LATEST}
depends_on:
jeecg-system:
condition: service_healthy
medical-center:
<<: *service_tmp
container_name: medical-center-${PROFILE_NAME}
hostname: medical-center-${PROFILE_NAME}
image: ${REGISTER_HOST}/${REGISTER_REPO}/${PROJECT_NAME}_medical-center:${IMAGE_TAG_LATEST}
networks:
default:
name: ${NETWORK_NAME}
external: true
+213
View File
@@ -0,0 +1,213 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright (c) 2019 <a href="http://www.jeecg.com">Jeecg Boot</a> All rights reserved.
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.
In any case, you must not make any such use of this software as to develop software which may be considered competitive with this software.
开源协议补充
JeecgBoot 是由 北京敲敲云科技有限公司 发行的软件。 总部位于北京,地址:中国·北京·朝阳区科荟前街1号院奥林佳泰大厦。邮箱:jeecgos@163.com
本软件受适用的国家软件著作权法(包括国际条约)和双重保护许可。
1.允许基于本平台软件开展业务系统开发。
2.不得基于该平台软件的基础,修改包装成一个与JeecgBoot平台软件功能类似的产品进行发布、销售,或与JeecgBoot参与同类软件产品市场的竞争。
违反此条款属于侵权行为,须赔偿侵权经济损失,同时立即停止著作权侵权行为。
解释权归:http://www.jeecg.com
+4
View File
@@ -0,0 +1,4 @@
## **新疆油田项目**
### **项目介绍**
新疆油田项目后端微服务。
+13
View File
@@ -0,0 +1,13 @@
FROM mysql:8.0.19
MAINTAINER jeecgos@163.com
ENV TZ=Asia/Shanghai
RUN ln -sf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
COPY ./tables_nacos.sql /docker-entrypoint-initdb.d
COPY ./jeecgboot-mysql-5.7.sql /docker-entrypoint-initdb.d
COPY ./tables_xxl_job.sql /docker-entrypoint-initdb.d
+146
View File
@@ -0,0 +1,146 @@
spring:
datasource:
druid:
stat-view-servlet:
enabled: true
loginUsername: admin
loginPassword: 123456
allow:
web-stat-filter:
enabled: true
dynamic:
druid:
initial-size: 5
min-idle: 5
maxActive: 20
maxWait: 60000
timeBetweenEvictionRunsMillis: 60000
minEvictableIdleTimeMillis: 300000
validationQuery: SELECT 1 FROM DUAL
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
poolPreparedStatements: true
maxPoolPreparedStatementPerConnectionSize: 20
filters: stat,wall,slf4j
connectionProperties: druid.stat.mergeSql\=true;druid.stat.slowSqlMillis\=5000
datasource:
master:
url: jdbc:mysql://jeecg-boot-mysql:3306/jeecg-boot?characterEncoding=UTF-8&useUnicode=true&useSSL=false&tinyInt1isBit=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai
username: root
password: root
driver-class-name: com.mysql.cj.jdbc.Driver
redis:
database: 0
host: jeecg-boot-redis
password:
port: 6379
rabbitmq:
host: jeecg-boot-rabbitmq
username: guest
password: guest
port: 5672
publisher-confirms: true
publisher-returns: true
virtual-host: /
listener:
simple:
acknowledge-mode: manual
concurrency: 1
max-concurrency: 1
retry:
enabled: true
minidao:
base-package: org.jeecg.modules.jmreport.*,org.jeecg.modules.drag.*
jeecg:
signatureSecret: dd05f1c54d63749eda95f9fa6d49v442a
signUrls: /sys/dict/getDictItems/*,/sys/dict/loadDict/*,/sys/dict/loadDictOrderByValue/*,/sys/dict/loadDictItem/*,/sys/dict/loadTreeData,/sys/api/queryTableDictItemsByCode,/sys/api/queryFilterTableDictInfo,/sys/api/queryTableDictByKeys,/sys/api/translateDictFromTable,/sys/api/translateDictFromTableByKeys
uploadType: local
domainUrl:
pc: http://localhost:3100
app: http://localhost:8051
path:
upload: /opt/upFiles
webapp: /opt/webapp
shiro:
excludeUrls: /test/jeecgDemo/demo3,/test/jeecgDemo/redisDemo/**,/category/**,/visual/**,/map/**,/jmreport/bigscreen2/**
oss:
endpoint: oss-cn-beijing.aliyuncs.com
accessKey: ??
secretKey: ??
bucketName: jeecgdev
staticDomain: ??
elasticsearch:
cluster-name: jeecg-ES
cluster-nodes: jeecg-boot-es:9200
check-enabled: false
file-view-domain: 127.0.0.1:8012
minio:
minio_url: http://minio.jeecg.com
minio_name: ??
minio_pass: ??
bucketName: otatest
jmreport:
mode: dev
is_verify_token: false
verify_methods: remove,delete,save,add,update
wps:
domain: https://wwo.wps.cn/office/
appid: ??
appsecret: ??
xxljob:
enabled: false
adminAddresses: http://jeecg-boot-xxljob:9080/xxl-job-admin
appname: ${spring.application.name}
accessToken: ''
logPath: logs/jeecg/job/jobhandler/
logRetentionDays: 30
redisson:
address: jeecg-boot-redis:6379
password:
type: STANDALONE
enabled: true
logging:
level:
org.jeecg.modules.system.mapper: info
cas:
prefixUrl: http://localhost:8888/cas
knife4j:
production: false
basic:
enable: false
username: jeecg
password: jeecg1314
justauth:
enabled: true
type:
GITHUB:
client-id: ??
client-secret: ??
redirect-uri: http://sso.test.com:8080/jeecg-boot/thirdLogin/github/callback
WECHAT_ENTERPRISE:
client-id: ??
client-secret: ??
redirect-uri: http://sso.test.com:8080/jeecg-boot/thirdLogin/wechat_enterprise/callback
agent-id: ??
DINGTALK:
client-id: ??
client-secret: ??
redirect-uri: http://sso.test.com:8080/jeecg-boot/thirdLogin/dingtalk/callback
cache:
type: default
prefix: 'demo::'
timeout: 1h
third-app:
enabled: false
type:
WECHAT_ENTERPRISE:
enabled: false
client-id: ??
client-secret: ??
agent-id: ??
DINGTALK:
enabled: false
client-id: ??
client-secret: ??
agent-id: ??
+13
View File
@@ -0,0 +1,13 @@
jeecg:
route:
config:
#type:database nacos yml
data-type: database
group: DEFAULT_GROUP
data-id: jeecg-gateway-router
spring:
redis:
database: 0
host: jeecg-boot-redis
port: 6379
password:
+65
View File
@@ -0,0 +1,65 @@
[
{
"id": "jeecg-system",
"order": 0,
"predicates": [
{
"name": "Path",
"args": {
"_genkey_0": "/sys/**",
"_genkey_1": "/jmreport/**",
"_genkey_3": "/online/**",
"_genkey_4": "/generic/**"
}
}
],
"filters": [],
"uri": "lb://jeecg-system"
},
{
"id": "jeecg-demo",
"order": 1,
"predicates": [
{
"name": "Path",
"args": {
"_genkey_0": "/mock/**",
"_genkey_1": "/test/**",
"_genkey_2": "/bigscreen/template1/**",
"_genkey_3": "/bigscreen/template2/**"
}
}
],
"filters": [],
"uri": "lb://jeecg-demo"
},
{
"id": "jeecg-system-websocket",
"order": 2,
"predicates": [
{
"name": "Path",
"args": {
"_genkey_0": "/websocket/**",
"_genkey_1": "/newsWebsocket/**"
}
}
],
"filters": [],
"uri": "lb:ws://jeecg-system"
},
{
"id": "jeecg-demo-websocket",
"order": 3,
"predicates": [
{
"name": "Path",
"args": {
"_genkey_0": "/vxeSocket/**"
}
}
],
"filters": [],
"uri": "lb:ws://jeecg-demo"
}
]
+100
View File
@@ -0,0 +1,100 @@
server:
tomcat:
max-swallow-size: -1
error:
include-exception: true
include-stacktrace: ALWAYS
include-message: ALWAYS
compression:
enabled: true
min-response-size: 1024
mime-types: application/javascript,application/json,application/xml,text/html,text/xml,text/plain,text/css,image/*
management:
health:
mail:
enabled: false
endpoints:
web:
exposure:
include: "*"
health:
sensitive: true
endpoint:
health:
show-details: ALWAYS
spring:
servlet:
multipart:
max-file-size: 10MB
max-request-size: 10MB
mail:
host: smtp.163.com
username: jeecgos@163.com
password: ??
properties:
mail:
smtp:
auth: true
starttls:
enable: true
required: true
quartz:
job-store-type: jdbc
initialize-schema: embedded
auto-startup: false
startup-delay: 1s
overwrite-existing-jobs: true
properties:
org:
quartz:
scheduler:
instanceName: MyScheduler
instanceId: AUTO
jobStore:
class: org.springframework.scheduling.quartz.LocalDataSourceJobStore
driverDelegateClass: org.quartz.impl.jdbcjobstore.StdJDBCDelegate
tablePrefix: QRTZ_
isClustered: true
misfireThreshold: 12000
clusterCheckinInterval: 15000
threadPool:
class: org.quartz.simpl.SimpleThreadPool
threadCount: 10
threadPriority: 5
threadsInheritContextClassLoaderOfInitializingThread: true
jackson:
date-format: yyyy-MM-dd HH:mm:ss
time-zone: GMT+8
aop:
proxy-target-class: true
activiti:
check-process-definitions: false
async-executor-activate: false
job-executor-activate: false
jpa:
open-in-view: false
freemarker:
suffix: .ftl
content-type: text/html
charset: UTF-8
cache: false
prefer-file-system-access: false
template-loader-path:
- classpath:/templates
mvc:
static-path-pattern: /**
pathmatch:
matching-strategy: ant_path_matcher
resource:
static-locations: classpath:/static/,classpath:/public/
autoconfigure:
exclude: com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure
mybatis-plus:
mapper-locations: classpath*:org/jeecg/modules/**/xml/*Mapper.xml
global-config:
banner: false
db-config:
id-type: ASSIGN_ID
table-underline: true
configuration:
call-setters-on-nulls: true
@@ -0,0 +1,59 @@
spring:
shardingsphere:
datasource:
names: ds0,ds1
ds0:
driverClassName: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://jeecg-boot-mysql:3306/jeecg-boot?useSSL=false&useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai
type: com.alibaba.druid.pool.DruidDataSource
username: root
password: root
ds1:
driverClassName: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://jeecg-boot-mysql:3306/jeecg-boot2?useSSL=false&useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai
type: com.alibaba.druid.pool.DruidDataSource
username: root
password: root
props:
sql-show: true
rules:
replica-query:
load-balancers:
round-robin:
type: ROUND_ROBIN
props:
default: 0
data-sources:
prds:
primary-data-source-name: ds0
replica-data-source-names: ds1
load-balancer-name: round_robin
sharding:
binding-tables:
- sys_log
key-generators:
snowflake:
type: SNOWFLAKE
props:
worker-id: 123
sharding-algorithms:
table-classbased:
props:
strategy: standard
algorithmClassName: org.jeecg.modules.test.sharding.algorithm.StandardModTableShardAlgorithm
type: CLASS_BASED
database-inline:
type: INLINE
props:
algorithm-expression: ds$->{operate_type % 2}
tables:
sys_log:
actual-data-nodes: ds$->{0..1}.sys_log$->{0..1}
database-strategy:
standard:
sharding-column: operate_type
sharding-algorithm-name: database-inline
table-strategy:
standard:
sharding-algorithm-name: table-classbased
sharding-column: log_type
@@ -0,0 +1,33 @@
spring:
shardingsphere:
datasource:
names: ds0
ds0:
driverClassName: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://jeecg-boot-mysql:3306/jeecg-boot?useSSL=false&useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai
username: root
password: root
type: com.alibaba.druid.pool.DruidDataSource
props:
sql-show: true
rules:
sharding:
binding-tables: sys_log
key-generators:
snowflake:
type: SNOWFLAKE
props:
worker-id: 123
sharding-algorithms:
table-classbased:
props:
strategy: standard
algorithmClassName: org.jeecg.modules.test.sharding.algorithm.StandardModTableShardAlgorithm
type: CLASS_BASED
tables:
sys_log:
actual-data-nodes: ds0.sys_log$->{0..1}
table-strategy:
standard:
sharding-algorithm-name: table-classbased
sharding-column: log_type
@@ -0,0 +1,45 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.renkang</groupId>
<artifactId>functional-modules</artifactId>
<version>2.0.0</version>
</parent>
<artifactId>data-center-api</artifactId>
<version>2.0.0</version>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
</dependency>
<dependency>
<groupId>org.jeecgframework.boot</groupId>
<artifactId>jeecg-boot-base-core</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
</project>
@@ -0,0 +1,72 @@
package com.renkang.datacenter.api;
import com.renkang.datacenter.bean.*;
import com.renkang.datacenter.config.ImcCenterConstants;
import org.jeecg.common.api.vo.Result;
import org.springframework.retry.annotation.Retryable;
/**
* @author Shunzhi Jiang
* @since 2023/11/5
*/
@Retryable(maxAttempts = 2, listeners = "restRetryListener")
public interface DataCenterExecutor {
Result<Object> addDevice(WatchDevice device);
Result<Object> addDeviceBatch(WatchDeviceBatch devices);
Result<Object> removeDevice(WatchNo watchNo);
Result<Object> setSwitchStatus(WatchDeviceSwitch watchDeviceSwitch);
Result<Object> setSwitchStatusAll(DeviceSwitch deviceSwitch);
Result<Object> cmdNotice(Notice notice);
Result<Object> cmdNoticeMulti(Notice notice);
Result<Object> watchDataList(WatchDataList watchDataList);
Result<Object> increaseWatchDataCount(IncreaseWatchDataCount dataCount);
Result<Object> increaseWatchDataList(IncreaseWatchDataList dataList);
Result<Object> watchSdcItemData(WatchSdcItemData sdcItemData);
Result<Object> deviceInfoAll(WatchNo watchNo);
Result<Object> getRate(WatchNo watchNo);
Result<Object> getPeriod(WatchNo watchNo);
Result<Object> getWarnSwitch(WatchNo watchNo);
Result<Object> saveRate(Rate rate);
Result<Object> savePeriod(Period period);
Result<Object> saveWarnSwitch(WarnSwitch warnSwitch);
Result<Object> cmdDataPush(String watchNo);
Result<Object> cmdUpdatePeriod(String watchNo);
Result<Object> cmdUpdateTask(String watchNo);
Result<Object> cmdUpdateSwitch(String watchNo);
@Retryable(maxAttempts = 2, listeners = ImcCenterConstants.IMC_LISTENER)
Result<Object> getImageMessage(ImcPeIdVo imcPeIdVo);
/**
* 通过peId获取影像基本信息
* @param imcPeIdVo
* @return
*/
@Retryable(maxAttempts = 2, listeners = ImcCenterConstants.IMC_LISTENER)
Result<Object> getImageItems(ImcPeIdVo imcPeIdVo);
String downloadAsString(String filepath);
}
@@ -0,0 +1,185 @@
package com.renkang.datacenter.api;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.renkang.datacenter.bean.*;
import com.renkang.datacenter.config.DataCenterConstants;
import com.renkang.datacenter.config.DataCenterProperties;
import com.renkang.datacenter.config.MethodApi;
import com.renkang.datacenter.util.RemoteUtils;
import org.jeecg.common.api.vo.Result;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.Map;
import java.util.Objects;
@Service
public class DefaultDataCenterExecutor implements DataCenterExecutor {
private DataCenterProperties properties;
@Autowired
public void setProperties(DataCenterProperties properties) {
this.properties = properties;
}
@Override
public Result<Object> addDevice(WatchDevice device) {
return RemoteUtils.post(MethodApi.ADD_DEVICE,device);
}
@Override
public Result<Object> addDeviceBatch(WatchDeviceBatch devices) {
return RemoteUtils.post(MethodApi.ADD_DEVICE_BATCH,devices);
}
@Override
public Result<Object> removeDevice(WatchNo watchNo) {
return RemoteUtils.post(MethodApi.REMOVE_DEVICE, objectToMap(watchNo));
}
@Override
public Result<Object> setSwitchStatus(WatchDeviceSwitch watchDeviceSwitch) {
return RemoteUtils.post(MethodApi.SET_SWITCH_STATUS, watchDeviceSwitch);
}
@Override
public Result<Object> setSwitchStatusAll(DeviceSwitch deviceSwitch) {
return RemoteUtils.post(MethodApi.SET_SWITCH_STATUS_ALL, deviceSwitch);
}
@Override
public Result<Object> cmdNotice(Notice notice) {
return RemoteUtils.post(MethodApi.CMD_NOTICE, objectToMap(notice));
}
@Override
public Result<Object> cmdNoticeMulti(Notice notice) {
return RemoteUtils.post(MethodApi.CMD_NOTICE_MULTI, objectToMap(notice));
}
@Override
public Result<Object> watchDataList(WatchDataList watchDataList) {
return RemoteUtils.get(MethodApi.WATCH_DATA_LIST, objectToMap(watchDataList));
}
@Override
public Result<Object> increaseWatchDataCount(IncreaseWatchDataCount dataCount) {
if (Objects.isNull(dataCount.getPageSize()) || dataCount.getPageSize() == 0) {
dataCount.setPageSize(properties.getPageSize());
}
return RemoteUtils.get(MethodApi.INCREASE_WATCH_DATA_PAGE_COUNT, objectToMap(dataCount));
}
@Override
public Result<Object> increaseWatchDataList(IncreaseWatchDataList dataList) {
if (Objects.isNull(dataList.getPageSize()) || dataList.getPageSize() == 0) {
dataList.setPageSize(properties.getPageSize());
}
return RemoteUtils.get(MethodApi.INCREASE_WATCH_DATA_PAGE_LIST, objectToMap(dataList));
}
@Override
public Result<Object> watchSdcItemData(WatchSdcItemData sdcItemData) {
return RemoteUtils.get(MethodApi.WATCH_SDC_ITEM_DATA, objectToMap(sdcItemData));
}
@Override
public Result<Object> deviceInfoAll(WatchNo watchNo) {
return RemoteUtils.get(MethodApi.DEVICE_INFO_ALL, objectToMap(watchNo));
}
@Override
public Result<Object> getRate(WatchNo watchNo) {
return RemoteUtils.get(MethodApi.GET_RATE, objectToMap(watchNo));
}
@Override
public Result<Object> getPeriod(WatchNo watchNo) {
return RemoteUtils.get(MethodApi.GET_PERIOD, objectToMap(watchNo));
}
@Override
public Result<Object> getWarnSwitch(WatchNo watchNo) {
return RemoteUtils.get(MethodApi.GET_WARN_SWITCH, objectToMap(watchNo));
}
@Override
public Result<Object> saveRate(Rate rate) {
return RemoteUtils.post(MethodApi.SAVE_RATE, rate);
}
@Override
public Result<Object> savePeriod(Period period) {
return RemoteUtils.post(MethodApi.SAVE_PERIOD, period);
}
@Override
public Result<Object> saveWarnSwitch(WarnSwitch warnSwitch) {
return RemoteUtils.post(MethodApi.SAVE_WARN_SWITCH, warnSwitch);
}
@Override
public Result<Object> cmdDataPush(String watchNo) {
MqttTask task = new MqttTask();
task.setWatchNo(watchNo);
task.setTaskName(DataCenterConstants.MQTT_CMD_DATA_PUSH);
return cmdDataPush(task);
}
@Override
public Result<Object> cmdUpdatePeriod(String watchNo) {
MqttTask task = new MqttTask();
task.setWatchNo(watchNo);
task.setTaskName(DataCenterConstants.MQTT_CMD_UPDATE_PERIOD);
return cmdDataPush(task);
}
@Override
public Result<Object> cmdUpdateTask(String watchNo) {
MqttTask task = new MqttTask();
task.setWatchNo(watchNo);
task.setTaskName(DataCenterConstants.MQTT_CMD_UPDATE_TASK);
return cmdDataPush(task);
}
@Override
public Result<Object> cmdUpdateSwitch(String watchNo) {
MqttTask task = new MqttTask();
task.setWatchNo(watchNo);
task.setTaskName(DataCenterConstants.MQTT_CMD_UPDATE_SWITCH);
return cmdDataPush(task);
}
@Override
public Result<Object> getImageMessage(ImcPeIdVo imcPeIdVo) {
return RemoteUtils.post(MethodApi.GET_IMAGE_MESSAGE, imcPeIdVo);
}
/**
* 通过peId获取影像基本信息
*
* @param imcPeIdVo
* @return
*/
@Override
public Result<Object> getImageItems(ImcPeIdVo imcPeIdVo) {
return RemoteUtils.post(MethodApi.GET_IMAGE_ITEMS, imcPeIdVo);
}
@Override
public String downloadAsString(String filepath) {
return RemoteUtils.downloadAsString(filepath);
}
private Result<Object> cmdDataPush(MqttTask task) {
return RemoteUtils.post(MethodApi.CMD_DATA_PUSH, objectToMap(task));
}
private Map<String,Object> objectToMap(Object source) {
return (JSONObject) JSON.toJSON(source);
}
}
@@ -0,0 +1,22 @@
package com.renkang.datacenter.bean;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
/**
* @author Shunzhi Jiang
* @since 2023/11/13
*/
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Data
public class DataCommon extends WatchDataBase {
private String wdType;
private Double dataValue;
private String timeStamp;
}
@@ -0,0 +1,40 @@
package com.renkang.datacenter.bean;
import com.alibaba.fastjson.annotation.JSONField;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import java.util.Date;
/**
* @author Jiang Shunzhi
*/
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Data
public class DataEcg extends WatchDataBase {
private Long ecgStartTime;
private Long ecgEndTime;
@JSONField(format = "yyyy-MM-dd")
private Date dataDate;
private Integer ecgArrhyType;
private String ecgArrhyTypeDict;
private Integer ecgUserSymptom;
private String ecgUserSymptomDict;
private Integer ecgArrhyAvgRate;
private Integer ecgDataPointsCount;
private String ecgDataPoints;
}
@@ -0,0 +1,29 @@
package com.renkang.datacenter.bean;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
/**
* @author Shunzhi Jiang
* @since 2023/11/13
*/
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Data
public class DataExercise extends WatchDataBase {
/**
* 中高强度运动时间(分钟)
*/
private Integer strengthTimes;
/**
* 总活动时长(小时)
*/
private Integer totalTime;
/**
* 数据日期
*/
private String dataDate;
}
@@ -0,0 +1,24 @@
package com.renkang.datacenter.bean;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
/**
* @author Shunzhi Jiang
* @since 2023/11/13
*/
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Data
public class DataGps extends WatchDataBase {
private Double lon;
private Double lat;
private String address;
private String timeStamp;
}
@@ -0,0 +1,22 @@
package com.renkang.datacenter.bean;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
/**
* @author Shunzhi Jiang
* @since 2023/11/13
*/
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Data
public class DataHeartRate extends WatchDataBase {
private Double dataValue;
private Double silenceValue;
private String timeStamp;
}
@@ -0,0 +1,22 @@
package com.renkang.datacenter.bean;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
/**
* @author Shunzhi Jiang
* @since 2023/11/13
*/
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Data
public class DataSdc extends WatchDataBase {
private String sdcDate;
private Long dataValue;
private String wdType;
}
@@ -0,0 +1,22 @@
package com.renkang.datacenter.bean;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
/**
* @author Shunzhi Jiang
* @since 2023/11/13
*/
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Data
public class DataSleep extends WatchDataBase {
private String startTimeStamp;
private String endTimeStamp;
private String sleepType;
}
@@ -0,0 +1,109 @@
package com.renkang.datacenter.bean;
import com.alibaba.fastjson.annotation.JSONField;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import java.io.Serializable;
import java.util.List;
/**
* @author Shunzhi Jiang
* @since 2023/11/13
*/
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Data
public class DataSleepNew extends WatchDataBase {
private String sleepFlag;
@JSONField(alternateNames = "DataDate")
private String dataDate;
private Integer errCode;
private List<Error> errCodeArr;
private List<Day> statusInDayArr;
private List<Minute> statusInMinuteArr;
@Data
public static class Error implements Serializable {
private Integer errCode;
private String startTime;
private String endTime;
}
@Data
public static class Day implements Serializable {
/**
* 睡梦时长(分钟)
*/
private Integer deepSleepPartCnt;
/**
* 入睡时间点
*/
private String fallAsleepTime;
/**
* 上床时间
*/
private String goBedTime;
/**
* 睡眠效率百分比
*/
private Integer sleepEfficiency;
/**
* 睡眠潜伏期
*/
private Long sleepLatency;
/**
* 睡眠得分
*/
private Integer sleepScore;
/**
* 原始睡眠得分
*/
@JSONField(alternateNames = "sleepScoreOrign")
private Integer sleepScoreOrigin;
/**
* 鼾声(每小时次数)
*/
private Integer snoreFreq;
/**
* 当天的时间戳
*/
private String startTime;
/**
* 数据有效性
*/
private Double validData;
/**
* 醒来时间
*/
private String wakeUpTime;
}
@Data
public static class Minute implements Serializable {
/**
* 睡眠状态
*/
private String status;
/**
* 开始时间
*/
private String startTime;
/**
* 结束时间
*/
private String endTime;
}
}
@@ -0,0 +1,27 @@
package com.renkang.datacenter.bean;
import com.alibaba.fastjson.annotation.JSONField;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import java.util.Date;
/**
* @author Shunzhi Jiang
* @since 2023/11/13
*/
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Data
public class DataSteps extends WatchDataBase {
/**
* 数据日期
*/
@JSONField(format = "yyyy-MM-dd")
private Date stepDate;
private Integer stepCount;
}
@@ -0,0 +1,22 @@
package com.renkang.datacenter.bean;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
/**
* @author Shunzhi Jiang
* @since 2023/11/13
*/
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Data
public class DataStress extends WatchDataBase {
private Double dataValue;
private String startTimeStamp;
private String endTimeStamp;
}
@@ -0,0 +1,22 @@
package com.renkang.datacenter.bean;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
/**
* @author Shunzhi Jiang
* @since 2023/11/13
*/
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Data
public class DataTemperature extends WatchDataBase {
private Double skinTempera;
private Double dataValue;
private String timeStamp;
}
@@ -0,0 +1,38 @@
package com.renkang.datacenter.bean;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
/**
* @author Shunzhi Jiang
* @since 2023/11/13
*/
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Data
public class DataTrace extends WatchDataBase {
private String recordId;
/**
* 海拔
*/
private Double altitude;
/**
* 纬度
*/
private Double latitude;
/**
* 经度
*/
private Double longitude;
/**
* 时间戳
*/
private String utcTime;
/**
* 数据是否合法
*/
private String valid;
}
@@ -0,0 +1,25 @@
package com.renkang.datacenter.bean;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
/**
* @author Shunzhi Jiang
* @since 2023/11/13
*/
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Data
public class DataWorkout extends WatchDataBase {
private String workoutType;
private Double calorie;
private Double distance;
private String startTimeStamp;
private String endTimeStamp;
}
@@ -0,0 +1,17 @@
package com.renkang.datacenter.bean;
import lombok.Data;
import java.io.Serializable;
/**
* @author Shunzhi Jiang
* @since 2023/11/5
*/
@Data
public class DeviceSwitch implements Serializable {
private String wdType;
private String switchFlag;
}
@@ -0,0 +1,11 @@
package com.renkang.datacenter.bean;
import lombok.Data;
@Data
public class ImcPeIdVo {
private String peId;
private String hospitalId;
}
@@ -0,0 +1,20 @@
package com.renkang.datacenter.bean;
import lombok.Data;
import java.io.Serializable;
/**
* @author Shunzhi Jiang
* @since 2023/11/5
*/
@Data
public class IncreaseWatchDataCount implements Serializable {
private String wdType;
private String id;
private Integer pageSize;
}
@@ -0,0 +1,20 @@
package com.renkang.datacenter.bean;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import java.io.Serializable;
/**
* @author Shunzhi Jiang
* @since 2023/11/5
*/
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Data
public class IncreaseWatchDataList extends IncreaseWatchDataCount implements Serializable {
private Integer pageNo;
}
@@ -0,0 +1,14 @@
package com.renkang.datacenter.bean;
import lombok.Data;
import java.io.Serializable;
@Data
public class Login implements Serializable {
private String username;
private String password;
}
@@ -0,0 +1,18 @@
package com.renkang.datacenter.bean;
import lombok.Data;
import java.io.Serializable;
/**
* @author Shunzhi Jiang
* @since 2023/11/5
*/
@Data
public class MqttTask implements Serializable {
private String watchNo;
private String taskName;
}
@@ -0,0 +1,20 @@
package com.renkang.datacenter.bean;
import lombok.Data;
import java.io.Serializable;
/**
* @author Shunzhi Jiang
* @since 2023/11/5
*/
@Data
public class Notice implements Serializable {
private String noticeTitle;
private String notice;
private String watchNo;
}
@@ -0,0 +1,24 @@
package com.renkang.datacenter.bean;
import lombok.Data;
import java.io.Serializable;
/**
* @author Shunzhi Jiang
* @since 2023/11/5
*/
@Data
public class Period implements Serializable {
private String watchNo;
private String wdType;
private Double warnMin;
private Double warnMax;
private Integer period;
}
@@ -0,0 +1,20 @@
package com.renkang.datacenter.bean;
import lombok.Data;
import java.io.Serializable;
/**
* @author Shunzhi Jiang
* @since 2023/11/5
*/
@Data
public class Rate implements Serializable {
private String watchNo;
private String wdType;
private Integer taskRate;
}
@@ -0,0 +1,20 @@
package com.renkang.datacenter.bean;
import lombok.Data;
import java.io.Serializable;
/**
* @author Shunzhi Jiang
* @since 2023/11/5
*/
@Data
public class WarnSwitch implements Serializable {
private String watchNo;
private String wdType;
private String switchFlag;
}
@@ -0,0 +1,20 @@
package com.renkang.datacenter.bean;
import lombok.Data;
import java.io.Serializable;
/**
* @author Shunzhi Jiang
* @since 2023/11/13
*/
@Data
public class WatchDataBase implements Serializable {
private static final long serialVersionUID = 1L;
private String id;
private String watchNo;
}
@@ -0,0 +1,18 @@
package com.renkang.datacenter.bean;
import lombok.Data;
import java.io.Serializable;
/**
* @author Shunzhi Jiang
* @since 2023/11/5
*/
@Data
public class WatchDataList implements Serializable {
private String watchNo;
private String queryDate;
}
@@ -0,0 +1,57 @@
package com.renkang.datacenter.bean;
import com.alibaba.fastjson.annotation.JSONField;
import lombok.Data;
import java.io.Serializable;
import java.util.List;
/**
* @author Shunzhi Jiang
* @since 2023/11/13
*/
@Data
public class WatchDataResult implements Serializable {
private String maxId;
@JSONField(alternateNames = "commonVoList")
private List<DataCommon> commonList;
@JSONField(alternateNames = "heartRateVoList")
private List<DataHeartRate> heartRateList;
@JSONField(alternateNames = "gpsVoList")
private List<DataGps> gpsList;
@JSONField(alternateNames = "sleepVoList")
private List<DataSleep> sleepList;
@JSONField(alternateNames = "sdcVoList")
private List<DataSdc> sdcList;
@JSONField(alternateNames = "tempVoList")
private List<DataTemperature> tempList;
@JSONField(alternateNames = "workoutVoList")
private List<DataWorkout> workoutList;
@JSONField(alternateNames = "stressVoList")
private List<DataStress> stressList;
@JSONField(alternateNames = "exerciseVoList")
private List<DataExercise> exerciseList;
@JSONField(alternateNames = "traceVoList")
private List<DataTrace> traceList;
@JSONField(alternateNames = "sleepNewVoList")
private List<DataSleepNew> sleepNewList;
@JSONField(alternateNames = "ecgVoList")
private List<DataEcg> ecgList;
@JSONField(alternateNames = "stepVoList")
private List<DataSteps> stepList;
}
@@ -0,0 +1,24 @@
package com.renkang.datacenter.bean;
import lombok.Data;
import java.io.Serializable;
@Data
public class WatchDevice implements Serializable {
/**
* 手表SN编码
*/
private String watchNo;
/**
* IMEI编码
*/
private String imeiNo;
/**
* EID编码
*/
private String eid;
private String watchModel;
}
@@ -0,0 +1,17 @@
package com.renkang.datacenter.bean;
import lombok.Data;
import java.io.Serializable;
import java.util.List;
/**
* @author Shunzhi Jiang
* @since 2023/11/5
*/
@Data
public class WatchDeviceBatch implements Serializable {
private List<WatchDevice> deviceVoList;
}
@@ -0,0 +1,20 @@
package com.renkang.datacenter.bean;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import java.io.Serializable;
/**
* @author Shunzhi Jiang
* @since 2023/11/5
*/
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Data
public class WatchDeviceSwitch extends DeviceSwitch implements Serializable {
private String watchNo;
}
@@ -0,0 +1,22 @@
package com.renkang.datacenter.bean;
import lombok.Data;
import java.io.Serializable;
/**
* @author Shunzhi Jiang
* @since 2023/11/5
*/
@Data
public class WatchNo implements Serializable {
private String watchNo;
public static WatchNo of(String watchNo) {
WatchNo instance = new WatchNo();
instance.setWatchNo(watchNo);
return instance;
}
}
@@ -0,0 +1,20 @@
package com.renkang.datacenter.bean;
import lombok.Data;
import java.io.Serializable;
/**
* @author Shunzhi Jiang
* @since 2023/11/5
*/
@Data
public class WatchSdcItemData implements Serializable {
private String watchNo;
private String wdType;
private String queryDate;
}
@@ -0,0 +1,22 @@
package com.renkang.datacenter.config;
/**
* @author Shunzhi Jiang
* @since 2023/11/3
*/
public interface DataCenterConstants {
//通知更新阈值及频率
String MQTT_CMD_UPDATE_PERIOD = "updatePeriod";
//通知更新定时任务
String MQTT_CMD_UPDATE_TASK = "updateTask";
//通知更新开关
String MQTT_CMD_UPDATE_SWITCH = "updateSwitch";
//通知手表上传数据
String MQTT_CMD_DATA_PUSH = "dataPush";
String REDIS_TOKEN_KEY = "DC_TOKEN";
String HEADER_TOKEN_KEY = "X-Access-Token";
}
@@ -0,0 +1,28 @@
package com.renkang.datacenter.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/**
* @author Shunzhi Jiang
* @since 2023/11/3
*/
@Component
@ConfigurationProperties("datacenter")
@Data
public class DataCenterProperties {
private String url = "";
private String username;
private String password;
private String publicKey;
private Long timeout = 3000L;
private Integer pageSize = 500;
}
@@ -0,0 +1,12 @@
package com.renkang.datacenter.config;
/**
* @author Shunzhi Jiang
* @since 2023/11/3
*/
public interface ImcCenterConstants {
String IMC_LISTENER = "RestRetryImcListener";
String REDIS_TOKEN_KEY = "IMC_TOKEN";
}
@@ -0,0 +1,109 @@
package com.renkang.datacenter.config;
import lombok.Getter;
/**
* @author Shunzhi Jiang
* @since 2023/11/3
*/
@Getter
public enum MethodApi {
/**
* 获取令牌
*/
GET_TOKEN("sys/getToken"),
/**
* 添加设备
*/
ADD_DEVICE("watch/api/addDevice"),
/**
* 批量添加设备
*/
ADD_DEVICE_BATCH("watch/api/batchAddDevice"),
/**
* 删除设备
*/
REMOVE_DEVICE("watch/api/removeDevice?watchNo={watchNo}"),
/**
* 设备开关设置
*/
SET_SWITCH_STATUS("watch/api/setSwitchStatus"),
/**
* 设备开关全部设置
*/
SET_SWITCH_STATUS_ALL("watch/api/setAllSwitchStatus"),
/**
* 通知消息
*/
CMD_NOTICE("watch/mqtt/cmdNotice?watchNo={watchNo}&noticeTitle={noticeTitle}&notice={notice}"),
/**
* 通知消息
*/
CMD_NOTICE_MULTI("watch/mqtt/cmdNoticeMulti?watchNo={watchNo}&noticeTitle={noticeTitle}&notice={notice}"),
/**
* 推送数据通知
*/
CMD_DATA_PUSH("watch/mqtt/cmdDataPush?watchNo={watchNo}&taskName={taskName}"),
/**
* 全量数据获取
*/
WATCH_DATA_LIST("watch/api/getWatchDataList/{queryDate}?watchNo={watchNo}"),
/**
* 增量数据获取(页数)
*/
INCREASE_WATCH_DATA_PAGE_COUNT("watch/api/getIncreWatchDataPageCount/{wdType}/{id}/{pageSize}"),
/**
* 增量数据获取(数据)
*/
INCREASE_WATCH_DATA_PAGE_LIST("watch/api/getIncreWatchDataPageList/{wdType}/{id}/{pageNo}/{pageSize}"),
/**
* SDC详细数据获取
*/
WATCH_SDC_ITEM_DATA("watch/api/getWatchSdcItemData/{watchNo}/{queryDate}/{wdType}"),
/**
* 设备列表查询
*/
DEVICE_INFO_ALL("watch/api/getAllDeviceInfo?watchNo={watchNo}"),
/**
* 查询上传频率设置
*/
GET_RATE("watch/api/getRateByWatchNo?watchNo={watchNo}"),
/**
* 设置上传频率
*/
SAVE_RATE("watch/api/saveRateByNoAndWdType"),
/**
* 查询阈值设置
*/
GET_PERIOD("watch/api/getPeriodByWatchNo?watchNo={watchNo}"),
/**
* 设置阈值
*/
SAVE_PERIOD("watch/api/savePeriodByNoAndWdType"),
/**
* 查询报警开关设置
*/
GET_WARN_SWITCH("watch/api/getWarnListByWatchNo?watchNo={watchNo}"),
/**
* 设置报警开关
*/
SAVE_WARN_SWITCH("watch/api/saveWarnByNoAndWdType"),
// =======================影像中心============================
/**
* 查询影像人员信息
*/
GET_IMAGE_MESSAGE("imc/file/getImageMsg"),
/**
* 通过peId获取影像基本信息
*/
GET_IMAGE_ITEMS("imc/file/getImageItems"),
;
private final String url;
MethodApi(String url) {
this.url = url;
}
}
@@ -0,0 +1,40 @@
package com.renkang.datacenter.config;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.retry.annotation.EnableRetry;
import org.springframework.web.client.RestTemplate;
import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.time.temporal.ChronoUnit;
/**
* @author Shunzhi Jiang
* @since 2023/11/3
*/
@Configuration
@EnableRetry
public class RestConfig {
private final DataCenterProperties properties;
public RestConfig(DataCenterProperties properties) {
this.properties = properties;
}
@Bean
public RestTemplate dataCenterTemplate() {
return new RestTemplateBuilder()
.setConnectTimeout(Duration.of(properties.getTimeout(), ChronoUnit.MILLIS))
.additionalMessageConverters(
new StringHttpMessageConverter(StandardCharsets.UTF_8),
new MappingJackson2HttpMessageConverter()
)
.build();
}
}
@@ -0,0 +1,88 @@
package com.renkang.datacenter.config;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.renkang.datacenter.bean.Login;
import com.renkang.datacenter.util.RemoteUtils;
import lombok.extern.slf4j.Slf4j;
import org.jeecg.common.api.vo.Result;
import org.jeecg.util.RSAEncryptUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.retry.RetryContext;
import org.springframework.retry.RetryListener;
import org.springframework.retry.interceptor.MethodInvocationRetryCallback;
import org.springframework.retry.listener.MethodInvocationRetryListenerSupport;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import java.time.Duration;
/**
* @author Shunzhi Jiang
* @since 2023/11/3
*/
@Component(ImcCenterConstants.IMC_LISTENER)
@Slf4j
public class RestRetryImcListener extends MethodInvocationRetryListenerSupport implements RetryListener {
private RedisTemplate<String, Object> redisTemplate;
private DataCenterProperties properties;
@Autowired
public void setRedisTemplate(RedisTemplate<String, Object> redisTemplate) {
this.redisTemplate = redisTemplate;
}
@Autowired
public void setProperties(DataCenterProperties properties) {
this.properties = properties;
}
@Override
protected <T, E extends Throwable> boolean doOpen(RetryContext context, MethodInvocationRetryCallback<T, E> callback) {
String token = "";
try{
token = (String) redisTemplate.opsForValue().get(ImcCenterConstants.REDIS_TOKEN_KEY);
if (!StringUtils.hasLength(token)) {
token = refreshToken();
}
}catch (Exception e){
token = refreshToken();
}
RemoteUtils.TOKEN_CACHE.set(token);
return true;
}
@Override
protected <T, E extends Throwable> void doClose(RetryContext context, MethodInvocationRetryCallback<T, E> callback, Throwable throwable) {
RemoteUtils.TOKEN_CACHE.remove();
}
@Override
protected <T, E extends Throwable> void doOnError(RetryContext context, MethodInvocationRetryCallback<T, E> callback, Throwable throwable) {
String methodName = callback.getInvocation().getMethod().getName();
log.error("执行方法[{}]出错,刷新令牌后重试,当前重试次数:{}",methodName, context.getRetryCount(),throwable);
String token = refreshToken();
RemoteUtils.TOKEN_CACHE.set(token);
}
private String refreshToken() {
Login body = new Login();
body.setUsername(properties.getUsername());
body.setPassword(RSAEncryptUtils.encrypt1(properties.getPassword(),properties.getPublicKey()));
Result<Object> result = RemoteUtils.post(MethodApi.GET_TOKEN, body);
if (!result.isSuccess()) {
log.info("刷新数据中心令牌失败:{}",result.getMessage());
return null;
}
JSONObject data = (JSONObject) JSON.toJSON(result.getResult());
String token = data.getString("token");
redisTemplate.opsForValue().set(ImcCenterConstants.REDIS_TOKEN_KEY,token, Duration.ofDays(1));
log.info("刷新影像中心令牌成功:{}", token);
return token;
}
}
@@ -0,0 +1,88 @@
package com.renkang.datacenter.config;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.renkang.datacenter.bean.Login;
import com.renkang.datacenter.util.RemoteUtils;
import lombok.extern.slf4j.Slf4j;
import org.jeecg.common.api.vo.Result;
import org.jeecg.util.RSAEncryptUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.retry.RetryContext;
import org.springframework.retry.RetryListener;
import org.springframework.retry.interceptor.MethodInvocationRetryCallback;
import org.springframework.retry.listener.MethodInvocationRetryListenerSupport;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import java.time.Duration;
/**
* @author Shunzhi Jiang
* @since 2023/11/3
*/
@Component
@Slf4j
public class RestRetryListener extends MethodInvocationRetryListenerSupport implements RetryListener {
private RedisTemplate<String, Object> redisTemplate;
private DataCenterProperties properties;
@Autowired
public void setRedisTemplate(RedisTemplate<String, Object> redisTemplate) {
this.redisTemplate = redisTemplate;
}
@Autowired
public void setProperties(DataCenterProperties properties) {
this.properties = properties;
}
@Override
protected <T, E extends Throwable> boolean doOpen(RetryContext context, MethodInvocationRetryCallback<T, E> callback) {
String token = "";
try{
token = (String) redisTemplate.opsForValue().get(DataCenterConstants.REDIS_TOKEN_KEY);
if (!StringUtils.hasLength(token)) {
token = refreshToken();
}
}catch (Exception e){
token = refreshToken();
}
RemoteUtils.TOKEN_CACHE.set(token);
return true;
}
@Override
protected <T, E extends Throwable> void doClose(RetryContext context, MethodInvocationRetryCallback<T, E> callback, Throwable throwable) {
RemoteUtils.TOKEN_CACHE.remove();
}
@Override
protected <T, E extends Throwable> void doOnError(RetryContext context, MethodInvocationRetryCallback<T, E> callback, Throwable throwable) {
String methodName = callback.getInvocation().getMethod().getName();
log.error("执行方法[{}]出错,刷新令牌后重试,当前重试次数:{}",methodName, context.getRetryCount(),throwable);
String token = refreshToken();
RemoteUtils.TOKEN_CACHE.set(token);
}
private String refreshToken() {
Login body = new Login();
body.setUsername(properties.getUsername());
body.setPassword(RSAEncryptUtils.encrypt1(properties.getPassword(),properties.getPublicKey()));
Result<Object> result = RemoteUtils.post(MethodApi.GET_TOKEN, body);
if (!result.isSuccess()) {
log.info("刷新数据中心令牌失败:{}",result.getMessage());
return null;
}
JSONObject data = (JSONObject) JSON.toJSON(result.getResult());
String token = data.getString("token");
redisTemplate.opsForValue().set(DataCenterConstants.REDIS_TOKEN_KEY,token, Duration.ofDays(1));
log.info("刷新数据中心令牌成功:{}", token);
return token;
}
}
@@ -0,0 +1,116 @@
package com.renkang.datacenter.util;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.renkang.datacenter.config.DataCenterConstants;
import com.renkang.datacenter.config.DataCenterProperties;
import com.renkang.datacenter.config.MethodApi;
import org.jeecg.common.api.vo.Result;
import org.jeecg.common.util.SpringContextUtils;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.client.RestTemplate;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
/**
* @author Shunzhi Jiang
* @since 2023/11/3
*/
public class RemoteUtils {
public static final ThreadLocal<String> TOKEN_CACHE = new ThreadLocal<>();
private static RestTemplate restTemplate;
private static DataCenterProperties properties;
public static RestTemplate getRestTemplate() {
if (Objects.isNull(restTemplate)) {
restTemplate = SpringContextUtils.getBean("dataCenterTemplate", RestTemplate.class);
}
return restTemplate;
}
public static DataCenterProperties getProperties() {
if (Objects.isNull(properties)) {
properties = SpringContextUtils.getBean(DataCenterProperties.class);
}
return properties;
}
public static Result<Object> get(MethodApi api, Map<String, Object> params) {
return exchange(api, HttpMethod.GET, null, params);
}
public static String downloadAsString(String filepath) {
return getRestTemplate().getForObject(urlHandlerForFileDown(filepath), String.class);
}
public static Result<Object> get(MethodApi api) {
return exchange(api, HttpMethod.GET, null, null);
}
public static Result<Object> post(MethodApi api, Object body) {
return exchange(api, HttpMethod.POST, body, null);
}
public static Result<Object> post(MethodApi api, Object body, Map<String, Object> params) {
return exchange(api, HttpMethod.POST, body, params);
}
public static Result<Object> post(MethodApi api, Map<String, Object> params) {
return exchange(api, HttpMethod.POST, null, params);
}
private static Result<Object> exchange(MethodApi api, HttpMethod method, Object body, Map<String, Object> params) {
HttpHeaders headers = new HttpHeaders();
String token = TOKEN_CACHE.get();
if (StringUtils.hasLength(token)) {
headers.add(DataCenterConstants.HEADER_TOKEN_KEY, TOKEN_CACHE.get());
}
HttpEntity<?> entity = new HttpEntity<>(body, headers);
if (CollectionUtils.isEmpty(params)) {
return responseHandler(getRestTemplate().exchange(urlHandler(api), method, entity, String.class));
} else {
return responseHandler(getRestTemplate().exchange(urlHandler(api), method, entity, String.class, params));
}
}
private static String urlHandler(MethodApi api) {
return urlHandler(api.getUrl());
}
private static String urlHandler(String url) {
String baseUrl = getProperties().getUrl();
String path = url;
if (!baseUrl.endsWith("/") && !path.startsWith("/")) {
path = "/".concat(path);
}
return baseUrl + path;
}
private static String urlHandlerForFileDown(String url) {
String fileUrl = "file/down/".concat(url).replace("//", "/");
return urlHandler(fileUrl);
}
private static Result<Object> responseHandler(ResponseEntity<String> entity) {
String body = entity.getBody();
JSONObject responseJson = JSON.parseObject(body);
String message = Optional.ofNullable(responseJson.getString("message")).orElse("");
if (responseJson.getBoolean("success")) {
return Result.OK(message, responseJson.get("result"));
} else {
return Result.error(message);
}
}
}
+20
View File
@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.renkang</groupId>
<artifactId>functional-modules</artifactId>
<version>2.0.0</version>
</parent>
<artifactId>font-resources</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
</project>
+118
View File
@@ -0,0 +1,118 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.renkang</groupId>
<artifactId>functional-modules</artifactId>
<version>2.0.0</version>
</parent>
<artifactId>monitoring-sync</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
</dependency>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
</dependencies>
<build>
<!-- 打包名称 -->
<finalName>${project.artifactId}</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-deploy-plugin</artifactId>
<configuration>
<skip>true</skip>
</configuration>
</plugin>
</plugins>
</build>
<profiles>
<profile>
<id>default</id>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</profile>
<profile>
<id>divide-package</id>
<activation>
<activeByDefault>false</activeByDefault>
</activation>
<properties>
<start-class>com.renkang.sync.ApplicationMain</start-class>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
</plugin>
</plugins>
</build>
</profile>
</profiles>
</project>
@@ -0,0 +1,20 @@
package com.renkang.sync;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.stereotype.Repository;
/**
* @author Jiang Shunzhi
*/
@SpringBootApplication
@EnableJpaRepositories(includeFilters = @ComponentScan.Filter(classes = Repository.class))
public class ApplicationMain {
public static void main(String[] args) {
SpringApplication.run(ApplicationMain.class, args);
}
}
@@ -0,0 +1,135 @@
package com.renkang.sync.bean;
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.Data;
import java.io.Serializable;
/**
* 接口返回数据格式
*
* @author scott
* @email jeecgos@163.com
* @date 2019年1月19日
*/
@Data
public class Result<T> implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 成功标志
*/
private boolean success = true;
/**
* 返回处理消息
*/
private String message = "";
/**
* 返回代码
*/
private Integer code = 0;
/**
* 返回数据对象 data
*/
private T result;
/**
* 时间戳
*/
private long timestamp = System.currentTimeMillis();
@JsonIgnore
private String onlTable;
public Result() {
}
/**
* 兼容VUE3版token失效不跳转登录页面
*/
public Result(Integer code, String message) {
this.code = code;
this.message = message;
}
public static <T> Result<T> ok() {
Result<T> r = new Result<>();
r.setSuccess(true);
r.setCode(200);
return r;
}
/**
* 此方法是为了兼容升级所创建
*/
public static <T> Result<T> ok(String msg) {
Result<T> r = new Result<>();
r.setSuccess(true);
r.setCode(200);
r.setMessage(msg);
return r;
}
public static <T> Result<T> ok(T data) {
Result<T> r = new Result<>();
r.setSuccess(true);
r.setCode(200);
r.setResult(data);
return r;
}
public static <T> Result<T> ok(String msg, T data) {
Result<T> r = new Result<>();
r.setSuccess(true);
r.setCode(200);
r.setMessage(msg);
r.setResult(data);
return r;
}
public static <T> Result<T> error(String msg, T data) {
Result<T> r = new Result<>();
r.setSuccess(false);
r.setCode(500);
r.setMessage(msg);
r.setResult(data);
return r;
}
public static <T> Result<T> error(String msg) {
return error(500, msg);
}
public static <T> Result<T> error(int code, String msg) {
Result<T> r = new Result<>();
r.setCode(code);
r.setMessage(msg);
r.setSuccess(false);
return r;
}
/**
* 无权限访问返回结果
*/
public static <T> Result<T> noAuth(String msg) {
return error(401, msg);
}
public Result<T> success(String message) {
this.message = message;
this.code = 200;
this.success = true;
return this;
}
public Result<T> error500(String message) {
this.message = message;
this.code = 500;
this.success = false;
return this;
}
}
@@ -0,0 +1,63 @@
package com.renkang.sync.bean.lefu;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateUtil;
import com.renkang.sync.entity.RemoteWeightManufacturerLefu;
import lombok.Data;
import javax.validation.Valid;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Pattern;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
/**
* @author Jiang Shunzhi
*/
@Data
public class BatchRecord {
@NotBlank(message = "Missing parameter: sn")
private String sn;
@NotBlank(message = "Missing parameter: type")
private String type;
@NotBlank(message = "Missing parameter: mac")
@Pattern(regexp = "([A-Fa-f0-9]{2}[:]){5}[A-Fa-f0-9]{2}", message = "MAC address does not meet the rules")
private String mac;
@NotBlank(message = "Missing parameter: charge")
private String charge;
private String firmwareVersion;
private String wifiVersion;
@Valid
private List<RecordData> data;
private RemoteWeightManufacturerLefu toRemoteEntity(RecordData data) {
RemoteWeightManufacturerLefu remoteWeightManufacturerLefu = new RemoteWeightManufacturerLefu();
remoteWeightManufacturerLefu.setDeviceSn(sn);
remoteWeightManufacturerLefu.setDeviceType(type);
remoteWeightManufacturerLefu.setDeviceMac(mac);
remoteWeightManufacturerLefu.setDeviceCharge(charge);
remoteWeightManufacturerLefu.setFirmwareVersion(firmwareVersion);
remoteWeightManufacturerLefu.setWifiVersion(wifiVersion);
remoteWeightManufacturerLefu.setDeviceImpedance(data.getImpedance());
remoteWeightManufacturerLefu.setDataTime(data.getDataTime());
remoteWeightManufacturerLefu.setWeight(data.getWeightNum());
remoteWeightManufacturerLefu.setHeartRate(data.getHeartRate());
remoteWeightManufacturerLefu.setCreateTime(DateUtil.date());
return remoteWeightManufacturerLefu;
}
public List<RemoteWeightManufacturerLefu> toRemoteEntities() {
return CollectionUtil.isEmpty(data) ? Collections.emptyList() : data.stream()
.map(this::toRemoteEntity)
.collect(Collectors.toList());
}
}
@@ -0,0 +1,28 @@
package com.renkang.sync.bean.lefu;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
/**
* @author Jiang Shunzhi
*/
@Data
public class DeviceInfo {
private String sn;
private String mac;
private String firmwareVersion;
@JsonProperty("WifiVersion")
private String wifiVersion;
private String hardwareVersion;
private String charge;
private String type;
private Integer timezone;
}
@@ -0,0 +1,91 @@
package com.renkang.sync.bean.lefu;
import java.io.Serializable;
/**
* @author Jiang Shunzhi
*/
public class LefuResponse<T> implements Serializable {
private static final Integer CODE_OK = 0;
private static final Integer CODE_FAIL = 1;
private static final Integer CODE_SUCCESS = 200;
private Integer code;
private T data;
/**
* The firmware on the device has been successfully written; this parameter is for compatibility with existing devices.
*/
private Integer errorCode; // Registration success status, 0 for success, non-zero for failure
/**
* The firmware on the device has been successfully written; this parameter is for compatibility with existing devices.
*/
private String text; // Return message, which can explain the reason for success or the reason for failure
public LefuResponse() {
setCode(CODE_SUCCESS);
}
public static <T> LefuResponse<T> ok(String text) {
LefuResponse<T> scaleR = new LefuResponse<>();
scaleR.setErrorCode(CODE_OK);
scaleR.setText(text);
return scaleR;
}
public static <T> LefuResponse<T> ok(String text, T data) {
LefuResponse<T> scaleR = new LefuResponse<>();
scaleR.setErrorCode(CODE_OK);
scaleR.setText(text);
scaleR.setData(data);
return scaleR;
}
public static <T> LefuResponse<T> fail(String text, T data) {
LefuResponse<T> scaleR = new LefuResponse<>();
scaleR.setErrorCode(CODE_FAIL);
scaleR.setText(text);
scaleR.setData(data);
return scaleR;
}
public static <T> LefuResponse<T> fail(String text) {
LefuResponse<T> scaleR = new LefuResponse<>();
scaleR.setErrorCode(CODE_FAIL);
scaleR.setText(text);
return scaleR;
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
public Integer getErrorCode() {
return errorCode;
}
public void setErrorCode(Integer errorCode) {
this.errorCode = errorCode;
}
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
public Integer getCode() {
return code;
}
public void setCode(Integer code) {
this.code = code;
}
}
@@ -0,0 +1,54 @@
package com.renkang.sync.bean.lefu;
/**
* @author Jiang Shunzhi
*/
import cn.hutool.core.date.DateUtil;
import com.renkang.sync.entity.RemoteWeightManufacturerLefu;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Pattern;
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Data
public class Record extends RecordData {
@NotBlank(message = "Missing parameter: sn")
private String sn;
@NotBlank(message = "Missing parameter: type")
private String type;
@NotBlank(message = "Missing parameter: mac")
@Pattern(regexp = "([A-Fa-f0-9]{2}[:]){5}[A-Fa-f0-9]{2}", message = "MAC address does not meet the rules")
private String mac;
@NotBlank(message = "Missing parameter: charge")
private String charge;
private String firmwareVersion;
private String wifiVersion;
public RemoteWeightManufacturerLefu toRemoteEntity() {
RemoteWeightManufacturerLefu remoteWeightManufacturerLefu = new RemoteWeightManufacturerLefu();
remoteWeightManufacturerLefu.setDeviceSn(sn);
remoteWeightManufacturerLefu.setDeviceType(type);
remoteWeightManufacturerLefu.setDeviceMac(mac);
remoteWeightManufacturerLefu.setDeviceCharge(charge);
remoteWeightManufacturerLefu.setFirmwareVersion(firmwareVersion);
remoteWeightManufacturerLefu.setWifiVersion(wifiVersion);
remoteWeightManufacturerLefu.setDeviceImpedance(getImpedance());
remoteWeightManufacturerLefu.setDataTime(getDataTime());
remoteWeightManufacturerLefu.setWeight(getWeightNum());
remoteWeightManufacturerLefu.setHeartRate(getHeartRate());
remoteWeightManufacturerLefu.setCreateTime(DateUtil.date());
return remoteWeightManufacturerLefu;
}
}
@@ -0,0 +1,35 @@
package com.renkang.sync.bean.lefu;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.StrUtil;
import lombok.Data;
import javax.validation.constraints.NotBlank;
import java.math.BigDecimal;
import java.util.Date;
/**
* @author Jiang Shunzhi
*/
@Data
public class RecordData {
private String weight;
private String impedance;
@NotBlank(message = "Missing parameter: timestamp")
private String timestamp;
private Integer heartRate;
public Date getDataTime() {
return StrUtil.isNumeric(getTimestamp()) ? DateUtil.date(Long.parseLong(getTimestamp())) : null;
}
public BigDecimal getWeightNum() {
return NumberUtil.isNumber(getWeight()) ? new BigDecimal(getWeight()) : null;
}
}
@@ -0,0 +1,15 @@
package com.renkang.sync.bean.lefu;
import lombok.Data;
/**
* @author Jiang Shunzhi
*/
@Data
public class TimeInfo {
private Long now;
private Integer unit;
}
@@ -0,0 +1,29 @@
package com.renkang.sync.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import java.util.HashMap;
import java.util.Map;
/**
* @author Jiang Shunzhi
*/
@ConfigurationProperties("lefu")
@Data
public class LefuProperties {
private Map<String, Service> sync = new HashMap<>();
@Data
public static class Service {
private String baseUrl = "";
private String username = "";
private String password = "";
}
}
@@ -0,0 +1,48 @@
package com.renkang.sync.config;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.task.TaskExecutor;
import org.springframework.http.client.OkHttp3ClientHttpRequestFactory;
import org.springframework.scheduling.TaskScheduler;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.web.client.RestTemplate;
/**
* @author Jiang Shunzhi
*/
@Configuration
@EnableConfigurationProperties({SyncProperties.class, LefuProperties.class})
@ConditionalOnProperty(prefix = "spring.cloud.nacos.discovery", name = "enabled", havingValue = "false", matchIfMissing = true)
@EnableScheduling
@EnableAsync
public class SyncConfiguration {
@Bean
public RestTemplate restTemplate() {
return new RestTemplate(new OkHttp3ClientHttpRequestFactory());
}
@Bean
public TaskScheduler schedulingTaskExecutor() {
ThreadPoolTaskScheduler threadPoolTaskScheduler = new ThreadPoolTaskScheduler();
threadPoolTaskScheduler.setPoolSize(10);
threadPoolTaskScheduler.setThreadNamePrefix("task-pool-");
threadPoolTaskScheduler.setWaitForTasksToCompleteOnShutdown(true);
return threadPoolTaskScheduler;
}
@Bean
public TaskExecutor taskExecutor() {
ThreadPoolTaskScheduler threadPoolTaskScheduler = new ThreadPoolTaskScheduler();
threadPoolTaskScheduler.setPoolSize(10);
threadPoolTaskScheduler.setThreadNamePrefix("async-pool-");
threadPoolTaskScheduler.setWaitForTasksToCompleteOnShutdown(true);
return threadPoolTaskScheduler;
}
}
@@ -0,0 +1,22 @@
package com.renkang.sync.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import java.util.ArrayList;
import java.util.List;
/**
* @author Jiang Shunzhi
*/
@ConfigurationProperties("sync")
@Data
public class SyncProperties {
private Integer size = 100;
private String cron = "0 */30 * * * *";
private List<String> baseUrls = new ArrayList<>();
}
@@ -0,0 +1,61 @@
package com.renkang.sync.controller;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.renkang.sync.bean.lefu.*;
import com.renkang.sync.entity.RemoteWeightManufacturerLefu;
import com.renkang.sync.service.LefuService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.Collections;
import java.util.List;
/**
* @author Jiang Shunzhi
*/
@RestController
@RequestMapping("/lefu/wifi")
public class LefuController {
@Autowired
private LefuService lefuService;
@PostMapping("/register")
public LefuResponse<TimeInfo> register(@Validated @RequestBody DeviceInfo deviceInfo) {
return LefuResponse.ok("DeviceInfo success", getTimeInfo());
}
private TimeInfo getTimeInfo() {
TimeInfo timeInfo = new TimeInfo();
timeInfo.setUnit(0);
timeInfo.setNow(System.currentTimeMillis());
return timeInfo;
}
@GetMapping("/config")
public LefuResponse<TimeInfo> config(@Validated @RequestBody DeviceInfo deviceInfo) {
return LefuResponse.ok("Get config info success", getTimeInfo());
}
@PostMapping("/record")
public LefuResponse<Boolean> record(@Validated @RequestBody Record record) throws JsonProcessingException {
RemoteWeightManufacturerLefu data = lefuService.record(record);
lefuService.sync(Collections.singletonList(data));
return LefuResponse.ok("success");
}
@PostMapping("/batchRecord")
public LefuResponse<Boolean> batchRecord(@Validated @RequestBody BatchRecord batchRecord) throws JsonProcessingException {
List<RemoteWeightManufacturerLefu> data = lefuService.batchRecord(batchRecord);
lefuService.sync(data);
return LefuResponse.ok("success");
}
@GetMapping("/list")
public LefuResponse<Page<RemoteWeightManufacturerLefu>> list(@RequestParam Integer pageNo, @RequestParam Integer pageSize) {
return LefuResponse.ok("", lefuService.list(pageNo, pageSize));
}
}
@@ -0,0 +1,48 @@
package com.renkang.sync.controller;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.renkang.sync.bean.Result;
import com.renkang.sync.entity.IdAware;
import com.renkang.sync.service.DataService;
import com.renkang.sync.util.Constants;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.web.bind.annotation.*;
import java.math.BigInteger;
import java.util.Objects;
import static com.renkang.sync.util.Constants.GET_MAX_ID;
import static com.renkang.sync.util.Constants.SAVE_DATA;
/**
* @Name SyncController
* @Author YangYuanChen
* @Data 2024/7/26下午4:22
*/
@RestController
@ConditionalOnProperty(prefix = "spring.cloud.nacos.discovery", name = "enabled", havingValue = "true")
public class SyncController {
private DataService dataService;
@Autowired
public void setDataService(DataService dataService) {
this.dataService = dataService;
}
@GetMapping(GET_MAX_ID)
public Result<String> getAllDataId(@RequestParam int type) {
BigInteger maxId = dataService.getMaxId(Constants.getClass(type));
return Result.ok("", Objects.nonNull(maxId) ? maxId.toString() : "0");
}
@PostMapping(SAVE_DATA)
public Result<Void> saveData(@RequestParam int type, @RequestBody String json) throws JsonProcessingException {
Class<? extends IdAware> clz = Constants.getClass(type);
Objects.requireNonNull(clz, "No type match.");
dataService.saveData(clz, json);
return Result.ok();
}
}
@@ -0,0 +1,12 @@
package com.renkang.sync.entity;
import java.math.BigInteger;
/**
* @author Jiang Shunzhi
*/
public interface IdAware {
BigInteger getId();
}
@@ -0,0 +1,73 @@
package com.renkang.sync.entity;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.Setter;
import lombok.ToString;
import org.hibernate.proxy.HibernateProxy;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
import java.io.Serializable;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Objects;
/**
* @Name IndoorEnvironmentData
* @Author YangYuanChen
* @Data 20242024/7/25下午2:02
*/
@Entity
@Table(name = "indoor_environment_data")
@Getter
@Setter
@ToString
@RequiredArgsConstructor
public class IndoorEnvironmentData implements IdAware, Serializable {
@Id
@Column(name = "id")
private BigInteger id;
@Column(name = "meter_code")
private String meterCode;
@Column(name = "read_time")
private Integer readTime;
@Column(name = "insert_time")
private Integer insertTime;
@Column(name = "temperature")
private BigDecimal temperature;
@Column(name = "humidity")
private BigDecimal humidity;
@Column(name = "carbon_dioxide")
private BigDecimal carbonDioxide;
@Column(name = "pm25")
private BigDecimal pm25;
@Column(name = "pm10")
private BigDecimal pm10;
@Column(name = "hcho")
private BigDecimal hcho;
@Override
public final boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null) {
return false;
}
Class<?> oEffectiveClass = o instanceof HibernateProxy ? ((HibernateProxy) o).getHibernateLazyInitializer().getPersistentClass() : o.getClass();
Class<?> thisEffectiveClass = this instanceof HibernateProxy ? ((HibernateProxy) this).getHibernateLazyInitializer().getPersistentClass() : this.getClass();
if (thisEffectiveClass != oEffectiveClass) {
return false;
}
IndoorEnvironmentData that = (IndoorEnvironmentData) o;
return getId() != null && Objects.equals(getId(), that.getId());
}
@Override
public final int hashCode() {
return this instanceof HibernateProxy ? ((HibernateProxy) this).getHibernateLazyInitializer().getPersistentClass().hashCode() : getClass().hashCode();
}
}
@@ -0,0 +1,77 @@
package com.renkang.sync.entity;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.Setter;
import lombok.ToString;
import org.hibernate.proxy.HibernateProxy;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
import java.io.Serializable;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Objects;
/**
* @Name OutdoorEnvironmentData
* @Author YangYuanChen
* @Data 20242024/7/25下午2:05
*/
@Entity
@Table(name = "outdoor_environment_data")
@Getter
@Setter
@ToString
@RequiredArgsConstructor
public class OutdoorEnvironmentData implements IdAware, Serializable {
@Id
@Column(name = "id")
private BigInteger id;
@Column(name = "meter_code")
private String meterCode;
@Column(name = "read_time")
private Integer readTime;
@Column(name = "insert_time")
private Integer insertTime;
@Column(name = "temperature")
private BigDecimal temperature;
@Column(name = "humidity")
private BigDecimal humidity;
@Column(name = "pm25")
private BigDecimal pm25;
@Column(name = "pm10")
private BigDecimal pm10;
@Column(name = "illuminance")
private BigDecimal illuminance;
@Column(name = "wind_speed")
private BigDecimal windSpeed;
@Column(name = "wind_direction")
private Integer windDirection;
@Column(name = "rs_ra")
private BigDecimal rsRa;
@Override
public final boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null) {
return false;
}
Class<?> oEffectiveClass = o instanceof HibernateProxy ? ((HibernateProxy) o).getHibernateLazyInitializer().getPersistentClass() : o.getClass();
Class<?> thisEffectiveClass = this instanceof HibernateProxy ? ((HibernateProxy) this).getHibernateLazyInitializer().getPersistentClass() : this.getClass();
if (thisEffectiveClass != oEffectiveClass) {
return false;
}
OutdoorEnvironmentData that = (OutdoorEnvironmentData) o;
return getId() != null && Objects.equals(getId(), that.getId());
}
@Override
public final int hashCode() {
return this instanceof HibernateProxy ? ((HibernateProxy) this).getHibernateLazyInitializer().getPersistentClass().hashCode() : getClass().hashCode();
}
}
@@ -0,0 +1,77 @@
package com.renkang.sync.entity;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.Setter;
import lombok.ToString;
import org.hibernate.proxy.HibernateProxy;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
import java.io.Serializable;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Objects;
/**
* @Name PollenData
* @Author YangYuanChen
* @Data 20242024/7/25下午1:46
*/
@Entity
@Table(name = "pollen_data")
@Getter
@Setter
@ToString
@RequiredArgsConstructor
public class PollenData implements IdAware, Serializable {
@Id
@Column(name = "id")
private BigInteger id;
@Column(name = "meter_code")
private String meterCode;
@Column(name = "read_time")
private Integer readTime;
@Column(name = "insert_time")
private Integer insertTime;
@Column(name = "one")
private Integer one;
@Column(name = "two")
private Integer two;
@Column(name = "three")
private Integer three;
@Column(name = "four")
private Integer four;
@Column(name = "five")
private Integer five;
@Column(name = "six")
private Integer six;
@Column(name = "temperature")
private BigDecimal temperature;
@Column(name = "humidity")
private BigDecimal humidity;
@Override
public final boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null) {
return false;
}
Class<?> oEffectiveClass = o instanceof HibernateProxy ? ((HibernateProxy) o).getHibernateLazyInitializer().getPersistentClass() : o.getClass();
Class<?> thisEffectiveClass = this instanceof HibernateProxy ? ((HibernateProxy) this).getHibernateLazyInitializer().getPersistentClass() : this.getClass();
if (thisEffectiveClass != oEffectiveClass) {
return false;
}
PollenData that = (PollenData) o;
return getId() != null && Objects.equals(getId(), that.getId());
}
@Override
public final int hashCode() {
return this instanceof HibernateProxy ? ((HibernateProxy) this).getHibernateLazyInitializer().getPersistentClass().hashCode() : getClass().hashCode();
}
}
@@ -0,0 +1,65 @@
package com.renkang.sync.entity;
import lombok.Data;
import org.hibernate.annotations.GenericGenerator;
import javax.persistence.*;
import javax.validation.constraints.Size;
import java.math.BigDecimal;
import java.util.Date;
/**
* @author admin
*/
@Data
@Entity
@Table(name = "remote_weight_manufacturer_lefu")
public class RemoteWeightManufacturerLefu {
@Id
@Size(max = 32)
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "snow_flake_generator")
@GenericGenerator(name = "snow_flake_generator", strategy = "com.renkang.sync.util.SnowFlakeIdGenerator")
@Column(name = "id", nullable = false, length = 32)
private String id;
@Size(max = 32)
@Column(name = "device_sn", length = 32)
private String deviceSn;
@Size(max = 32)
@Column(name = "device_type", length = 32)
private String deviceType;
@Size(max = 32)
@Column(name = "device_mac", length = 32)
private String deviceMac;
@Size(max = 32)
@Column(name = "device_charge", length = 32)
private String deviceCharge;
@Size(max = 32)
@Column(name = "device_impedance", length = 32)
private String deviceImpedance;
@Size(max = 32)
@Column(name = "wifi_version", length = 32)
private String wifiVersion;
@Size(max = 32)
@Column(name = "firmware_version", length = 32)
private String firmwareVersion;
@Column(name = "data_time")
private Date dataTime;
@Column(name = "weight", precision = 6, scale = 2)
private BigDecimal weight;
@Column(name = "heart_rate")
private Integer heartRate;
@Column(name = "create_time")
private Date createTime;
}
@@ -0,0 +1,77 @@
package com.renkang.sync.entity;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.Setter;
import lombok.ToString;
import org.hibernate.proxy.HibernateProxy;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
import java.io.Serializable;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Objects;
/**
* @Name WaterQualityData
* @Author YangYuanChen
* @Data 20242024/7/25下午2:41
*/
@Entity
@Table(name = "water_quality_data")
@Getter
@Setter
@ToString
@RequiredArgsConstructor
public class WaterQualityData implements IdAware, Serializable {
@Id
@Column(name = "id")
private BigInteger id;
@Column(name = "meter_code")
private String meterCode;
@Column(name = "insert_time")
private Integer insertTime;
@Column(name = "read_time")
private Integer readTime;
@Column(name = "temperature")
private BigDecimal temperature;
@Column(name = "conductivity")
private BigDecimal conductivity;
@Column(name = "turbidity")
private BigDecimal turbidity;
@Column(name = "residual_chlorine")
private BigDecimal residualChlorine;
@Column(name = "ph")
private BigDecimal ph;
@Column(name = "salinity")
private BigDecimal salinity;
@Column(name = "resistivity")
private BigDecimal resistivity;
@Column(name = "tds")
private BigDecimal tds;
@Override
public final boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null) {
return false;
}
Class<?> oEffectiveClass = o instanceof HibernateProxy ? ((HibernateProxy) o).getHibernateLazyInitializer().getPersistentClass() : o.getClass();
Class<?> thisEffectiveClass = this instanceof HibernateProxy ? ((HibernateProxy) this).getHibernateLazyInitializer().getPersistentClass() : this.getClass();
if (thisEffectiveClass != oEffectiveClass) {
return false;
}
WaterQualityData that = (WaterQualityData) o;
return getId() != null && Objects.equals(getId(), that.getId());
}
@Override
public final int hashCode() {
return this instanceof HibernateProxy ? ((HibernateProxy) this).getHibernateLazyInitializer().getPersistentClass().hashCode() : getClass().hashCode();
}
}
@@ -0,0 +1,31 @@
package com.renkang.sync.repository;
import com.renkang.sync.entity.IdAware;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import java.math.BigInteger;
/**
* @author Jiang Shunzhi
*/
public interface BaseRepository<T extends IdAware> extends JpaRepository<T, BigInteger> {
/**
* 根据起始ID查询分页数据
*
* @param id 起始ID
* @param pageable 分页参数
* @return 分页数据
*/
Page<T> findByIdGreaterThan(BigInteger id, Pageable pageable);
/**
* 查询所有数据并返回 ID 最大的 BigInteger
*
* @return 最大ID值
*/
BigInteger findMaxId();
}
@@ -0,0 +1,26 @@
package com.renkang.sync.repository;
import com.renkang.sync.entity.IndoorEnvironmentData;
import org.springframework.data.jpa.repository.Query;
import org.springframework.stereotype.Repository;
import java.math.BigInteger;
/**
* @Name IndoorRepository
* @Author YangYuanChen
* @Data 20242024/7/25下午4:52
*/
@Repository
public interface IndoorRepository extends BaseRepository<IndoorEnvironmentData> {
/**
* 查询所有数据并返回 ID 最大的 BigInteger
*
* @return 最大ID值
*/
@Override
@Query("SELECT MAX(id) FROM IndoorEnvironmentData")
BigInteger findMaxId();
}
@@ -0,0 +1,12 @@
package com.renkang.sync.repository;
import com.renkang.sync.entity.RemoteWeightManufacturerLefu;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
/**
* @author Jiang Shunzhi
*/
@Repository
public interface LefuRepository extends JpaRepository<RemoteWeightManufacturerLefu, String> {
}
@@ -0,0 +1,26 @@
package com.renkang.sync.repository;
import com.renkang.sync.entity.OutdoorEnvironmentData;
import org.springframework.data.jpa.repository.Query;
import org.springframework.stereotype.Repository;
import java.math.BigInteger;
/**
* @Name OutdoorRepository
* @Author YangYuanChen
* @Data 20242024/7/25下午4:57
*/
@Repository
public interface OutdoorRepository extends BaseRepository<OutdoorEnvironmentData> {
/**
* 查询所有数据并返回 ID 最大的 BigInteger
*
* @return 最大ID值
*/
@Override
@Query("SELECT MAX(id) FROM OutdoorEnvironmentData")
BigInteger findMaxId();
}
@@ -0,0 +1,26 @@
package com.renkang.sync.repository;
import com.renkang.sync.entity.PollenData;
import org.springframework.data.jpa.repository.Query;
import org.springframework.stereotype.Repository;
import java.math.BigInteger;
/**
* @Name PollenRepository
* @Author YangYuanChen
* @Data 20242024/7/25下午4:58
*/
@Repository
public interface PollenRepository extends BaseRepository<PollenData> {
/**
* 查询所有数据并返回 ID 最大的 BigInteger
*
* @return 最大ID值
*/
@Override
@Query("SELECT MAX(id) FROM PollenData")
BigInteger findMaxId();
}
@@ -0,0 +1,26 @@
package com.renkang.sync.repository;
import com.renkang.sync.entity.WaterQualityData;
import org.springframework.data.jpa.repository.Query;
import org.springframework.stereotype.Repository;
import java.math.BigInteger;
/**
* @Name WaterRepository
* @Author YangYuanChen
* @Data 20242024/7/25下午4:59
*/
@Repository
public interface WaterRepository extends BaseRepository<WaterQualityData> {
/**
* 查询所有数据并返回 ID 最大的 BigInteger
*
* @return 最大ID值
*/
@Override
@Query("SELECT MAX(id) FROM WaterQualityData")
BigInteger findMaxId();
}
@@ -0,0 +1,123 @@
package com.renkang.sync.service;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.renkang.sync.entity.*;
import com.renkang.sync.repository.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import java.math.BigInteger;
import java.util.List;
import java.util.Objects;
/**
* @Name DataService
* @Author YangYuanChen
* @Data 20242024/7/26下午3:41
*/
@Service
@Slf4j
public class DataService {
private IndoorRepository indoorRepository;
private OutdoorRepository outdoorRepository;
private PollenRepository pollenRepository;
private WaterRepository waterRepository;
private ObjectMapper objectMapper;
@Autowired
public void setIndoorRepository(IndoorRepository indoorRepository) {
this.indoorRepository = indoorRepository;
}
@Autowired
public void setOutdoorRepository(OutdoorRepository outdoorRepository) {
this.outdoorRepository = outdoorRepository;
}
@Autowired
public void setPollenRepository(PollenRepository pollenRepository) {
this.pollenRepository = pollenRepository;
}
@Autowired
public void setWaterRepository(WaterRepository waterRepository) {
this.waterRepository = waterRepository;
}
@Autowired
public void setObjectMapper(ObjectMapper objectMapper) {
this.objectMapper = objectMapper;
}
@SuppressWarnings("unchecked")
public <T extends IdAware> Page<T> getPagedDate(Class<T> clz, BigInteger start, Pageable pageable) {
if (clz.equals(IndoorEnvironmentData.class)) {
return (Page<T>) indoorRepository.findByIdGreaterThan(start, pageable);
} else if (clz.equals(OutdoorEnvironmentData.class)) {
return (Page<T>) outdoorRepository.findByIdGreaterThan(start, pageable);
} else if (clz.equals(PollenData.class)) {
return (Page<T>) pollenRepository.findByIdGreaterThan(start, pageable);
} else if (clz.equals(WaterQualityData.class)) {
return (Page<T>) waterRepository.findByIdGreaterThan(start, pageable);
}
return null;
}
public void saveData(Class<? extends IdAware> clz, String json) throws JsonProcessingException {
if (clz.equals(IndoorEnvironmentData.class)) {
JavaType javaType = objectMapper.getTypeFactory().constructCollectionType(List.class, IndoorEnvironmentData.class);
List<IndoorEnvironmentData> data = objectMapper.readValue(json, javaType);
save(indoorRepository, data);
} else if (clz.equals(OutdoorEnvironmentData.class)) {
JavaType javaType = objectMapper.getTypeFactory().constructCollectionType(List.class, OutdoorEnvironmentData.class);
List<OutdoorEnvironmentData> data = objectMapper.readValue(json, javaType);
save(outdoorRepository, data);
} else if (clz.equals(PollenData.class)) {
JavaType javaType = objectMapper.getTypeFactory().constructCollectionType(List.class, PollenData.class);
List<PollenData> data = objectMapper.readValue(json, javaType);
save(pollenRepository, data);
} else if (clz.equals(WaterQualityData.class)) {
JavaType javaType = objectMapper.getTypeFactory().constructCollectionType(List.class, WaterQualityData.class);
List<WaterQualityData> data = objectMapper.readValue(json, javaType);
save(waterRepository, data);
}
}
private <T extends IdAware> void save(BaseRepository<T> repository, List<T> data) {
repository.saveAll(data);
}
public BigInteger getMaxId(Class<?> clz) {
BaseRepository<? extends IdAware> repository = getRepository(clz);
Objects.requireNonNull(repository, "No repository match.");
return repository.findMaxId();
}
private BaseRepository<? extends IdAware> getRepository(Class<?> clz) {
if (clz == null) {
return null;
}
if (clz.equals(IndoorEnvironmentData.class)) {
return indoorRepository;
} else if (clz.equals(OutdoorEnvironmentData.class)) {
return outdoorRepository;
} else if (clz.equals(PollenData.class)) {
return pollenRepository;
} else if (clz.equals(WaterQualityData.class)) {
return waterRepository;
} else {
return null;
}
}
}
@@ -0,0 +1,186 @@
package com.renkang.sync.service;
import cn.hutool.core.util.StrUtil;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer;
import com.renkang.sync.bean.Result;
import com.renkang.sync.bean.lefu.BatchRecord;
import com.renkang.sync.bean.lefu.Record;
import com.renkang.sync.config.LefuProperties;
import com.renkang.sync.entity.RemoteWeightManufacturerLefu;
import com.renkang.sync.repository.LefuRepository;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.http.*;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.util.ObjectUtils;
import org.springframework.web.client.RestTemplate;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* @author Jiang Shunzhi
*/
@Service
@Slf4j
public class LefuService {
public static final String X_ACCESS_TOKEN = "X-Access-Token";
public static String PUSH_DATA = "/remote/data/device/weighing/lefu/sync";
public static String CHECK_TOKEN = "/sys/checkToken";
public static String GET_TOKEN = "/sys/thirdLogin";
private final Map<String, String> tokenMap = new ConcurrentHashMap<>();
private final ObjectMapper objectMapper;
private LefuRepository lefuRepository;
private LefuProperties lefuProperties;
private RestTemplate restTemplate;
public LefuService() {
objectMapper = new ObjectMapper();
//处理bigDecimal
objectMapper.enable(JsonGenerator.Feature.WRITE_BIGDECIMAL_AS_PLAIN);
objectMapper.enable(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS);
//处理失败
objectMapper.configure(DeserializationFeature.FAIL_ON_IGNORED_PROPERTIES, false);
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
objectMapper.configure(DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES, false);
objectMapper.configure(DeserializationFeature.FAIL_ON_NULL_CREATOR_PROPERTIES, false);
//默认的处理日期时间格式
objectMapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
JavaTimeModule javaTimeModule = new JavaTimeModule();
javaTimeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
javaTimeModule.addSerializer(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
javaTimeModule.addSerializer(LocalTime.class, new LocalTimeSerializer(DateTimeFormatter.ofPattern("HH:mm:ss")));
javaTimeModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
javaTimeModule.addDeserializer(LocalDate.class, new LocalDateDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
javaTimeModule.addDeserializer(LocalTime.class, new LocalTimeDeserializer(DateTimeFormatter.ofPattern("HH:mm:ss")));
objectMapper.registerModule(javaTimeModule);
}
@Autowired
public void setRestTemplate(RestTemplate restTemplate) {
this.restTemplate = restTemplate;
}
@Autowired
public void setLefuProperties(LefuProperties lefuProperties) {
this.lefuProperties = lefuProperties;
}
@Autowired
public void setLefuRepository(LefuRepository lefuRepository) {
this.lefuRepository = lefuRepository;
}
@Async
public void sync(List<RemoteWeightManufacturerLefu> data) throws JsonProcessingException {
Map<String, LefuProperties.Service> sync = lefuProperties.getSync();
for (Map.Entry<String, LefuProperties.Service> entry : sync.entrySet()) {
String name = entry.getKey();
LefuProperties.Service service = entry.getValue();
String url = service.getBaseUrl() + PUSH_DATA;
ResponseEntity<Result<Void>> response = restTemplate.exchange(
url,
HttpMethod.POST,
new HttpEntity<>(objectMapper.writeValueAsString(data), getHeaders(name, service)),
new ParameterizedTypeReference<Result<Void>>() {
});
if (!response.getStatusCode().equals(HttpStatus.OK)
|| ObjectUtils.isEmpty(response.getBody())
|| !response.getBody().isSuccess()) {
throw new RuntimeException("Push Lefu Data Error remotely");
}
log.info("Push Lefu Success {} for {}", data.size(), name);
}
}
private HttpHeaders getHeaders(String name, LefuProperties.Service service) throws JsonProcessingException {
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.add(X_ACCESS_TOKEN, getToken(name, service));
return httpHeaders;
}
private String getToken(String name, LefuProperties.Service service) throws JsonProcessingException {
String token = tokenMap.get(name);
if (StrUtil.isBlank(token)) {
remoteToken(name, service);
} else {
if (!checkToken(token, service)) {
remoteToken(name, service);
}
}
return tokenMap.get(name);
}
private void remoteToken(String name, LefuProperties.Service service) throws JsonProcessingException {
Map<String, String> data = new HashMap<>();
data.put("username", service.getUsername());
data.put("password", service.getPassword());
String url = service.getBaseUrl() + GET_TOKEN;
ResponseEntity<Result<Map<String, String>>> response = restTemplate.exchange(
url,
HttpMethod.POST,
new HttpEntity<>(objectMapper.writeValueAsString(data)),
new ParameterizedTypeReference<Result<Map<String, String>>>() {
});
if (!response.getStatusCode().equals(HttpStatus.OK)
|| ObjectUtils.isEmpty(response.getBody())
|| !response.getBody().isSuccess()) {
throw new RuntimeException("Get Token Error remotely");
}
String token = response.getBody().getResult().get("token");
tokenMap.put(name, token);
}
private boolean checkToken(String token, LefuProperties.Service service) {
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.add(X_ACCESS_TOKEN, token);
String url = service.getBaseUrl() + CHECK_TOKEN;
ResponseEntity<Result<Void>> response = restTemplate.exchange(
url,
HttpMethod.POST,
new HttpEntity<>(httpHeaders),
new ParameterizedTypeReference<Result<Void>>() {
});
return response.getStatusCode().equals(HttpStatus.OK)
&& !ObjectUtils.isEmpty(response.getBody())
&& response.getBody().isSuccess();
}
public RemoteWeightManufacturerLefu record(Record record) {
return lefuRepository.save(record.toRemoteEntity());
}
public List<RemoteWeightManufacturerLefu> batchRecord(BatchRecord batchRecord) {
return lefuRepository.saveAll(batchRecord.toRemoteEntities());
}
public Page<RemoteWeightManufacturerLefu> list(Integer pageNo, Integer pageSize) {
Pageable pageable = PageRequest.of(pageNo, pageSize).withSort(Sort.by(Sort.Order.desc("createTime")));
return lefuRepository.findAll(pageable);
}
}
@@ -0,0 +1,212 @@
package com.renkang.sync.task;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.renkang.sync.bean.Result;
import com.renkang.sync.config.SyncProperties;
import com.renkang.sync.entity.*;
import com.renkang.sync.service.DataService;
import com.renkang.sync.util.Constants;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.scheduling.TaskScheduler;
import org.springframework.scheduling.support.CronTrigger;
import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.UriComponentsBuilder;
import javax.annotation.PostConstruct;
import java.math.BigInteger;
import java.util.List;
import static com.renkang.sync.util.Constants.*;
/**
* @Name SyncTask
* @Author YangYuanChen
* @Data 20242024/7/29下午7:45
*/
@Component
@Slf4j
@ConditionalOnProperty(prefix = "spring.cloud.nacos.discovery", name = "enabled", havingValue = "false", matchIfMissing = true)
public class SyncTask {
private static final int[] TYPES = new int[]{
INDOOR,
OUTDOOR,
POLLEN,
WATER
};
private DataService dataService;
private SyncProperties syncProperties;
private RestTemplate restTemplate;
private TaskScheduler schedulingTaskExecutor;
private ObjectMapper objectMapper;
@Autowired
public void setDataService(DataService dataService) {
this.dataService = dataService;
}
@Autowired
public void setSyncProperties(SyncProperties syncProperties) {
this.syncProperties = syncProperties;
}
@Autowired
public void setRestTemplate(RestTemplate restTemplate) {
this.restTemplate = restTemplate;
}
@Autowired
public void setSchedulingTaskExecutor(TaskScheduler schedulingTaskExecutor) {
this.schedulingTaskExecutor = schedulingTaskExecutor;
}
@Autowired
public void setObjectMapper(ObjectMapper objectMapper) {
this.objectMapper = objectMapper;
}
@PostConstruct
public void init() {
String cron = syncProperties.getCron();
CronTrigger trigger = new CronTrigger(cron);
for (String baseUrl : syncProperties.getBaseUrls()) {
log.info("Add schedule task for base: {}", baseUrl);
schedulingTaskExecutor.schedule(() -> {
try {
run(baseUrl);
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
}, trigger);
}
}
private void run(String baseUrl) throws JsonProcessingException {
for (int type : TYPES) {
try {
BigInteger remoteMaxId = getRemoteMaxId(baseUrl, type);
BigInteger localMaxId = getLocalMaxId(type);
log.info("MaxId for base {} is: {}", baseUrl, remoteMaxId);
if (remoteMaxId.compareTo(localMaxId) >= 0) {
log.info("remote base {} is up-to-date", baseUrl);
continue;
}
pushData(baseUrl, remoteMaxId, type);
} catch (Exception e) {
log.error("Error has occurred for base {} and type {}", baseUrl, type, e);
}
}
}
private BigInteger getRemoteMaxId(String baseUrl, int type) {
String url = getUrl(baseUrl, type, GET_MAX_ID);
ResponseEntity<Result<String>> response = restTemplate.exchange(
url,
HttpMethod.GET,
HttpEntity.EMPTY,
new ParameterizedTypeReference<Result<String>>() {
});
if (!response.getStatusCode().equals(HttpStatus.OK)
|| ObjectUtils.isEmpty(response.getBody())
|| !StringUtils.hasText(response.getBody().getResult())) {
errorLog(response);
throw new RuntimeException("Get maxId Error remotely");
}
return new BigInteger(response.getBody().getResult());
}
private BigInteger getLocalMaxId(int type) {
BigInteger maxId = dataService.getMaxId(Constants.getClass(type));
if (ObjectUtils.isEmpty(maxId)) {
throw new RuntimeException("Get maxId Error locally");
}
return maxId;
}
private void pushData(String baseUrl, BigInteger start, int type) throws JsonProcessingException {
Pageable pageable = pageableInstance();
Page<?> pagedData;
boolean first = true;
do {
if (first) {
first = false;
} else {
pageable = pageable.next();
}
pagedData = getPagedData(start, type, pageable);
if (pagedData.isEmpty()) {
return;
}
remotePush(baseUrl, pagedData.getContent(), type);
} while (pagedData.hasNext());
}
private Pageable pageableInstance() {
Sort sort = Sort.by(Sort.Order.asc(ID_FIELD));
return PageRequest.of(0, syncProperties.getSize(), sort);
}
private <T extends IdAware> Page<T> getPagedData(BigInteger start, Class<T> clz, Pageable pageable) {
return dataService.getPagedDate(clz, start, pageable);
}
private Page<?> getPagedData(BigInteger start, int type, Pageable pageable) {
if (type == INDOOR) {
return getPagedData(start, IndoorEnvironmentData.class, pageable);
} else if (type == OUTDOOR) {
return getPagedData(start, OutdoorEnvironmentData.class, pageable);
} else if (type == POLLEN) {
return getPagedData(start, PollenData.class, pageable);
} else if (type == WATER) {
return getPagedData(start, WaterQualityData.class, pageable);
} else {
throw new RuntimeException("Unknown type");
}
}
private void remotePush(String baseUrl, List<?> data, int type) throws JsonProcessingException {
String url = getUrl(baseUrl, type, SAVE_DATA);
ResponseEntity<Result<Void>> response = restTemplate.exchange(
url,
HttpMethod.POST,
new HttpEntity<>(objectMapper.writeValueAsString(data)),
new ParameterizedTypeReference<Result<Void>>() {
});
if (!response.getStatusCode().equals(HttpStatus.OK)
|| ObjectUtils.isEmpty(response.getBody())
|| !response.getBody().isSuccess()) {
errorLog(response);
throw new RuntimeException("Push Data Error remotely");
}
log.info("Push Success {} for type {}", data.size(), type);
}
private void errorLog(ResponseEntity<?> response) {
log.error("MaxId HttpCode: {}", response.getStatusCodeValue());
log.error("MaxId Response: {}", response.getBody());
}
private String getUrl(String baseUrl, int type, String api) {
return UriComponentsBuilder.fromHttpUrl(baseUrl + api)
.queryParam(TYPE_PARAM, type)
.encode()
.toUriString();
}
}
@@ -0,0 +1,46 @@
package com.renkang.sync.util;
import com.renkang.sync.entity.*;
/**
* @author Jiang Shunzhi
*/
public interface Constants {
int INDOOR = 1;
int OUTDOOR = 2;
int POLLEN = 3;
int WATER = 4;
String TYPE_PARAM = "type";
String ID_FIELD = "id";
String GET_MAX_ID = "/maxId";
String SAVE_DATA = "/saveData";
/**
* 根据字典类型返回对应的类
*
* @param type 类型
* @return 实体类
*/
static Class<? extends IdAware> getClass(int type) {
switch (type) {
case INDOOR:
return IndoorEnvironmentData.class;
case OUTDOOR:
return OutdoorEnvironmentData.class;
case POLLEN:
return PollenData.class;
case WATER:
return WaterQualityData.class;
default:
return null;
}
}
}
@@ -0,0 +1,54 @@
package com.renkang.sync.util;
import cn.hutool.core.lang.Snowflake;
import cn.hutool.core.util.IdUtil;
import lombok.extern.slf4j.Slf4j;
import org.hibernate.HibernateException;
import org.hibernate.MappingException;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.id.IdentifierGenerator;
import org.hibernate.service.ServiceRegistry;
import org.hibernate.type.Type;
import java.io.Serializable;
import java.util.Properties;
/**
* @author Jiang Shunzhi
*/
@Slf4j
public class SnowFlakeIdGenerator implements IdentifierGenerator {
private final Snowflake snowFlake;
private Class<?> type;
public SnowFlakeIdGenerator() {
this.snowFlake = IdUtil.getSnowflake();
}
public synchronized long snowflakeId() {
return snowFlake.nextId();
}
@Override
public Serializable generate(SharedSessionContractImplementor session, Object object)
throws HibernateException {
long id = snowflakeId();
if (Long.class.isAssignableFrom(type)) {
return id;
} else if (String.class.isAssignableFrom(type)) {
return Long.toString(id);
} else if (byte[].class.isAssignableFrom(type)) {
return Long.toUnsignedString(id).getBytes();
} else {
throw new HibernateException("Unanticipated return type [" + type.getName() + "] for ID conversion");
}
}
@Override
public void configure(Type type, Properties params, ServiceRegistry serviceRegistry) throws MappingException {
this.type = type.getReturnedClass();
}
}
@@ -0,0 +1,8 @@
PROFILE_NAME=dev
SERVER_PORT=27002
NACOS_SERVER_ADDR=nacos.yg.dt.io:80
NACOS_USERNAME=cqyt
NACOS_PASSWORD=Aa123456
NACOS_NAMESPACE=2639a2d4-7b64-4408-98c9-b97e0bdf4c1f
NACOS_GROUP=dev
NACOS_ENABLE=false
@@ -0,0 +1,27 @@
server:
port: ${SERVER_PORT:27001}
spring:
application:
name: env-sync
config:
import:
- optional:nacos:${spring.application.name}-${PROFILE_NAME}.yaml
cloud:
nacos:
server-addr: ${NACOS_SERVER_ADDR}
username: ${NACOS_USERNAME:nacos}
password: ${NACOS_PASSWORD:nacos}
config:
enabled: true
namespace: ${NACOS_NAMESPACE:}
group: ${NACOS_GROUP:DEFAULT_GROUP}
server-addr: ${spring.cloud.nacos.server-addr}
username: ${spring.cloud.nacos.username}
password: ${spring.cloud.nacos.password}
discovery:
enabled: ${NACOS_ENABLE:false}
namespace: ${NACOS_NAMESPACE:}
group: ${NACOS_GROUP:DEFAULT_GROUP}
server-addr: ${spring.cloud.nacos.server-addr}
username: ${spring.cloud.nacos.username}
password: ${spring.cloud.nacos.password}

Some files were not shown because too many files have changed in this diff Show More