diff --git a/xinelu-admin/src/main/resources/application.yml b/xinelu-admin/src/main/resources/application.yml index 9f59fc2..3685ecc 100644 --- a/xinelu-admin/src/main/resources/application.yml +++ b/xinelu-admin/src/main/resources/application.yml @@ -169,3 +169,10 @@ xss: excludes: /system/notice # 匹配链接 urlPatterns: /system/*,/monitor/*,/tool/* + +# 腾讯云音视频 +trtc: + sdkappid: 1400236771 + sdksecretkey: 83ab78d1a8513af6626d58cc2bacd7b28bfb2af06515fa02b0204129ebb53422 + secretid: AKIDOBpP2ICALat0wP4lcIiAMtZ7XgUJ5vMO + secretkey: zxjJhGcx75lECyweHgphKYefWCkBPSHt diff --git a/xinelu-common/pom.xml b/xinelu-common/pom.xml index 657082d..e68911b 100644 --- a/xinelu-common/pom.xml +++ b/xinelu-common/pom.xml @@ -17,6 +17,17 @@ + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-tomcat + + + + org.springframework @@ -135,6 +146,40 @@ org.projectlombok lombok + + + org.springframework + spring-websocket + + + com.squareup.okio + okio + 1.12.0 + + + com.squareup.okhttp + okhttp + 2.7.5 + + + com.google.code.gson + gson + + + javax.xml.bind + jaxb-api + + + + + cn.hutool + hutool-all + 5.4.7 + + + org.apache.tomcat.embed + tomcat-embed-websocket + diff --git a/xinelu-common/src/main/java/com/xinelu/common/core/dto/MessageTemplate.java b/xinelu-common/src/main/java/com/xinelu/common/core/dto/MessageTemplate.java new file mode 100644 index 0000000..f5a80de --- /dev/null +++ b/xinelu-common/src/main/java/com/xinelu/common/core/dto/MessageTemplate.java @@ -0,0 +1,55 @@ +package com.xinelu.common.core.dto; + +import java.util.Date; +import lombok.Data; + +/** + * shitianqi
+ * 2022-01-19 19:02
+ * 长连接消息模板 + */ +@Data +public class MessageTemplate { + /** + * 发送消息对象key值(消息来源) + */ + private String fromKey; + + /** + * 发送消息对象Name值(消息来源) + */ + private String fromName; + + /** + * 接收消息对象key值(消息去向) + */ + private String toKey; + + /** + * 接收消息对象name值(消息去向) + */ + private String toName; + + /** + * 消息内容(Json) + */ + private String message; + + /** + * 消息类型(标识在{@link com.xinelu.common.enums.MessageContentType}中) + */ + private String msgType; + + /** + * 发送时间 + * */ + private Date sendTime; + + /** + * 问诊记录业务主键 + */ + private String recordId; + + /** 问诊房间号 */ + private String roomNo; +} diff --git a/xinelu-common/src/main/java/com/xinelu/common/enums/MessageContentType.java b/xinelu-common/src/main/java/com/xinelu/common/enums/MessageContentType.java new file mode 100644 index 0000000..17b2b60 --- /dev/null +++ b/xinelu-common/src/main/java/com/xinelu/common/enums/MessageContentType.java @@ -0,0 +1,21 @@ +package com.xinelu.common.enums; + +import lombok.Getter; + +/** + * shitainqi
+ * 2022-01-18 21:01 + * 长连接内容对象枚举列表(在此处增加长连接返回对象) + */ +@Getter +public enum MessageContentType { + + DEVICE, + NOTICE, + PUSH, + CHAT, + /** 会诊 */ + CONSULTATION, + /** 问诊 */ + VIDEO +} diff --git a/xinelu-common/src/main/java/com/xinelu/common/socket/WebSocket.java b/xinelu-common/src/main/java/com/xinelu/common/socket/WebSocket.java new file mode 100644 index 0000000..01b3083 --- /dev/null +++ b/xinelu-common/src/main/java/com/xinelu/common/socket/WebSocket.java @@ -0,0 +1,74 @@ +package com.xinelu.common.socket; + +import java.io.IOException; +import java.util.Map; +import javax.websocket.OnClose; +import javax.websocket.OnError; +import javax.websocket.OnMessage; +import javax.websocket.OnOpen; +import javax.websocket.Session; +import javax.websocket.server.PathParam; +import javax.websocket.server.ServerEndpoint; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Component; + +@Slf4j +@Component +@ServerEndpoint(value = "/webSocket/{key}") +public class WebSocket { + + /** + * 连接建立成功调用的方法 + */ + @OnOpen + public void onOpen(@PathParam(value = "key") String key, Session session) throws IOException { + if (StringUtils.isNotBlank(key)) { + WebSocketUtils.add(key, session); + } else { + session.getAsyncRemote().sendText("来自服务器的消息:您未传递key值"); + } + } + + /** + * 连接关闭调用的方法 + */ + @OnClose + public void onClose(@PathParam("key") String key) throws IOException { + WebSocketUtils.remove(key); + } + + /** + * 收到客户端消息后调用的方法 + * + * @param message 客户端发送过来的消息 + */ + @OnMessage + public void onMessage(String message, Session fromSession) { + if (message.equals("ping")) { + return; + } + for (Map.Entry sessionEntry : WebSocketUtils.clients.entrySet()) { + // 获取自己 + if (fromSession.getId().equals(sessionEntry.getValue().getId())) { + //打印消息 + WebSocketUtils.receive(sessionEntry.getKey(), message); + } + } + + } + + @OnError + public void onError(Session session, Throwable error) throws IOException { + for (Map.Entry sessionEntry : WebSocketUtils.clients.entrySet()) { + // 获取自己 + if (session.getId().equals(sessionEntry.getValue().getId())) { + //清除异常连接 + WebSocketUtils.remove(sessionEntry.getKey()); + } + } + log.error("发生错误"); + error.printStackTrace(); + } + +} diff --git a/xinelu-common/src/main/java/com/xinelu/common/socket/WebSocketConfig.java b/xinelu-common/src/main/java/com/xinelu/common/socket/WebSocketConfig.java new file mode 100644 index 0000000..aed392b --- /dev/null +++ b/xinelu-common/src/main/java/com/xinelu/common/socket/WebSocketConfig.java @@ -0,0 +1,17 @@ +package com.xinelu.common.socket; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.socket.server.standard.ServerEndpointExporter; + +@Configuration +public class WebSocketConfig { + + /** + * 注入一个ServerEndpointExporter,该Bean会自动注册使用@ServerEndpoint注解申明的websocket endpoint + */ + @Bean + public ServerEndpointExporter serverEndpointExporter() { + return new ServerEndpointExporter(); + } +} diff --git a/xinelu-common/src/main/java/com/xinelu/common/socket/WebSocketUtils.java b/xinelu-common/src/main/java/com/xinelu/common/socket/WebSocketUtils.java new file mode 100644 index 0000000..80dbf38 --- /dev/null +++ b/xinelu-common/src/main/java/com/xinelu/common/socket/WebSocketUtils.java @@ -0,0 +1,102 @@ +package com.xinelu.common.socket; + +import com.alibaba.fastjson2.JSON; +import com.xinelu.common.core.dto.MessageTemplate; +import java.io.IOException; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicInteger; +import javax.websocket.Session; +import lombok.extern.slf4j.Slf4j; + +/** + * shitianqi + * 2022-01-18 19:17 + * socket类和请求和方法 + */ +@Slf4j +public class WebSocketUtils { + + + //静态变量,用来记录当前在线连接数。应该把它设计成线程安全的。 + /** + * 记录当前在线连接数 + */ + public static AtomicInteger onlineCount = new AtomicInteger(); + + /** + * 存放所有在线的客户端 + */ + public static ConcurrentHashMap clients = new ConcurrentHashMap<>(); + + /** + * shitianqi
+ * 2022-01-18 19:47
+ * 上线用户
+ * + * @param key + * @param session + */ + public static void add(String key, Session session) throws IOException { + remove(key); + clients.put(key, session); + onlineCount.incrementAndGet(); // 在线数加1 + int count = onlineCount.get(); + log.info("有新连接加入:{},当前在线人数为:{}", key, count > 0? count: 0); + } + + + /** + * shitianqi
+ * 2022-01-18 19:47
+ * 打印某在线用户接收消息内容和在线情况到日志
+ * + * @param key + * @param message + */ + public static void receive(String key, String message) { + log.info("服务端收到客户端[{}]的消息:{}", key, message); +// log.info("当前连接数 = " + clients.size()); + } + + /** + * shitianqi
+ * 2022-01-18 19:47
+ * 下线用户
+ * + * @param key + */ + public static void remove(String key) throws IOException { + if (clients.containsKey(key)) { + clients.get(key).close(); + clients.remove(key); + onlineCount.decrementAndGet(); // 在线数减1 +// log.info("有一连接关闭:{},当前在线人数为:{}", key, onlineCount.get()); + } +// log.info("当前连接数 = " + clients.size()); + } + + /** + * shitianqi
+ * 2022-01-18 19:47
+ * 发送消息(内容为对象json)
+ * + * @param key + * @param msg + * @return boolean 是否成功 + */ + public static boolean sendMessage(String key, MessageTemplate msg) { +// log.info("当前连接数 = " + clients.size()); + if (clients.get(key) == null) { + return false; + } else { + try { + log.info("发送消息:" + JSON.toJSONString(msg)); + clients.get(key).getBasicRemote().sendText(JSON.toJSONString(msg)); + return true; + } catch (IOException e) { + e.printStackTrace(); + return false; + } + } + } +} diff --git a/xinelu-common/src/main/java/com/xinelu/common/utils/tencentcloudapi/common/AbstractClient.java b/xinelu-common/src/main/java/com/xinelu/common/utils/tencentcloudapi/common/AbstractClient.java new file mode 100644 index 0000000..ab12ccc --- /dev/null +++ b/xinelu-common/src/main/java/com/xinelu/common/utils/tencentcloudapi/common/AbstractClient.java @@ -0,0 +1,521 @@ +/* + * Copyright (c) 2018 THL A29 Limited, a Tencent company. 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. + */ + +package com.xinelu.common.utils.tencentcloudapi.common; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonSyntaxException; +import com.google.gson.reflect.TypeToken; +import com.squareup.okhttp.Headers; +import com.squareup.okhttp.Headers.Builder; +import com.squareup.okhttp.Response; +import com.xinelu.common.utils.tencentcloudapi.common.exception.TencentCloudSDKException; +import com.xinelu.common.utils.tencentcloudapi.common.http.HttpConnection; +import com.xinelu.common.utils.tencentcloudapi.common.profile.ClientProfile; +import com.xinelu.common.utils.tencentcloudapi.common.profile.HttpProfile; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.lang.reflect.Type; +import java.net.URLEncoder; +import java.sql.Date; +import java.text.SimpleDateFormat; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; +import java.util.Random; +import java.util.TimeZone; +import java.util.TreeMap; +import java.util.UUID; +import javax.crypto.Mac; +import javax.net.ssl.SSLContext; +import javax.xml.bind.DatatypeConverter; + +/** + * 抽象client类 + */ +abstract public class AbstractClient { + + public static final int HTTP_RSP_OK = 200; + public static final String SDK_VERSION = "SDK_JAVA_3.0.89"; + + + private Credential credential; + private ClientProfile profile; + private String endpoint; + private String region; + private String path; + private String sdkVersion; + private String apiVersion; + public Gson gson; + + public AbstractClient(String endpoint, String version, Credential credential, String region) { + this(endpoint, version, credential, region, new ClientProfile()); + } + + public AbstractClient(String endpoint, String version, Credential credential, String region, + ClientProfile profile) { + + this.credential = credential; + this.profile = profile; + this.endpoint = endpoint; + this.region = region; + this.path = "/"; + this.sdkVersion = AbstractClient.SDK_VERSION; + this.apiVersion = version; + this.gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create(); + warmup(); + } + + /** + * 设置产品地域 + * + * @param region + * 产品地域 + */ + public void setRegion(String region) { + this.region = region; + } + + /** + * 返回产品地域 + * + * @return 地域名称 + */ + public String getRegion() { + return this.region; + } + + /** + * 设置配置实例 + * + * @param profile + * 配置实例 + */ + public void setClientProfile(ClientProfile profile) { + this.profile = profile; + } + + /** + * 获取配置实例 + * + * @return 配置实例 + */ + public ClientProfile getClientProfile() { + return this.profile; + } + + /** + * 设置认证信息实例 + * + * @param credential + * 认证信息实例 + */ + public void setCredential(Credential credential) { + this.credential = credential; + } + + /** + * 获取认证信息实例 + * + * @return 认证信息实例 + */ + public Credential getCredential() { + return this.credential; + } + + /** + * Use post/json with tc3-hmac-sha256 signature to call any action. Ignore + * request method and signature method defined in profile. + * + * @param action + * Name of action to be called. + * @param jsonPayload + * Parameters of action serialized in json string format. + * @return Raw response from API if request succeeded, otherwise an exception + * will be raised instead of raw response + * @throws TencentCloudSDKException + */ + public String call(String action, String jsonPayload) throws TencentCloudSDKException { + String endpoint = this.endpoint; + // in case user has reset endpoint after init this client + if (!(this.profile.getHttpProfile().getEndpoint() == null)) { + endpoint = this.profile.getHttpProfile().getEndpoint(); + } + // always use post tc3-hmac-sha256 signature process + // okhttp always set charset even we don't specify it, + // to ensure signature be correct, we have to set it here as well. + String contentType = "application/json; charset=utf-8"; + byte[] requestPayload = jsonPayload.getBytes(); + String canonicalUri = "/"; + String canonicalQueryString = ""; + String canonicalHeaders = "content-type:" + contentType + "\nhost:" + endpoint + "\n"; + String signedHeaders = "content-type;host"; + + String hashedRequestPayload = ""; + if (this.profile.isUnsignedPayload()) { + hashedRequestPayload = Sign.sha256Hex("UNSIGNED-PAYLOAD".getBytes()); + } else { + hashedRequestPayload = Sign.sha256Hex(requestPayload); + } + String canonicalRequest = HttpProfile.REQ_POST + "\n" + canonicalUri + "\n" + canonicalQueryString + "\n" + + canonicalHeaders + "\n" + signedHeaders + "\n" + hashedRequestPayload; + + String timestamp = String.valueOf(System.currentTimeMillis() / 1000); + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); + sdf.setTimeZone(TimeZone.getTimeZone("UTC")); + String date = sdf.format(new Date(Long.valueOf(timestamp + "000"))); + String service = endpoint.split("\\.")[0]; + String credentialScope = date + "/" + service + "/" + "tc3_request"; + String hashedCanonicalRequest = Sign.sha256Hex(canonicalRequest.getBytes()); + String stringToSign = "TC3-HMAC-SHA256\n" + timestamp + "\n" + credentialScope + "\n" + hashedCanonicalRequest; + + String secretId = this.credential.getSecretId(); + String secretKey = this.credential.getSecretKey(); + byte[] secretDate = Sign.hmac256(("TC3" + secretKey).getBytes(), date); + byte[] secretService = Sign.hmac256(secretDate, service); + byte[] secretSigning = Sign.hmac256(secretService, "tc3_request"); + String signature = DatatypeConverter.printHexBinary(Sign.hmac256(secretSigning, stringToSign)).toLowerCase(); + String authorization = "TC3-HMAC-SHA256 " + "Credential=" + secretId + "/" + credentialScope + ", " + + "SignedHeaders=" + signedHeaders + ", " + "Signature=" + signature; + + HttpConnection conn = new HttpConnection(this.profile.getHttpProfile().getConnTimeout(), + this.profile.getHttpProfile().getReadTimeout(), this.profile.getHttpProfile().getWriteTimeout()); + String url = this.profile.getHttpProfile().getProtocol() + endpoint + this.path; + Builder hb = new Headers.Builder(); + hb.add("Content-Type", contentType).add("Host", endpoint).add("Authorization", authorization) + .add("X-TC-Action", action).add("X-TC-Timestamp", timestamp).add("X-TC-Version", this.apiVersion) + .add("X-TC-Region", this.getRegion()).add("X-TC-RequestClient", SDK_VERSION); + String token = this.credential.getToken(); + if (token != null && !token.isEmpty()) { + hb.add("X-TC-Token", token); + } + if (this.profile.isUnsignedPayload()) { + hb.add("X-TC-Content-SHA256", "UNSIGNED-PAYLOAD"); + } + + Headers headers = hb.build(); + Response resp = conn.postRequest(url, requestPayload, headers); + if (resp.code() != AbstractClient.HTTP_RSP_OK) { + throw new TencentCloudSDKException(resp.code() + resp.message()); + } + String respbody = null; + try { + respbody = resp.body().string(); + } catch (IOException e) { + throw new TencentCloudSDKException(e.getClass().getName() + "-" + e.getMessage()); + } + JsonResponseModel errResp = null; + try { + Type errType = new TypeToken>() { + }.getType(); + errResp = gson.fromJson(respbody, errType); + } catch (JsonSyntaxException e) { + throw new TencentCloudSDKException(e.getClass().getName() + "-" + e.getMessage()); + } + if (errResp.response.error != null) { + throw new TencentCloudSDKException(errResp.response.error.code + "-" + errResp.response.error.message, + errResp.response.requestId); + } + return respbody; + } + + protected String internalRequest(AbstractModel request, String actionName) throws TencentCloudSDKException { + Response okRsp = null; + String endpoint = this.endpoint; + if (!(this.profile.getHttpProfile().getEndpoint() == null)) { + endpoint = this.profile.getHttpProfile().getEndpoint(); + } + String [] binaryParams = request.getBinaryParams(); + String sm = this.profile.getSignMethod(); + String reqMethod = this.profile.getHttpProfile().getReqMethod(); + + // currently, customized params only can be supported via post json tc3-hmac-sha256 + HashMap customizedParams = request.any(); + if (customizedParams.size() > 0) { + if (binaryParams.length > 0) { + throw new TencentCloudSDKException("WrongUsage: Cannot post multipart with customized parameters."); + } + if (sm.equals(ClientProfile.SIGN_SHA1) || sm.equals(ClientProfile.SIGN_SHA256)) { + throw new TencentCloudSDKException("WrongUsage: Cannot use HmacSHA1 or HmacSHA256 with customized parameters."); + } + if (reqMethod.equals(HttpProfile.REQ_GET)) { + throw new TencentCloudSDKException("WrongUsage: Cannot use get method with customized parameters."); + } + } + + if (binaryParams.length > 0 || sm.equals(ClientProfile.SIGN_TC3_256)) { + okRsp = doRequestWithTC3(endpoint, request, actionName); + } else if (sm.equals(ClientProfile.SIGN_SHA1) || sm.equals(ClientProfile.SIGN_SHA256)) { + okRsp = doRequest(endpoint, request, actionName); + } else { + throw new TencentCloudSDKException("Signature method " + sm + " is invalid or not supported yet."); + } + + if (okRsp.code() != AbstractClient.HTTP_RSP_OK) { + throw new TencentCloudSDKException(okRsp.code() + okRsp.message()); + } + String strResp = null; + try { + strResp = okRsp.body().string(); + } catch (IOException e) { + throw new TencentCloudSDKException(e.getClass().getName() + "-" + e.getMessage()); + } + + JsonResponseModel errResp = null; + try { + Type errType = new TypeToken>() { + }.getType(); + errResp = gson.fromJson(strResp, errType); + } catch (JsonSyntaxException e) { + throw new TencentCloudSDKException(e.getClass().getName() + "-" + e.getMessage()); + } + if (errResp.response.error != null) { + throw new TencentCloudSDKException(errResp.response.error.code + "-" + errResp.response.error.message, + errResp.response.requestId); + } + + return strResp; + } + + private Response doRequest(String endpoint, AbstractModel request, String action) throws TencentCloudSDKException { + HashMap param = new HashMap(); + request.toMap(param, ""); + String strParam = this.formatRequestData(action, param); + HttpConnection conn = new HttpConnection( + this.profile.getHttpProfile().getConnTimeout(), + this.profile.getHttpProfile().getReadTimeout(), + this.profile.getHttpProfile().getWriteTimeout()); + String reqMethod = this.profile.getHttpProfile().getReqMethod(); + String url = this.profile.getHttpProfile().getProtocol() + endpoint + this.path; + if (reqMethod.equals(HttpProfile.REQ_GET)) { + return conn.getRequest(url + "?" + strParam); + } else if (reqMethod.equals(HttpProfile.REQ_POST)) { + return conn.postRequest(url, strParam); + } else { + throw new TencentCloudSDKException("Method only support (GET, POST)"); + } + } + + private Response doRequestWithTC3(String endpoint, AbstractModel request, String action) throws TencentCloudSDKException { + String httpRequestMethod = this.profile.getHttpProfile().getReqMethod(); + if (httpRequestMethod == null) { + throw new TencentCloudSDKException("Request method should not be null, can only be GET or POST"); + } + String contentType = "application/x-www-form-urlencoded"; + byte [] requestPayload = "".getBytes(); + HashMap params = new HashMap(); + request.toMap(params, ""); + String [] binaryParams = request.getBinaryParams(); + if ( binaryParams.length > 0 ) { + httpRequestMethod = HttpProfile.REQ_POST; + String boundary = UUID.randomUUID().toString(); + // okhttp always set charset even we don't specify it, + // to ensure signature be correct, we have to set it here as well. + contentType = "multipart/form-data; charset=utf-8" + "; boundary=" + boundary; + try { + requestPayload = getMultipartPayload(request, boundary); + } catch (Exception e) { + throw new TencentCloudSDKException("Failed to generate multipart. because: " + e); + } + } else if (httpRequestMethod.equals(HttpProfile.REQ_POST)) { + requestPayload = AbstractModel.toJsonString(request).getBytes(); + // okhttp always set charset even we don't specify it, + // to ensure signature be correct, we have to set it here as well. + contentType = "application/json; charset=utf-8"; + } + String canonicalUri = "/"; + String canonicalQueryString = this.getCanonicalQueryString(params, httpRequestMethod); + String canonicalHeaders = "content-type:" + contentType + "\nhost:" + endpoint + "\n"; + String signedHeaders = "content-type;host"; + + String hashedRequestPayload = ""; + if (this.profile.isUnsignedPayload()) { + hashedRequestPayload = Sign.sha256Hex("UNSIGNED-PAYLOAD".getBytes()); + } else { + hashedRequestPayload = Sign.sha256Hex(requestPayload); + } + String canonicalRequest = httpRequestMethod + "\n" + canonicalUri + "\n" + canonicalQueryString + "\n" + + canonicalHeaders + "\n" + signedHeaders + "\n" + hashedRequestPayload; + + String timestamp = String.valueOf(System.currentTimeMillis() / 1000); + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); + sdf.setTimeZone(TimeZone.getTimeZone("UTC")); + String date = sdf.format(new Date(Long.valueOf(timestamp + "000"))); + String service = endpoint.split("\\.")[0]; + String credentialScope = date + "/" + service + "/" + "tc3_request"; + String hashedCanonicalRequest = Sign.sha256Hex(canonicalRequest.getBytes()); + String stringToSign = "TC3-HMAC-SHA256\n" + timestamp + "\n" + credentialScope + "\n" + hashedCanonicalRequest; + + String secretId = this.credential.getSecretId(); + String secretKey = this.credential.getSecretKey(); + byte[] secretDate = Sign.hmac256(("TC3" + secretKey).getBytes(), date); + byte[] secretService = Sign.hmac256(secretDate, service); + byte[] secretSigning = Sign.hmac256(secretService, "tc3_request"); + String signature = DatatypeConverter.printHexBinary(Sign.hmac256(secretSigning, stringToSign)).toLowerCase(); + String authorization = "TC3-HMAC-SHA256 " + "Credential=" + secretId + "/" + credentialScope + ", " + + "SignedHeaders=" + signedHeaders + ", " + "Signature=" + signature; + + HttpConnection conn = new HttpConnection( + this.profile.getHttpProfile().getConnTimeout(), + this.profile.getHttpProfile().getReadTimeout(), + this.profile.getHttpProfile().getWriteTimeout()); + String url = this.profile.getHttpProfile().getProtocol() + endpoint + this.path; + Builder hb = new Headers.Builder(); + hb.add("Content-Type", contentType) + .add("Host", endpoint) + .add("Authorization", authorization) + .add("X-TC-Action", action) + .add("X-TC-Timestamp", timestamp) + .add("X-TC-Version", this.apiVersion) + .add("X-TC-RequestClient", SDK_VERSION); + if (null != this.getRegion()) { + hb.add("X-TC-Region", this.getRegion()); + } + String token = this.credential.getToken(); + if (token != null && ! token.isEmpty()) { + hb.add("X-TC-Token", token); + } + if (this.profile.isUnsignedPayload()) { + hb.add("X-TC-Content-SHA256", "UNSIGNED-PAYLOAD"); + } + + Headers headers = hb.build(); + if (httpRequestMethod.equals(HttpProfile.REQ_GET)) { + return conn.getRequest(url + "?" + canonicalQueryString, headers); + } else if (httpRequestMethod.equals(HttpProfile.REQ_POST)) { + return conn.postRequest(url, requestPayload, headers); + } else { + throw new TencentCloudSDKException("Method only support GET, POST"); + } + } + + private byte [] getMultipartPayload(AbstractModel request, String boundary) throws Exception { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + String [] binaryParams = request.getBinaryParams(); + for (Map.Entry entry : request.getMultipartRequestParams().entrySet()) { + baos.write("--".getBytes()); + baos.write(boundary.getBytes()); + baos.write("\r\n".getBytes()); + baos.write("Content-Disposition: form-data; name=\"".getBytes()); + baos.write(entry.getKey().getBytes()); + if (Arrays.asList(binaryParams).contains(entry.getKey())) { + baos.write("\"; filename=\"".getBytes()); + baos.write(entry.getKey().getBytes()); + baos.write("\"\r\n".getBytes()); + } else { + baos.write("\"\r\n".getBytes()); + } + baos.write("\r\n".getBytes()); + baos.write(entry.getValue()); + baos.write("\r\n".getBytes()); + } + if (baos.size() != 0) { + baos.write("--".getBytes()); + baos.write(boundary.getBytes()); + baos.write("--\r\n".getBytes()); + } + byte [] bytes = baos.toByteArray(); + baos.close(); + return bytes; + } + + private String getCanonicalQueryString(HashMap params, String method) throws TencentCloudSDKException { + if ( method != null && method.equals(HttpProfile.REQ_POST)) { + return ""; + } + StringBuilder queryString = new StringBuilder(""); + for (Map.Entry entry : params.entrySet()) { + String v; + try { + v = URLEncoder.encode(entry.getValue(), "UTF8"); + } catch (UnsupportedEncodingException e) { + throw new TencentCloudSDKException("UTF8 is not supported." + e.getMessage()); + } + queryString.append("&") + .append(entry.getKey()) + .append("=") + .append(v); + } + return queryString.toString().substring(1); + } + + private String formatRequestData(String action, Map param) throws TencentCloudSDKException { + param.put("Action", action); + param.put("RequestClient", this.sdkVersion); + param.put("Nonce", String.valueOf(Math.abs(new Random().nextInt()))); + param.put("Timestamp", String.valueOf(System.currentTimeMillis() / 1000)); + param.put("Version", this.apiVersion); + + if (this.credential.getSecretId() != null && (!this.credential.getSecretId().isEmpty())) { + param.put("SecretId", this.credential.getSecretId()); + } + + if (this.region != null && (!this.region.isEmpty())) { + param.put("Region", this.region); + } + + if (this.profile.getSignMethod() != null && (!this.profile.getSignMethod().isEmpty())) { + param.put("SignatureMethod", this.profile.getSignMethod()); + } + + if (this.credential.getToken() != null && (!this.credential.getToken().isEmpty())) { + param.put("Token", this.credential.getToken()); + } + + String endpoint = this.endpoint; + if (!(this.profile.getHttpProfile().getEndpoint() == null)) { + endpoint = this.profile.getHttpProfile().getEndpoint(); + } + + String sigInParam = Sign.makeSignPlainText(new TreeMap(param), + this.profile.getHttpProfile().getReqMethod(), endpoint, this.path); + String sigOutParam = Sign.sign(this.credential.getSecretKey(), sigInParam, this.profile.getSignMethod()); + + String strParam = ""; + try { + for (Map.Entry entry : param.entrySet()) { + strParam += (URLEncoder.encode(entry.getKey(), "utf-8") + "=" + + URLEncoder.encode(entry.getValue(), "utf-8") + "&"); + } + strParam += ("Signature=" + URLEncoder.encode(sigOutParam, "utf-8")); + } catch (UnsupportedEncodingException e) { + throw new TencentCloudSDKException(e.getClass().getName() + "-" + e.getMessage()); + } + return strParam; + } + + /** + * warm up, try to avoid unnecessary cost in the first request + */ + private void warmup() { + try { + // it happens in SDK signature process. + // first invoke costs around 250 ms. + Mac.getInstance("HmacSHA1"); + Mac.getInstance("HmacSHA256"); + // it happens inside okhttp, but I think any https framework/package will do the same. + // first invoke costs around 150 ms. + SSLContext sslContext = SSLContext.getInstance("TLS"); + sslContext.init(null, null, null); + } catch (Exception e) { + // ignore but print message to console + e.printStackTrace(); + } + } +} diff --git a/xinelu-common/src/main/java/com/xinelu/common/utils/tencentcloudapi/common/AbstractModel.java b/xinelu-common/src/main/java/com/xinelu/common/utils/tencentcloudapi/common/AbstractModel.java new file mode 100644 index 0000000..3a0c7d9 --- /dev/null +++ b/xinelu-common/src/main/java/com/xinelu/common/utils/tencentcloudapi/common/AbstractModel.java @@ -0,0 +1,165 @@ +/* + * Copyright (c) 2018 THL A29 Limited, a Tencent company. 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. + */ + +package com.xinelu.common.utils.tencentcloudapi.common; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import java.lang.reflect.Field; +import java.util.HashMap; +import java.util.Map; + +/** + * 抽象model类 + */ +abstract public class AbstractModel { + // any stores customized parameters which are not documented. + // You should make sure it can be correctly serialized to json string. + private HashMap customizedParams = new HashMap(); + + protected abstract void toMap(HashMap map, String prefix); + + /* + * valid only when it's a request object. + * some actions can only be posted in multipart format, + * this method is used to mark which parameters are binary type. + */ + protected String [] getBinaryParams() { + return new String[0]; + } + + /* + * valid only when it's a multipart request object. + */ + protected HashMap getMultipartRequestParams() { + return new HashMap(); + } + + protected void setParamSimple(HashMap map, String key, V value) { + if (value != null) { + + key = key.substring(0, 1).toUpperCase() + key.substring(1); + key = key.replace("_", "."); + map.put(key, String.valueOf(value)); + } + } + + protected void setParamArraySimple(HashMap map, String prefix, V [] array) { + if (array != null) { + for (int i = 0; i < array.length; i++) { + this.setParamSimple(map, prefix + i, array[i]); + } + } + } + + protected void setParamObj(HashMap map, String prefix, V obj) { + if (obj != null) { + obj.toMap(map, prefix); + } + } + + protected void setParamArrayObj(HashMap map, String prefix, V [] array) { + if (array != null) { + for (int i = 0; i < array.length; i++) { + this.setParamObj(map, prefix + i + ".", array[i]); + } + } + } + + /** + * 序列化函数,将对象数据转化为json格式的string + * + * @param obj + * 集成自AbstractModel的子类实例 + * @return json格式的string + */ + public static String toJsonString(O obj) { + return toJsonObject(obj).toString(); + } + + /** + * Recursively generate obj's JSON object. Even if obj.any() is empty, this + * recursive progress cannot be skipped because customized additional parameter + * might be hidden in lower data structure. + * + * @param obj + * @return + */ + private static JsonObject toJsonObject(O obj) { + Gson gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create(); + JsonObject joall = new JsonObject(); + JsonObject joadd = gson.toJsonTree(obj.any()).getAsJsonObject(); + for (Map.Entry entry : joadd.entrySet()) { + joall.add(entry.getKey(), entry.getValue()); + } + // jopublic will override joadd if key conflict exists + JsonObject jopublic = gson.toJsonTree(obj).getAsJsonObject(); + for (Map.Entry entry : jopublic.entrySet()) { + Object fo = null; + try { + Field f = obj.getClass().getDeclaredField(entry.getKey()); + f.setAccessible(true); + fo = f.get(obj); + } catch (Exception e) { + // this should never happen + e.printStackTrace(); + } + if (fo instanceof AbstractModel) { + joall.add(entry.getKey(), toJsonObject((AbstractModel)fo)); + } else { + joall.add(entry.getKey(), entry.getValue()); + } + } + return joall; + } + + /** + * 序列化函数,根据传入的json格式的string实例化一个cls对象返回 + * + * @param json + * json格式的string + * @param cls + * 与json匹配的类对象 + * @return cls的实例 + */ + public static O fromJsonString(String json, Class cls) { + Gson gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create(); + return gson.fromJson(json, cls); + } + + /** + * Set any key value pair to this model. + * + * @param key + * @param value + */ + public void set(String key, Object value) { + this.customizedParams.put(key, value); + } + + /** + * Get customized key value pairs from this model. + * + * @return + */ + public HashMap any() { + return this.customizedParams; + } +} + diff --git a/xinelu-common/src/main/java/com/xinelu/common/utils/tencentcloudapi/common/Credential.java b/xinelu-common/src/main/java/com/xinelu/common/utils/tencentcloudapi/common/Credential.java new file mode 100644 index 0000000..7bdde2f --- /dev/null +++ b/xinelu-common/src/main/java/com/xinelu/common/utils/tencentcloudapi/common/Credential.java @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2018 THL A29 Limited, a Tencent company. 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. + */ + +package com.xinelu.common.utils.tencentcloudapi.common; + +/** + * 认证相关信息类 + */ +public class Credential { + + /** + * secretId,在控制台申请 + */ + private String secretId; + + /** + * secretKey,在控制台申请 + */ + private String secretKey; + + /** + * token + */ + private String token; + + /** + * @param secretId 在控制台申请 + * @param secretKey 在控制台申请 + */ + public Credential(String secretId, String secretKey) { + this(secretId, secretKey, ""); + } + + /** + * @param secretId 在控制台申请 + * @param secretKey 在控制台申请 + * @param token + */ + public Credential(String secretId, String secretKey, String token) { + this.secretId = secretId; + this.secretKey = secretKey; + this.token = token; + } + + /** + * 设置secretId + * @param secretId + */ + public void setSecretId(String secretId) { + this.secretId = secretId; + } + + /** + * 设置secretKey + * @param secretKey + */ + public void setSecretKey(String secretKey) { + this.secretKey = secretKey; + } + + /** + * 设置token + * @param token + */ + public void setToken(String token) { + this.token = token; + } + + /** + * 获取secretId + * @return secretId + */ + public String getSecretId() { + return this.secretId; + } + + /** + * 获取secretKey + * @return secretKey + */ + public String getSecretKey() { + return this.secretKey; + } + + /** + * 获取token + * @return token + */ + public String getToken() { + return this.token; + } + +} diff --git a/xinelu-common/src/main/java/com/xinelu/common/utils/tencentcloudapi/common/JsonResponseErrModel.java b/xinelu-common/src/main/java/com/xinelu/common/utils/tencentcloudapi/common/JsonResponseErrModel.java new file mode 100644 index 0000000..f5d03fe --- /dev/null +++ b/xinelu-common/src/main/java/com/xinelu/common/utils/tencentcloudapi/common/JsonResponseErrModel.java @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2018 THL A29 Limited, a Tencent company. 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. + */ + +package com.xinelu.common.utils.tencentcloudapi.common; + +import com.google.gson.annotations.Expose; +import com.google.gson.annotations.SerializedName; + +/** + * api 报错response类,用以格式化接收失败的http response + * @author Administrator + * + */ +public class JsonResponseErrModel { + + @SerializedName("RequestId") + @Expose + public String requestId; + + @SerializedName("Error") + @Expose + public ErrorInfo error; + + class ErrorInfo { + @SerializedName("Code") + @Expose + public String code; + + @Expose + @SerializedName("Message") + public String message; + } +} + diff --git a/xinelu-common/src/main/java/com/xinelu/common/utils/tencentcloudapi/common/JsonResponseModel.java b/xinelu-common/src/main/java/com/xinelu/common/utils/tencentcloudapi/common/JsonResponseModel.java new file mode 100644 index 0000000..c8eea20 --- /dev/null +++ b/xinelu-common/src/main/java/com/xinelu/common/utils/tencentcloudapi/common/JsonResponseModel.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2018 THL A29 Limited, a Tencent company. 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. + */ + +package com.xinelu.common.utils.tencentcloudapi.common; + +import com.google.gson.annotations.Expose; +import com.google.gson.annotations.SerializedName; + +/** + * api response类,用以格式化接收http response + * @param 具体对应的response类 + */ +public class JsonResponseModel { + + @SerializedName("Response") + @Expose + public T response; +} diff --git a/xinelu-common/src/main/java/com/xinelu/common/utils/tencentcloudapi/common/Sign.java b/xinelu-common/src/main/java/com/xinelu/common/utils/tencentcloudapi/common/Sign.java new file mode 100644 index 0000000..a346700 --- /dev/null +++ b/xinelu-common/src/main/java/com/xinelu/common/utils/tencentcloudapi/common/Sign.java @@ -0,0 +1,127 @@ +/* + * Copyright (c) 2018 THL A29 Limited, a Tencent company. 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. + */ + +package com.xinelu.common.utils.tencentcloudapi.common; + +import com.xinelu.common.utils.tencentcloudapi.common.exception.TencentCloudSDKException; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import java.security.InvalidKeyException; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.TreeMap; +import javax.crypto.Mac; +import javax.crypto.spec.SecretKeySpec; +import javax.xml.bind.DatatypeConverter; + +; + +/** + * 签名工具类 + */ +public class Sign { + private final static Charset UTF8 = StandardCharsets.UTF_8; + + /** + * + * @param sigStr + * @param secretKey + * @param sigMethod + * @return string sign string + * @throws TencentCloudSDKException + */ + public static String sign(String secretKey, String sigStr, String sigMethod) throws TencentCloudSDKException + { + String sig = null; + try{ + Mac mac = Mac.getInstance(sigMethod); + byte[] hash; + SecretKeySpec secretKeySpec = new SecretKeySpec(secretKey.getBytes(UTF8), mac.getAlgorithm()); + + mac.init(secretKeySpec); + hash = mac.doFinal(sigStr.getBytes(UTF8)); + sig = DatatypeConverter.printBase64Binary(hash); + } catch (Exception e) { + throw new TencentCloudSDKException(e.getClass().getName() + "-" + e.getMessage()); + } + return sig; + } + + public static String makeSignPlainText(TreeMap requestParams, String reqMethod, String host, String path) { + + String retStr = ""; + retStr += reqMethod; + retStr += host; + retStr += path; + retStr += buildParamStr(requestParams, reqMethod); + return retStr; + } + + protected static String buildParamStr(TreeMap requestParams, String requestMethod) { + + String retStr = ""; + for(String key: requestParams.keySet()) { + String value = requestParams.get(key).toString(); + if (retStr.length() == 0) { + retStr += '?'; + } else { + retStr += '&'; + } + retStr += key.replace("_", ".") + '=' + value; + + } + return retStr; + } + + public static String sha256Hex(String s) throws TencentCloudSDKException { + MessageDigest md; + try { + md = MessageDigest.getInstance("SHA-256"); + } catch (NoSuchAlgorithmException e) { + throw new TencentCloudSDKException("SHA-256 is not supported." + e.getMessage()); + } + byte[] d = md.digest(s.getBytes(UTF8)); + return DatatypeConverter.printHexBinary(d).toLowerCase(); + } + + public static String sha256Hex(byte[] b) throws TencentCloudSDKException { + MessageDigest md; + try { + md = MessageDigest.getInstance("SHA-256"); + } catch (NoSuchAlgorithmException e) { + throw new TencentCloudSDKException("SHA-256 is not supported." + e.getMessage()); + } + byte[] d = md.digest(b); + return DatatypeConverter.printHexBinary(d).toLowerCase(); + } + + public static byte[] hmac256(byte[] key, String msg) throws TencentCloudSDKException { + Mac mac; + try { + mac = Mac.getInstance("HmacSHA256"); + } catch (NoSuchAlgorithmException e) { + throw new TencentCloudSDKException("HmacSHA256 is not supported." + e.getMessage()); + } + SecretKeySpec secretKeySpec = new SecretKeySpec(key, mac.getAlgorithm()); + try { + mac.init(secretKeySpec); + } catch (InvalidKeyException e) { + throw new TencentCloudSDKException(e.getClass().getName() + "-" + e.getMessage()); + } + return mac.doFinal(msg.getBytes(UTF8)); + } +} diff --git a/xinelu-common/src/main/java/com/xinelu/common/utils/tencentcloudapi/common/exception/TencentCloudSDKException.java b/xinelu-common/src/main/java/com/xinelu/common/utils/tencentcloudapi/common/exception/TencentCloudSDKException.java new file mode 100644 index 0000000..a87ccdd --- /dev/null +++ b/xinelu-common/src/main/java/com/xinelu/common/utils/tencentcloudapi/common/exception/TencentCloudSDKException.java @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2018 THL A29 Limited, a Tencent company. 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. + */ + +package com.xinelu.common.utils.tencentcloudapi.common.exception; + +/** + * 腾讯云api sdk异常类 + */ +public class TencentCloudSDKException extends Exception { + + + /** + * + */ + private static final long serialVersionUID = 1L; + + /** + * 请求Id,发起请求前的异常这个字段为空 + */ + private String requestId; + + /** + * Error code, When API returns a failure, it must have an error code. + */ + private String errorCode; + + /** + * @param message 异常信息 + */ + public TencentCloudSDKException(String message) { + this(message, ""); + } + + /** + * @param message 异常信息 + * @param requestId 请求id + */ + public TencentCloudSDKException(String message, String requestId) { + super(message); + this.requestId = requestId; + } + + public TencentCloudSDKException(String message, String requestId, String errorCode) { + super(message); + this.requestId = requestId; + this.errorCode = errorCode; + } + + /** + * 获取请求id + * @return requestId + */ + public String getRequestId() { + return requestId; + } + + /** + * Get error code + * @return A string represents error code + */ +// public String getErrorCode() { +// return errorCode; +// } + + /** + * 格式化输出异常信息 + * @return 异常信息 + */ + public String toString() { + return "[TencentCloudSDKException]" + "message:" + this.getMessage() + " requestId:" + this.getRequestId(); + } +} diff --git a/xinelu-common/src/main/java/com/xinelu/common/utils/tencentcloudapi/common/http/HttpConnection.java b/xinelu-common/src/main/java/com/xinelu/common/utils/tencentcloudapi/common/http/HttpConnection.java new file mode 100644 index 0000000..2ca6516 --- /dev/null +++ b/xinelu-common/src/main/java/com/xinelu/common/utils/tencentcloudapi/common/http/HttpConnection.java @@ -0,0 +1,132 @@ +/* + * Copyright (c) 2018 THL A29 Limited, a Tencent company. 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. + */ + +package com.xinelu.common.utils.tencentcloudapi.common.http; + +import com.squareup.okhttp.Headers; +import com.squareup.okhttp.MediaType; +import com.squareup.okhttp.OkHttpClient; +import com.squareup.okhttp.Request; +import com.squareup.okhttp.RequestBody; +import com.squareup.okhttp.Response; +import com.xinelu.common.utils.tencentcloudapi.common.exception.TencentCloudSDKException; +import java.io.IOException; +import java.util.concurrent.TimeUnit; + +/** + * http连接类 + */ +public class HttpConnection { + + private OkHttpClient client; + + public HttpConnection(Integer connTimeout, Integer readTimeout, Integer writeTimeout) { + this.client = new OkHttpClient(); + this.client.setConnectTimeout(connTimeout, TimeUnit.SECONDS); + this.client.setReadTimeout(readTimeout, TimeUnit.SECONDS); + this.client.setWriteTimeout(writeTimeout, TimeUnit.SECONDS); + + } + + + public Response doRequest(Request request) throws TencentCloudSDKException { + Response response = null; + try { + response = this.client.newCall(request).execute(); + } catch (IOException e) { + throw new TencentCloudSDKException(e.getClass().getName() + "-" + e.getMessage()); + } + return response; + } + + public Response getRequest(String url) throws TencentCloudSDKException { + Request request = null; + try { + request = new Request.Builder() + .url(url) + .get() + .build(); + } catch (IllegalArgumentException e) { + throw new TencentCloudSDKException(e.getClass().getName() + "-" + e.getMessage()); + } + + return this.doRequest(request); + } + + public Response getRequest(String url, Headers headers) throws TencentCloudSDKException { + Request request = null; + try { + request = new Request.Builder() + .url(url) + .headers(headers) + .get() + .build(); + } catch (IllegalArgumentException e) { + throw new TencentCloudSDKException(e.getClass().getName() + "-" + e.getMessage()); + } + + return this.doRequest(request); + } + + public Response postRequest(String url, String body) throws TencentCloudSDKException { + MediaType contentType = MediaType.parse("application/x-www-form-urlencoded"); + Request request = null; + try { + request = new Request.Builder() + .url(url) + .post(RequestBody.create(contentType, body)) + .build(); + } catch (IllegalArgumentException e) { + throw new TencentCloudSDKException(e.getClass().getName() + "-" + e.getMessage()); + } + + return this.doRequest(request); + } + + public Response postRequest(String url, String body, Headers headers) throws TencentCloudSDKException { + MediaType contentType = MediaType.parse(headers.get("Content-Type")); + Request request = null; + try { + request = new Request.Builder() + .url(url) + .post(RequestBody.create(contentType, body)) + .headers(headers) + .build(); + } catch (IllegalArgumentException e) { + throw new TencentCloudSDKException(e.getClass().getName() + "-" + e.getMessage()); + } + + return this.doRequest(request); + } + + public Response postRequest(String url, byte [] body, Headers headers) throws TencentCloudSDKException { + MediaType contentType = MediaType.parse(headers.get("Content-Type")); + Request request = null; + try { + request = new Request.Builder() + .url(url) + .post(RequestBody.create(contentType, body)) + .headers(headers) + .build(); + } catch (IllegalArgumentException e) { + throw new TencentCloudSDKException(e.getClass().getName() + "-" + e.getMessage()); + } + + return this.doRequest(request); + } +} + diff --git a/xinelu-common/src/main/java/com/xinelu/common/utils/tencentcloudapi/common/profile/ClientProfile.java b/xinelu-common/src/main/java/com/xinelu/common/utils/tencentcloudapi/common/profile/ClientProfile.java new file mode 100644 index 0000000..c1038cd --- /dev/null +++ b/xinelu-common/src/main/java/com/xinelu/common/utils/tencentcloudapi/common/profile/ClientProfile.java @@ -0,0 +1,127 @@ +/* + * Copyright (c) 2018 THL A29 Limited, a Tencent company. 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. + */ + +package com.xinelu.common.utils.tencentcloudapi.common.profile; + +/** + * client选项类 + * @author Administrator + * + */ +public class ClientProfile { + + /** + * HmacSHA1签名方法 + */ + public static final String SIGN_SHA1 = "HmacSHA1"; + + /** + * HmacSHA256签名方法 + */ + public static final String SIGN_SHA256 = "HmacSHA256"; + + /** + * Signature Version 3 + */ + public static final String SIGN_TC3_256 = "TC3-HMAC-SHA256"; + + /** + * http相关选项,请参考HttpProfile + */ + private HttpProfile httpProfile; + + /** + * 签名方法 + */ + private String signMethod; + + /** + * If payload is NOT involved in signing process, true means will ignore payload, + * default is false. + */ + private boolean unsignedPayload; + + /** + * @param signMethod 签名方法 + * @param httpProfile HttpProfile实例 + */ + public ClientProfile(String signMethod, HttpProfile httpProfile) { + if (signMethod == null || signMethod.isEmpty()) { + signMethod = SIGN_TC3_256; + } + this.signMethod = signMethod; + this.httpProfile = httpProfile; + this.unsignedPayload = false; + } + + public ClientProfile(String signMethod) { + this(signMethod, new HttpProfile()); + } + + public ClientProfile() { + this(ClientProfile.SIGN_TC3_256, new HttpProfile()); + } + + /** + * 设置签名方法 + * @param signMethod + */ + public void setSignMethod(String signMethod) { + this.signMethod = signMethod; + } + + /** + * 设置http选项 + * @param httpProfile 参考HttpProfile + */ + public void setHttpProfile(HttpProfile httpProfile) { + this.httpProfile = httpProfile; + } + + /** + * 获取签名方法 + * @return 签名方法 + */ + public String getSignMethod() { + return this.signMethod; + } + + /** + * 获取HttpProfile实例 + * @return HttpProfile实例 + */ + public HttpProfile getHttpProfile() { + return this.httpProfile; + } + + /** + * Set the flag of whether payload should be ignored. + * Only has effect when request method is POST. + * @param flag + */ + public void setUnsignedPayload(boolean flag) { + this.unsignedPayload = flag; + } + + /** + * Get the flag of whether payload is ignored. + * @return + */ + public boolean isUnsignedPayload() { + return this.unsignedPayload; + } +} diff --git a/xinelu-common/src/main/java/com/xinelu/common/utils/tencentcloudapi/common/profile/HttpProfile.java b/xinelu-common/src/main/java/com/xinelu/common/utils/tencentcloudapi/common/profile/HttpProfile.java new file mode 100644 index 0000000..e16ee58 --- /dev/null +++ b/xinelu-common/src/main/java/com/xinelu/common/utils/tencentcloudapi/common/profile/HttpProfile.java @@ -0,0 +1,185 @@ +/* + * Copyright (c) 2018 THL A29 Limited, a Tencent company. 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. + */ + +package com.xinelu.common.utils.tencentcloudapi.common.profile; + +/** + * http选项类 + */ +public class HttpProfile { + + /** + * https协议 + */ + public static final String REQ_HTTPS = "https://"; + + /** + * http协议 + */ + public static final String REQ_HTTP = "http://"; + + /** + * post请求 + */ + public static final String REQ_POST = "POST"; + + /** + * get请求 + */ + public static final String REQ_GET = "GET"; + + /** + * 时间单位,1分钟 60s + */ + public static final int TM_MINUTE = 60; + + /** + * 请求方法 + */ + private String reqMethod; + + /** + * 请求域名 + */ + private String endpoint; + + /** + * 请求协议 + */ + private String protocol; + + /** + * 读超时时间(秒) + */ + private int readTimeout; + + /** + * 写超时时间(秒) + */ + private int writeTimeout; + + /** + * 连接超时时间(秒) + */ + private int connTimeout; + + + public HttpProfile() { + this.reqMethod = HttpProfile.REQ_POST; + this.endpoint = null; + this.protocol = HttpProfile.REQ_HTTPS; + this.readTimeout = 0; + this.writeTimeout = 0; + this.connTimeout = HttpProfile.TM_MINUTE; + } + + /** + * 设置请求方法 + * @param reqMethod 请求方法 + */ + public void setReqMethod(String reqMethod) { + this.reqMethod = reqMethod; + } + + /** + * 设置请求域名 + * @param endpoint 域名(xx.[region.]tencentcloudapi.com) + */ + public void setEndpoint(String endpoint) { + this.endpoint = endpoint; + } + + /** + * 设置读超时时间 + * @param readTimeout 读超时时间 + */ + public void setReadTimeout(int readTimeout) { + this.readTimeout = readTimeout; + } + + /** + * 设置写超时时间 + * @param writeTimeout 写超时时间 + */ + public void setWriteTimeout(int writeTimeout) { + this.writeTimeout = writeTimeout; + } + + /** + * 设置连接超时时间 + * @param connTimeout 连接超时时间 + */ + public void setConnTimeout(int connTimeout) { + this.connTimeout = connTimeout; + } + + /** + * 设置请求协议 + * @param protocol 请求协议(https:// http://) + */ + public void setProtocol(String protocol) { + this.protocol = protocol; + } + + /** + * 获取请求方法 + * @return reqMethod + */ + public String getReqMethod() { + return this.reqMethod; + } + + /** + * 获取请求域名 + * @return endpoint + */ + public String getEndpoint() { + return this.endpoint; + } + + /** + * 获取读超时时间 + * @return readTimeout + */ + public int getReadTimeout() { + return this.readTimeout; + } + + /** + * 获取写超时时间 + * @return writeTimeout + */ + public int getWriteTimeout() { + return this.writeTimeout; + } + + /** + * 获取连接超时时间 + * @return connTimeout + */ + public int getConnTimeout() { + return this.connTimeout; + } + + /** + * 获取请求协议 + * @return protocol + */ + public String getProtocol() { + return this.protocol; + } +} diff --git a/xinelu-common/src/main/java/com/xinelu/common/utils/tencentcloudapi/trtc/v20190722/Base64URL.java b/xinelu-common/src/main/java/com/xinelu/common/utils/tencentcloudapi/trtc/v20190722/Base64URL.java new file mode 100644 index 0000000..abb9433 --- /dev/null +++ b/xinelu-common/src/main/java/com/xinelu/common/utils/tencentcloudapi/trtc/v20190722/Base64URL.java @@ -0,0 +1,24 @@ +package com.xinelu.common.utils.tencentcloudapi.trtc.v20190722; + +import java.util.Base64; + +public class Base64URL { + public static byte[] base64EncodeUrl(byte[] input) { + byte[] base64 = Base64.getEncoder().encode(input); + for (int i = 0; i < base64.length; ++i) + switch (base64[i]) { + case '+': + base64[i] = '*'; + break; + case '/': + base64[i] = '-'; + break; + case '=': + base64[i] = '_'; + break; + default: + break; + } + return base64; + } +} diff --git a/xinelu-common/src/main/java/com/xinelu/common/utils/tencentcloudapi/trtc/v20190722/TLSSigAPIv2.java b/xinelu-common/src/main/java/com/xinelu/common/utils/tencentcloudapi/trtc/v20190722/TLSSigAPIv2.java new file mode 100644 index 0000000..6a05466 --- /dev/null +++ b/xinelu-common/src/main/java/com/xinelu/common/utils/tencentcloudapi/trtc/v20190722/TLSSigAPIv2.java @@ -0,0 +1,312 @@ +package com.xinelu.common.utils.tencentcloudapi.trtc.v20190722; + +import com.alibaba.fastjson2.JSONObject; +import java.nio.charset.StandardCharsets; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.util.Arrays; +import java.util.Base64; +import java.util.zip.Deflater; +import javax.crypto.Mac; +import javax.crypto.spec.SecretKeySpec; + +public class TLSSigAPIv2 { + final private long sdkappid; + final private String key; + + public TLSSigAPIv2(long sdkappid, String key) { + this.sdkappid = sdkappid; + this.key = key; + } + + /** + * 【功能说明】用于签发 TRTC 和 IM 服务中必须要使用的 UserSig 鉴权票据 + *

+ * 【参数说明】 + * + * @param userid - 用户id,限制长度为32字节,只允许包含大小写英文字母(a-zA-Z)、数字(0-9)及下划线和连词符。 + * @param expire - UserSig 票据的过期时间,单位是秒,比如 86400 代表生成的 UserSig 票据在一天后就无法再使用了。 + * @return usersig -生成的签名 + */ + + /** + * Function: Used to issue UserSig that is required by the TRTC and IM services. + *

+ * Parameter description: + * + * @param userid - User ID. The value can be up to 32 bytes in length and contain letters (a-z and A-Z), digits (0-9), underscores (_), and hyphens (-). + * @param expire - UserSig expiration time, in seconds. For example, 86400 indicates that the generated UserSig will expire one day after being generated. + * @return usersig - Generated signature. + */ + public String genUserSig(String userid, long expire) { + return genUserSig(userid, expire, null); + } + + /** + * 【功能说明】 + * 用于签发 TRTC 进房参数中可选的 PrivateMapKey 权限票据。 + * PrivateMapKey 需要跟 UserSig 一起使用,但 PrivateMapKey 比 UserSig 有更强的权限控制能力: + * - UserSig 只能控制某个 UserID 有无使用 TRTC 服务的权限,只要 UserSig 正确,其对应的 UserID 可以进出任意房间。 + * - PrivateMapKey 则是将 UserID 的权限控制的更加严格,包括能不能进入某个房间,能不能在该房间里上行音视频等等。 + * 如果要开启 PrivateMapKey 严格权限位校验,需要在【实时音视频控制台】/【应用管理】/【应用信息】中打开“启动权限密钥”开关。 + *

+ * 【参数说明】 + * + * @param userid - 用户id,限制长度为32字节,只允许包含大小写英文字母(a-zA-Z)、数字(0-9)及下划线和连词符。 + * @param expire - PrivateMapKey 票据的过期时间,单位是秒,比如 86400 生成的 PrivateMapKey 票据在一天后就无法再使用了。 + * @param roomid - 房间号,用于指定该 userid 可以进入的房间号 + * @param privilegeMap - 权限位,使用了一个字节中的 8 个比特位,分别代表八个具体的功能权限开关: + * - 第 1 位:0000 0001 = 1,创建房间的权限 + * - 第 2 位:0000 0010 = 2,加入房间的权限 + * - 第 3 位:0000 0100 = 4,发送语音的权限 + * - 第 4 位:0000 1000 = 8,接收语音的权限 + * - 第 5 位:0001 0000 = 16,发送视频的权限 + * - 第 6 位:0010 0000 = 32,接收视频的权限 + * - 第 7 位:0100 0000 = 64,发送辅路(也就是屏幕分享)视频的权限 + * - 第 8 位:1000 0000 = 200,接收辅路(也就是屏幕分享)视频的权限 + * - privilegeMap == 1111 1111 == 255 代表该 userid 在该 roomid 房间内的所有功能权限。 + * - privilegeMap == 0010 1010 == 42 代表该 userid 拥有加入房间和接收音视频数据的权限,但不具备其他权限。 + * @return usersig - 生成带userbuf的签名 + */ + + /** + * Function: + * Used to issue PrivateMapKey that is optional for room entry. + * PrivateMapKey must be used together with UserSig but with more powerful permission control capabilities. + * - UserSig can only control whether a UserID has permission to use the TRTC service. As long as the UserSig is correct, the user with the corresponding UserID can enter or leave any room. + * - PrivateMapKey specifies more stringent permissions for a UserID, including whether the UserID can be used to enter a specific room and perform audio/video upstreaming in the room. + * To enable stringent PrivateMapKey permission bit verification, you need to enable permission key in TRTC console > Application Management > Application Info. + * + * Parameter description: + * + * @param userid - User ID. The value can be up to 32 bytes in length and contain letters (a-z and A-Z), digits (0-9), underscores (_), and hyphens (-). + * @param roomid - ID of the room to which the specified UserID can enter. + * @param expire - PrivateMapKey expiration time, in seconds. For example, 86400 indicates that the generated PrivateMapKey will expire one day after being generated. + * @param privilegeMap - Permission bits. Eight bits in the same byte are used as the permission switches of eight specific features: + * - Bit 1: 0000 0001 = 1, permission for room creation + * - Bit 2: 0000 0010 = 2, permission for room entry + * - Bit 3: 0000 0100 = 4, permission for audio sending + * - Bit 4: 0000 1000 = 8, permission for audio receiving + * - Bit 5: 0001 0000 = 16, permission for video sending + * - Bit 6: 0010 0000 = 32, permission for video receiving + * - Bit 7: 0100 0000 = 64, permission for substream video sending (screen sharing) + * - Bit 8: 1000 0000 = 200, permission for substream video receiving (screen sharing) + * - privilegeMap == 1111 1111 == 255: Indicates that the UserID has all feature permissions of the room specified by roomid. + * - privilegeMap == 0010 1010 == 42: Indicates that the UserID has only the permissions to enter the room and receive audio/video data. + * @return usersig - Generate signature with userbuf + */ + public String genPrivateMapKey(String userid, long expire, long roomid, long privilegeMap) { + byte[] userbuf = genUserBuf(userid, roomid, expire, privilegeMap, 0, ""); //生成userbuf + return genUserSig(userid, expire, userbuf); + } + + /** + * 【功能说明】 + * 用于签发 TRTC 进房参数中可选的 PrivateMapKey 权限票据。 + * PrivateMapKey 需要跟 UserSig 一起使用,但 PrivateMapKey 比 UserSig 有更强的权限控制能力: + * - UserSig 只能控制某个 UserID 有无使用 TRTC 服务的权限,只要 UserSig 正确,其对应的 UserID 可以进出任意房间。 + * - PrivateMapKey 则是将 UserID 的权限控制的更加严格,包括能不能进入某个房间,能不能在该房间里上行音视频等等。 + * 如果要开启 PrivateMapKey 严格权限位校验,需要在【实时音视频控制台】/【应用管理】/【应用信息】中打开“启动权限密钥”开关。 + *

+ * 【参数说明】 + * + * @param userid - 用户id,限制长度为32字节,只允许包含大小写英文字母(a-zA-Z)、数字(0-9)及下划线和连词符。 + * @param expire - PrivateMapKey 票据的过期时间,单位是秒,比如 86400 生成的 PrivateMapKey 票据在一天后就无法再使用了。 + * @param roomstr - 字符串房间号,用于指定该 userid 可以进入的房间号 + * @param privilegeMap - 权限位,使用了一个字节中的 8 个比特位,分别代表八个具体的功能权限开关: + * - 第 1 位:0000 0001 = 1,创建房间的权限 + * - 第 2 位:0000 0010 = 2,加入房间的权限 + * - 第 3 位:0000 0100 = 4,发送语音的权限 + * - 第 4 位:0000 1000 = 8,接收语音的权限 + * - 第 5 位:0001 0000 = 16,发送视频的权限 + * - 第 6 位:0010 0000 = 32,接收视频的权限 + * - 第 7 位:0100 0000 = 64,发送辅路(也就是屏幕分享)视频的权限 + * - 第 8 位:1000 0000 = 200,接收辅路(也就是屏幕分享)视频的权限 + * - privilegeMap == 1111 1111 == 255 代表该 userid 在该 roomid 房间内的所有功能权限。 + * - privilegeMap == 0010 1010 == 42 代表该 userid 拥有加入房间和接收音视频数据的权限,但不具备其他权限。 + * @return usersig - 生成带userbuf的签名 + */ + + /** + * Function: + * Used to issue PrivateMapKey that is optional for room entry. + * PrivateMapKey must be used together with UserSig but with more powerful permission control capabilities. + * - UserSig can only control whether a UserID has permission to use the TRTC service. As long as the UserSig is correct, the user with the corresponding UserID can enter or leave any room. + * - PrivateMapKey specifies more stringent permissions for a UserID, including whether the UserID can be used to enter a specific room and perform audio/video upstreaming in the room. + * To enable stringent PrivateMapKey permission bit verification, you need to enable permission key in TRTC console > Application Management > Application Info. + * + * Parameter description: + * + * + * @param userid - User ID. The value can be up to 32 bytes in length and contain letters (a-z and A-Z), digits (0-9), underscores (_), and hyphens (-). + * @param roomid - ID of the room to which the specified UserID can enter. + * @param expire - PrivateMapKey expiration time, in seconds. For example, 86400 indicates that the generated PrivateMapKey will expire one day after being generated. + * @param privilegeMap - Permission bits. Eight bits in the same byte are used as the permission switches of eight specific features: + * - Bit 1: 0000 0001 = 1, permission for room creation + * - Bit 2: 0000 0010 = 2, permission for room entry + * - Bit 3: 0000 0100 = 4, permission for audio sending + * - Bit 4: 0000 1000 = 8, permission for audio receiving + * - Bit 5: 0001 0000 = 16, permission for video sending + * - Bit 6: 0010 0000 = 32, permission for video receiving + * - Bit 7: 0100 0000 = 64, permission for substream video sending (screen sharing) + * - Bit 8: 1000 0000 = 200, permission for substream video receiving (screen sharing) + * - privilegeMap == 1111 1111 == 255: Indicates that the UserID has all feature permissions of the room specified by roomid. + * - privilegeMap == 0010 1010 == 42: Indicates that the UserID has only the permissions to enter the room and receive audio/video data. + * @return usersig - Generate signature with userbuf + */ + public String genPrivateMapKeyWithStringRoomID(String userid, long expire, String roomstr, long privilegeMap) { + byte[] userbuf = genUserBuf(userid, 0, expire, privilegeMap, 0, roomstr); //生成userbuf + return genUserSig(userid, expire, userbuf); + } + + private String hmacsha256(String identifier, long currTime, long expire, String base64Userbuf) { + String contentToBeSigned = "TLS.identifier:" + identifier + "\n" + + "TLS.sdkappid:" + sdkappid + "\n" + + "TLS.time:" + currTime + "\n" + + "TLS.expire:" + expire + "\n"; + if (null != base64Userbuf) { + contentToBeSigned += "TLS.userbuf:" + base64Userbuf + "\n"; + } + try { + byte[] byteKey = key.getBytes(StandardCharsets.UTF_8); + Mac hmac = Mac.getInstance("HmacSHA256"); + SecretKeySpec keySpec = new SecretKeySpec(byteKey, "HmacSHA256"); + hmac.init(keySpec); + byte[] byteSig = hmac.doFinal(contentToBeSigned.getBytes(StandardCharsets.UTF_8)); + return (Base64.getEncoder().encodeToString(byteSig)).replaceAll("\\s*", ""); + } catch (NoSuchAlgorithmException | InvalidKeyException e) { + return ""; + } + } + + private String genUserSig(String userid, long expire, byte[] userbuf) { + + long currTime = System.currentTimeMillis() / 1000; + + JSONObject sigDoc = new JSONObject(); + sigDoc.put("TLS.ver", "2.0"); + sigDoc.put("TLS.identifier", userid); + sigDoc.put("TLS.sdkappid", sdkappid); + sigDoc.put("TLS.expire", expire); + sigDoc.put("TLS.time", currTime); + + String base64UserBuf = null; + if (null != userbuf) { + base64UserBuf = Base64.getEncoder().encodeToString(userbuf).replaceAll("\\s*", ""); + sigDoc.put("TLS.userbuf", base64UserBuf); + } + String sig = hmacsha256(userid, currTime, expire, base64UserBuf); + if (sig.length() == 0) { + return ""; + } + sigDoc.put("TLS.sig", sig); + Deflater compressor = new Deflater(); + compressor.setInput(sigDoc.toString().getBytes(StandardCharsets.UTF_8)); + compressor.finish(); + byte[] compressedBytes = new byte[2048]; + int compressedBytesLength = compressor.deflate(compressedBytes); + compressor.end(); + return (new String(Base64URL.base64EncodeUrl(Arrays.copyOfRange(compressedBytes, + 0, compressedBytesLength)))).replaceAll("\\s*", ""); + } + + public byte[] genUserBuf(String account, long dwAuthID, long dwExpTime, + long dwPrivilegeMap, long dwAccountType, String RoomStr) { + //视频校验位需要用到的字段,按照网络字节序放入buf中 + /* + cVer unsigned char/1 版本号,填0 + wAccountLen unsigned short /2 第三方自己的帐号长度 + account wAccountLen 第三方自己的帐号字符 + dwSdkAppid unsigned int/4 sdkappid + dwAuthID unsigned int/4 群组号码 + dwExpTime unsigned int/4 过期时间 ,直接使用填入的值 + dwPrivilegeMap unsigned int/4 权限位,主播0xff,观众0xab + dwAccountType unsigned int/4 第三方帐号类型 + */ + + //The fields required for the video check digit are placed in buf according to the network byte order. + /* + cVer unsigned char/1 Version number, fill in 0 + wAccountLen unsigned short /2 Third party's own account length + account wAccountLen Third party's own account characters + dwSdkAppid unsigned int/4 sdkappid + dwAuthID unsigned int/4 group number + dwExpTime unsigned int/4 Expiration time , use the filled value directly + dwPrivilegeMap unsigned int/4 Permission bits, host 0xff, audience 0xab + dwAccountType unsigned int/4 Third-party account type + */ + int accountLength = account.length(); + int roomStrLength = RoomStr.length(); + int offset = 0; + int bufLength = 1 + 2 + accountLength + 20 ; + if (roomStrLength > 0) { + bufLength = bufLength + 2 + roomStrLength; + } + byte[] userbuf = new byte[bufLength]; + + //cVer + if (roomStrLength > 0) { + userbuf[offset++] = 1; + } else { + userbuf[offset++] = 0; + } + + //wAccountLen + userbuf[offset++] = (byte) ((accountLength & 0xFF00) >> 8); + userbuf[offset++] = (byte) (accountLength & 0x00FF); + + //account + for (; offset < 3 + accountLength; ++offset) { + userbuf[offset] = (byte) account.charAt(offset - 3); + } + + //dwSdkAppid + userbuf[offset++] = (byte) ((sdkappid & 0xFF000000) >> 24); + userbuf[offset++] = (byte) ((sdkappid & 0x00FF0000) >> 16); + userbuf[offset++] = (byte) ((sdkappid & 0x0000FF00) >> 8); + userbuf[offset++] = (byte) (sdkappid & 0x000000FF); + + //dwAuthId,房间号 + //dwAuthId, room number + userbuf[offset++] = (byte) ((dwAuthID & 0xFF000000) >> 24); + userbuf[offset++] = (byte) ((dwAuthID & 0x00FF0000) >> 16); + userbuf[offset++] = (byte) ((dwAuthID & 0x0000FF00) >> 8); + userbuf[offset++] = (byte) (dwAuthID & 0x000000FF); + + //expire,过期时间,当前时间 + 有效期(单位:秒) + //expire,Expiration time, current time + validity period (unit: seconds) + long currTime = System.currentTimeMillis() / 1000; + long expire = currTime + dwExpTime; + userbuf[offset++] = (byte) ((expire & 0xFF000000) >> 24); + userbuf[offset++] = (byte) ((expire & 0x00FF0000) >> 16); + userbuf[offset++] = (byte) ((expire & 0x0000FF00) >> 8); + userbuf[offset++] = (byte) (expire & 0x000000FF); + + //dwPrivilegeMap,权限位 + //dwPrivilegeMap,Permission bits + userbuf[offset++] = (byte) ((dwPrivilegeMap & 0xFF000000) >> 24); + userbuf[offset++] = (byte) ((dwPrivilegeMap & 0x00FF0000) >> 16); + userbuf[offset++] = (byte) ((dwPrivilegeMap & 0x0000FF00) >> 8); + userbuf[offset++] = (byte) (dwPrivilegeMap & 0x000000FF); + + //dwAccountType,账户类型 + //dwAccountType,account type + userbuf[offset++] = (byte) ((dwAccountType & 0xFF000000) >> 24); + userbuf[offset++] = (byte) ((dwAccountType & 0x00FF0000) >> 16); + userbuf[offset++] = (byte) ((dwAccountType & 0x0000FF00) >> 8); + userbuf[offset++] = (byte) (dwAccountType & 0x000000FF); + + + if (roomStrLength > 0) { + //roomStrLen + userbuf[offset++] = (byte) ((roomStrLength & 0xFF00) >> 8); + userbuf[offset++] = (byte) (roomStrLength & 0x00FF); + + //roomStr + for (; offset < bufLength; ++offset) { + userbuf[offset] = (byte) RoomStr.charAt(offset - (bufLength - roomStrLength)); + } + } + return userbuf; + } +} diff --git a/xinelu-common/src/main/java/com/xinelu/common/utils/tencentcloudapi/trtc/v20190722/TrtcClient.java b/xinelu-common/src/main/java/com/xinelu/common/utils/tencentcloudapi/trtc/v20190722/TrtcClient.java new file mode 100644 index 0000000..1e0ba8b --- /dev/null +++ b/xinelu-common/src/main/java/com/xinelu/common/utils/tencentcloudapi/trtc/v20190722/TrtcClient.java @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2017-2018 THL A29 Limited, a Tencent company. 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. + */ +package com.xinelu.common.utils.tencentcloudapi.trtc.v20190722; + +import com.google.gson.JsonSyntaxException; +import com.google.gson.reflect.TypeToken; +import com.xinelu.common.utils.tencentcloudapi.common.AbstractClient; +import com.xinelu.common.utils.tencentcloudapi.common.Credential; +import com.xinelu.common.utils.tencentcloudapi.common.JsonResponseModel; +import com.xinelu.common.utils.tencentcloudapi.common.exception.TencentCloudSDKException; +import com.xinelu.common.utils.tencentcloudapi.common.profile.ClientProfile; +import com.xinelu.common.utils.tencentcloudapi.trtc.v20190722.models.DissolveRoomRequest; +import com.xinelu.common.utils.tencentcloudapi.trtc.v20190722.models.DissolveRoomResponse; +import com.xinelu.common.utils.tencentcloudapi.trtc.v20190722.models.KickOutUserRequest; +import com.xinelu.common.utils.tencentcloudapi.trtc.v20190722.models.KickOutUserResponse; +import java.lang.reflect.Type; + +public class TrtcClient extends AbstractClient { + private static String endpoint = "trtc.tencentcloudapi.com"; + private static String version = "2019-07-22"; + + /** + * 构造client + * @param credential 认证信息实例 + * @param region 产品地域 + */ + public TrtcClient(Credential credential, String region) { + this(credential, region, new ClientProfile()); + } + + /** + * 构造client + * @param credential 认证信息实例 + * @param region 产品地域 + * @param profile 配置实例 + */ + public TrtcClient(Credential credential, String region, ClientProfile profile) { + super(TrtcClient.endpoint, TrtcClient.version, credential, region, profile); + } + + /** + *接口说明:把房间所有用户从房间踢出,解散房间。支持 TRTC SDK 6.6及以上版本,包括Android、iOS、Windows 和 macOS。 + * @param req DissolveRoomRequest + * @return DissolveRoomResponse + * @throws TencentCloudSDKException + */ + public DissolveRoomResponse DissolveRoom(DissolveRoomRequest req) throws TencentCloudSDKException { + JsonResponseModel rsp = null; + try { + Type type = new TypeToken>() { + }.getType(); + rsp = gson.fromJson(this.internalRequest(req, "DissolveRoom"), type); + } catch (JsonSyntaxException e) { + throw new TencentCloudSDKException(e.getMessage()); + } + return rsp.response; + } + + /** + *接口说明:将用户从房间踢出。支持 TRTC SDK 6.6及以上版本,包括Android、iOS、Windows 和 macOS。 + * @param req KickOutUserRequest + * @return KickOutUserResponse + * @throws TencentCloudSDKException + */ + public KickOutUserResponse KickOutUser(KickOutUserRequest req) throws TencentCloudSDKException{ + JsonResponseModel rsp = null; + try { + Type type = new TypeToken>() { + }.getType(); + rsp = gson.fromJson(this.internalRequest(req, "KickOutUser"), type); + } catch (JsonSyntaxException e) { + throw new TencentCloudSDKException(e.getMessage()); + } + return rsp.response; + } + +} diff --git a/xinelu-common/src/main/java/com/xinelu/common/utils/tencentcloudapi/trtc/v20190722/models/DissolveRoomRequest.java b/xinelu-common/src/main/java/com/xinelu/common/utils/tencentcloudapi/trtc/v20190722/models/DissolveRoomRequest.java new file mode 100644 index 0000000..5cd5139 --- /dev/null +++ b/xinelu-common/src/main/java/com/xinelu/common/utils/tencentcloudapi/trtc/v20190722/models/DissolveRoomRequest.java @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2017-2018 THL A29 Limited, a Tencent company. 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. + */ +package com.xinelu.common.utils.tencentcloudapi.trtc.v20190722.models; + +import com.google.gson.annotations.Expose; +import com.google.gson.annotations.SerializedName; +import com.xinelu.common.utils.tencentcloudapi.common.AbstractModel; +import java.util.HashMap; + +public class DissolveRoomRequest extends AbstractModel { + + /** + * TRTC的SDKAppId。 + */ + @SerializedName("SdkAppId") + @Expose + private Long SdkAppId; + + /** + * 房间号。 + */ + @SerializedName("RoomId") + @Expose + private Long RoomId; + + /** + * 获取TRTC的SDKAppId。 + * @return SdkAppId TRTC的SDKAppId。 + */ + public Long getSdkAppId() { + return this.SdkAppId; + } + + /** + * 设置TRTC的SDKAppId。 + * @param SdkAppId TRTC的SDKAppId。 + */ + public void setSdkAppId(Long SdkAppId) { + this.SdkAppId = SdkAppId; + } + + /** + * 获取房间号。 + * @return RoomId 房间号。 + */ + public Long getRoomId() { + return this.RoomId; + } + + /** + * 设置房间号。 + * @param RoomId 房间号。 + */ + public void setRoomId(Long RoomId) { + this.RoomId = RoomId; + } + + /** + * 内部实现,用户禁止调用 + */ + public void toMap(HashMap map, String prefix) { + this.setParamSimple(map, prefix + "SdkAppId", this.SdkAppId); + this.setParamSimple(map, prefix + "RoomId", this.RoomId); + + } +} + diff --git a/xinelu-common/src/main/java/com/xinelu/common/utils/tencentcloudapi/trtc/v20190722/models/DissolveRoomResponse.java b/xinelu-common/src/main/java/com/xinelu/common/utils/tencentcloudapi/trtc/v20190722/models/DissolveRoomResponse.java new file mode 100644 index 0000000..041b16d --- /dev/null +++ b/xinelu-common/src/main/java/com/xinelu/common/utils/tencentcloudapi/trtc/v20190722/models/DissolveRoomResponse.java @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2017-2018 THL A29 Limited, a Tencent company. 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. + */ +package com.xinelu.common.utils.tencentcloudapi.trtc.v20190722.models; + +import com.google.gson.annotations.Expose; +import com.google.gson.annotations.SerializedName; +import com.xinelu.common.utils.tencentcloudapi.common.AbstractModel; +import java.util.HashMap; + +public class DissolveRoomResponse extends AbstractModel { + + /** + * 唯一请求 ID,每次请求都会返回。定位问题时需要提供该次请求的 RequestId。 + */ + @SerializedName("RequestId") + @Expose + private String RequestId; + + /** + * 获取唯一请求 ID,每次请求都会返回。定位问题时需要提供该次请求的 RequestId。 + * @return RequestId 唯一请求 ID,每次请求都会返回。定位问题时需要提供该次请求的 RequestId。 + */ + public String getRequestId() { + return this.RequestId; + } + + /** + * 设置唯一请求 ID,每次请求都会返回。定位问题时需要提供该次请求的 RequestId。 + * @param RequestId 唯一请求 ID,每次请求都会返回。定位问题时需要提供该次请求的 RequestId。 + */ + public void setRequestId(String RequestId) { + this.RequestId = RequestId; + } + + /** + * 内部实现,用户禁止调用 + */ + public void toMap(HashMap map, String prefix) { + this.setParamSimple(map, prefix + "RequestId", this.RequestId); + + } +} + diff --git a/xinelu-common/src/main/java/com/xinelu/common/utils/tencentcloudapi/trtc/v20190722/models/KickOutUserRequest.java b/xinelu-common/src/main/java/com/xinelu/common/utils/tencentcloudapi/trtc/v20190722/models/KickOutUserRequest.java new file mode 100644 index 0000000..6323444 --- /dev/null +++ b/xinelu-common/src/main/java/com/xinelu/common/utils/tencentcloudapi/trtc/v20190722/models/KickOutUserRequest.java @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2017-2018 THL A29 Limited, a Tencent company. 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. + */ +package com.xinelu.common.utils.tencentcloudapi.trtc.v20190722.models; + +import com.google.gson.annotations.Expose; +import com.google.gson.annotations.SerializedName; +import com.xinelu.common.utils.tencentcloudapi.common.AbstractModel; +import java.util.HashMap; + +public class KickOutUserRequest extends AbstractModel { + + /** + * TRTC的SDKAppId。 + */ + @SerializedName("SdkAppId") + @Expose + private Long SdkAppId; + + /** + * 房间号。 + */ + @SerializedName("RoomId") + @Expose + private Long RoomId; + + /** + * 要踢的用户列表,最多10个。 + */ + @SerializedName("UserIds") + @Expose + private String [] UserIds; + + /** + * 获取TRTC的SDKAppId。 + * @return SdkAppId TRTC的SDKAppId。 + */ + public Long getSdkAppId() { + return this.SdkAppId; + } + + /** + * 设置TRTC的SDKAppId。 + * @param SdkAppId TRTC的SDKAppId。 + */ + public void setSdkAppId(Long SdkAppId) { + this.SdkAppId = SdkAppId; + } + + /** + * 获取房间号。 + * @return RoomId 房间号。 + */ + public Long getRoomId() { + return this.RoomId; + } + + /** + * 设置房间号。 + * @param RoomId 房间号。 + */ + public void setRoomId(Long RoomId) { + this.RoomId = RoomId; + } + + /** + * 获取要踢的用户列表,最多10个。 + * @return UserIds 要踢的用户列表,最多10个。 + */ + public String [] getUserIds() { + return this.UserIds; + } + + /** + * 设置要踢的用户列表,最多10个。 + * @param UserIds 要踢的用户列表,最多10个。 + */ + public void setUserIds(String [] UserIds) { + this.UserIds = UserIds; + } + + /** + * 内部实现,用户禁止调用 + */ + public void toMap(HashMap map, String prefix) { + this.setParamSimple(map, prefix + "SdkAppId", this.SdkAppId); + this.setParamSimple(map, prefix + "RoomId", this.RoomId); + this.setParamArraySimple(map, prefix + "UserIds.", this.UserIds); + + } +} + diff --git a/xinelu-common/src/main/java/com/xinelu/common/utils/tencentcloudapi/trtc/v20190722/models/KickOutUserResponse.java b/xinelu-common/src/main/java/com/xinelu/common/utils/tencentcloudapi/trtc/v20190722/models/KickOutUserResponse.java new file mode 100644 index 0000000..7aa59dc --- /dev/null +++ b/xinelu-common/src/main/java/com/xinelu/common/utils/tencentcloudapi/trtc/v20190722/models/KickOutUserResponse.java @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2017-2018 THL A29 Limited, a Tencent company. 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. + */ +package com.xinelu.common.utils.tencentcloudapi.trtc.v20190722.models; + +import com.google.gson.annotations.Expose; +import com.google.gson.annotations.SerializedName; +import com.xinelu.common.utils.tencentcloudapi.common.AbstractModel; +import java.util.HashMap; + +public class KickOutUserResponse extends AbstractModel { + + /** + * 唯一请求 ID,每次请求都会返回。定位问题时需要提供该次请求的 RequestId。 + */ + @SerializedName("RequestId") + @Expose + private String RequestId; + + /** + * 获取唯一请求 ID,每次请求都会返回。定位问题时需要提供该次请求的 RequestId。 + * @return RequestId 唯一请求 ID,每次请求都会返回。定位问题时需要提供该次请求的 RequestId。 + */ + public String getRequestId() { + return this.RequestId; + } + + /** + * 设置唯一请求 ID,每次请求都会返回。定位问题时需要提供该次请求的 RequestId。 + * @param RequestId 唯一请求 ID,每次请求都会返回。定位问题时需要提供该次请求的 RequestId。 + */ + public void setRequestId(String RequestId) { + this.RequestId = RequestId; + } + + /** + * 内部实现,用户禁止调用 + */ + public void toMap(HashMap map, String prefix) { + this.setParamSimple(map, prefix + "RequestId", this.RequestId); + + } +} + diff --git a/xinelu-framework/src/main/java/com/xinelu/framework/config/SecurityConfig.java b/xinelu-framework/src/main/java/com/xinelu/framework/config/SecurityConfig.java index a0b9738..daea435 100644 --- a/xinelu-framework/src/main/java/com/xinelu/framework/config/SecurityConfig.java +++ b/xinelu-framework/src/main/java/com/xinelu/framework/config/SecurityConfig.java @@ -111,7 +111,7 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter { .antMatchers("/login", "/register", "/captchaImage").anonymous() // 静态资源,可匿名访问 .antMatchers(HttpMethod.GET, "/", "/*.html", "/**/*.html", "/**/*.css", "/**/*.js", "/profile/**").permitAll() - .antMatchers("/swagger-ui.html", "/swagger-resources/**", "/webjars/**", "/*/api-docs", "/druid/**", "/nurseApplet/**", "/nurseApp/**").permitAll() + .antMatchers("/swagger-ui.html", "/swagger-resources/**", "/webjars/**", "/*/api-docs", "/druid/**", "/nurseApplet/**", "/nurseApp/**", "/webSocket/**").permitAll() // 除上面外的所有请求全部需要鉴权认证 .anyRequest().authenticated() .and() diff --git a/xinelu-nurse-applet/src/main/java/com/xinelu/applet/controller/appletscreeningproject/AppletScreeningProjectController.java b/xinelu-nurse-applet/src/main/java/com/xinelu/applet/controller/appletscreeningproject/AppletScreeningProjectController.java new file mode 100644 index 0000000..93cd3ea --- /dev/null +++ b/xinelu-nurse-applet/src/main/java/com/xinelu/applet/controller/appletscreeningproject/AppletScreeningProjectController.java @@ -0,0 +1,33 @@ +package com.xinelu.applet.controller.appletscreeningproject; + +import com.xinelu.common.core.controller.BaseController; +import com.xinelu.common.core.domain.R; +import com.xinelu.manage.domain.screeningproject.ScreeningProject; +import com.xinelu.manage.service.screeningproject.IScreeningProjectService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import java.util.List; +import javax.annotation.Resource; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * @description: 项目控制器——小程序 + * @author: haown + * @create: 2023-03-07 14:23 + **/ +@RestController +@RequestMapping("/nurseApplet/screening/project") +@Api(tags = "项目控制器-小程序") +public class AppletScreeningProjectController extends BaseController { + @Resource + private IScreeningProjectService projectService; + + @ApiOperation("筛查项目列表") + @GetMapping("list") + public R> list(ScreeningProject project) { + List list = projectService.findList(project); + return R.ok(list); + } +} diff --git a/xinelu-nurse-applet/src/main/java/com/xinelu/applet/controller/appletscreeningrecord/AppletScreeningRecordController.java b/xinelu-nurse-applet/src/main/java/com/xinelu/applet/controller/appletscreeningrecord/AppletScreeningRecordController.java new file mode 100644 index 0000000..33a1ff2 --- /dev/null +++ b/xinelu-nurse-applet/src/main/java/com/xinelu/applet/controller/appletscreeningrecord/AppletScreeningRecordController.java @@ -0,0 +1,131 @@ +package com.xinelu.applet.controller.appletscreeningrecord; + +import com.alibaba.fastjson2.JSONObject; +import com.xinelu.common.annotation.RepeatSubmit; +import com.xinelu.common.constant.ScreeningProjectConstants; +import com.xinelu.common.core.controller.BaseController; +import com.xinelu.common.core.domain.R; +import com.xinelu.common.core.page.TableDataInfo; +import com.xinelu.common.utils.DateUtils; +import com.xinelu.common.utils.StringUtils; +import com.xinelu.manage.dto.screeningrecord.ScreeningApplyDTO; +import com.xinelu.manage.dto.screeningrecord.ScreeningRecordDTO; +import com.xinelu.manage.service.patientinfo.IPatientInfoService; +import com.xinelu.manage.service.screeningrecord.IScreeningRecordService; +import com.xinelu.manage.vo.screeningrecord.ScreeningRecordVo; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import java.util.List; +import javax.annotation.Resource; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * @Author mengkuiliang + * @Description 筛查预约控制器 + * @Date 2023-01-29 13:57 + * @Param + * @return + **/ +@RestController +@RequestMapping("/nurseApplet/screening/record") +@Api(tags = "筛查预约控制器-小程序") +public class AppletScreeningRecordController extends BaseController { + + @Resource + private IScreeningRecordService screeningRecordService; + + @Resource + private IPatientInfoService patientService; + + @GetMapping("/record") + @ApiOperation(value = "获取筛查结果记录") + public TableDataInfo record(ScreeningRecordDTO query) { + startPage(); + query.setScreeningType("1"); + query.setScreeningStatus("4"); + return getDataTable(screeningRecordService.record(query)); + } + + @GetMapping("/list") + @ApiOperation(value = "获取列表") + public TableDataInfo list(ScreeningRecordDTO query) { + startPage(); + query.setScreeningType("1"); + List list = screeningRecordService.list(query); + // 二维码返回 + list.forEach(record -> { + StringUtils.isNotEmpty(record.getApplyBarcode()); + // TODO 居民申请条码 + //record.setApplyBarcode(patientService.getPicToBase64(record.getApplyBarcode())); + }); + return getDataTable(list); + } + + @PostMapping("/save") + @ApiOperation(value = "预约") + @RepeatSubmit(interval = 2000, message = "请求过于频繁") + public R save(@RequestBody ScreeningApplyDTO body) { + try { + if(!DateUtils.formatDate(body.getApplyStartTime(), "yyyy-MM-dd").equals(DateUtils.formatDate(body.getApplyEndTime(), "yyyy-MM-dd"))) { + return R.fail("预约日期请选择在同一天"); + } + return R.ok(screeningRecordService.save(body)); + } catch (Exception e) { + return R.fail(e.getMessage()); + } + } + + @GetMapping("/cancel/{screeningId}") + @ApiOperation(value = "取消预约") + public R cancel(@PathVariable String screeningId) { + // 判断是否已登记 + ScreeningRecordVo screeningRecordVo = screeningRecordService.detail(screeningId); + if (StringUtils.equals(screeningRecordVo.getScreeningStatus(), "3") || StringUtils.equals(screeningRecordVo.getScreeningStatus(), "4")) { + return R.fail("该预约已登记,不能取消"); + } + screeningRecordService.cancel(screeningId); + return R.ok(); + } + + @GetMapping("/detail/{screeningId}") + @ApiOperation(value = "获取预约详情") + public R detail(@PathVariable String screeningId) { + ScreeningRecordVo screeningRecordVo = screeningRecordService.detail(screeningId); + if (screeningRecordVo != null) { + // TODO 设置文件类型 + //File fileInfo = sysFileService.getFile(screeningRecordVo.getAttachment()); + //if (fileInfo != null) { + // screeningRecordVo.setFileType(fileInfo.getSuffix()); + //} + if (!StringUtils.contains(screeningRecordVo.getProjectName(), ScreeningProjectConstants.ALZHEIMER)) { + screeningRecordService.getRecordDetail(screeningRecordVo); + } + } + return R.ok(screeningRecordVo); + } + + @GetMapping("/getScreening/{registerId}") + @ApiOperation(value = "获取当前预约信息") + public R getScreeningByRegisterId(@PathVariable String registerId) { + return R.ok(screeningRecordService.getScreeningByRegisterId(registerId)); + } + + @GetMapping("/last/{registerId}/{projectId}") + @ApiOperation(value = "获取最新一次筛查结果") + public R last(@PathVariable String registerId, @PathVariable String projectId) { + ScreeningRecordVo screeningRecordVo = screeningRecordService.last(registerId, projectId); + return R.ok(screeningRecordVo); + } + + @GetMapping("/getInfo/{assessRecordId}") + @ApiOperation(value = "推送筛查项目跳转查询推荐详情") + public R getInfo(@PathVariable("assessRecordId") String assessRecordId) { + JSONObject jsonObject = screeningRecordService.getInfo(assessRecordId); + return R.ok(jsonObject); + } +} diff --git a/xinelu-nurse-applet/src/main/java/com/xinelu/applet/controller/videoconsultation/VideoConsultationController.java b/xinelu-nurse-applet/src/main/java/com/xinelu/applet/controller/videoconsultation/VideoConsultationController.java new file mode 100644 index 0000000..2558bb3 --- /dev/null +++ b/xinelu-nurse-applet/src/main/java/com/xinelu/applet/controller/videoconsultation/VideoConsultationController.java @@ -0,0 +1,79 @@ +package com.xinelu.applet.controller.videoconsultation; + +import cn.hutool.core.util.RandomUtil; +import com.xinelu.common.core.domain.R; +import com.xinelu.common.core.dto.MessageTemplate; +import com.xinelu.common.enums.MessageContentType; +import com.xinelu.common.socket.WebSocketUtils; +import com.xinelu.common.utils.tencentcloudapi.common.Credential; +import com.xinelu.common.utils.tencentcloudapi.common.exception.TencentCloudSDKException; +import com.xinelu.common.utils.tencentcloudapi.common.profile.ClientProfile; +import com.xinelu.common.utils.tencentcloudapi.common.profile.HttpProfile; +import com.xinelu.common.utils.tencentcloudapi.trtc.v20190722.TLSSigAPIv2; +import com.xinelu.common.utils.tencentcloudapi.trtc.v20190722.TrtcClient; +import com.xinelu.common.utils.tencentcloudapi.trtc.v20190722.models.DissolveRoomRequest; +import com.xinelu.common.utils.tencentcloudapi.trtc.v20190722.models.DissolveRoomResponse; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import java.util.Date; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * @author gaoyu + * @description 远程会诊控制器 + * @date 2022-12-07 9:49 + */ +@Api(tags = "远程会诊控制器") +@RestController +@RequestMapping("/nurseApplet/consultation") +public class VideoConsultationController { + + @Value("${trtc.sdkappid}") + private String sdkappid; + @Value("${trtc.secretid}") + private String secretid; + @Value("${trtc.secretkey}") + private String secretkey; + private final static String ENDPOINT = "trtc.tencentcloudapi.com"; + private final static String REGION = "ap-beijing"; + + @ApiOperation("获取userSig") + @GetMapping("getUserSig/{userId}") + public R getUserSig(@PathVariable String userId) { + TLSSigAPIv2 sigAPIv2 = new TLSSigAPIv2(1600003294, "6b8b57a7eedb92b6646d1c81bd68681ab924e53b52069cd20b0f53c8e3801a18"); + String userSig = sigAPIv2.genUserSig(userId, 36000); + return R.ok(userSig); + } + + @ApiOperation("开始会诊") + @GetMapping("start/{applyId}") + public R start(@PathVariable String applyId) { + int roomId = RandomUtil.randomInt(1, 1000); + MessageTemplate msg = new MessageTemplate(); + msg.setMessage(String.valueOf(roomId)); + msg.setToKey(applyId); + msg.setMsgType(MessageContentType.CONSULTATION.name()); + msg.setSendTime(new Date()); + WebSocketUtils.sendMessage(applyId, msg); + return R.ok(String.valueOf(roomId)); + } + + @ApiOperation("解散房间") + @GetMapping("dissolveRoom/{roomId}") + public R dissolveRoom(@PathVariable String roomId) throws TencentCloudSDKException { + Credential cred = new Credential(secretid, secretkey); + HttpProfile httpProfile = new HttpProfile(); + httpProfile.setEndpoint(ENDPOINT); + ClientProfile clientProfile = new ClientProfile(); + clientProfile.setHttpProfile(httpProfile); + TrtcClient client = new TrtcClient(cred, REGION, clientProfile); + String params = "{\"SdkAppId\":" + sdkappid + ",\"RoomId\":" + roomId + "}"; + DissolveRoomRequest req = DissolveRoomRequest.fromJsonString(params, DissolveRoomRequest.class); + DissolveRoomResponse resp = client.DissolveRoom(req); + return R.ok(resp.getRequestId()); + } +} diff --git a/xinelu-nurse-manage/src/main/java/com/xinelu/manage/controller/schedule/ScheduleController.java b/xinelu-nurse-manage/src/main/java/com/xinelu/manage/controller/schedule/ScheduleController.java new file mode 100644 index 0000000..cdade48 --- /dev/null +++ b/xinelu-nurse-manage/src/main/java/com/xinelu/manage/controller/schedule/ScheduleController.java @@ -0,0 +1,18 @@ +package com.xinelu.manage.controller.schedule; + +import io.swagger.annotations.ApiModel; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * @description: 排班班次控制器 + * @author: haown + * @create: 2023-09-21 17:25 + **/ +@ApiModel(value = "排班班次控制器") +@RestController +@RequestMapping("/system/schedule") +public class ScheduleController { + + +} diff --git a/xinelu-nurse-manage/src/main/java/com/xinelu/manage/controller/screeningproject/ScreeningProjectController.java b/xinelu-nurse-manage/src/main/java/com/xinelu/manage/controller/screeningproject/ScreeningProjectController.java index 2284c69..8b522b4 100644 --- a/xinelu-nurse-manage/src/main/java/com/xinelu/manage/controller/screeningproject/ScreeningProjectController.java +++ b/xinelu-nurse-manage/src/main/java/com/xinelu/manage/controller/screeningproject/ScreeningProjectController.java @@ -29,7 +29,7 @@ import org.springframework.web.bind.annotation.RestController; * @create: 2023-01-19 10:49 **/ @RestController -@RequestMapping("/business/project") +@RequestMapping("/system/screening/project") @Api(tags = "项目控制器") public class ScreeningProjectController extends BaseController { @Resource diff --git a/xinelu-nurse-manage/src/main/java/com/xinelu/manage/controller/screeningrecord/ScreeningRecordController.java b/xinelu-nurse-manage/src/main/java/com/xinelu/manage/controller/screeningrecord/ScreeningRecordController.java index e857b97..9a56d52 100644 --- a/xinelu-nurse-manage/src/main/java/com/xinelu/manage/controller/screeningrecord/ScreeningRecordController.java +++ b/xinelu-nurse-manage/src/main/java/com/xinelu/manage/controller/screeningrecord/ScreeningRecordController.java @@ -33,7 +33,7 @@ import org.springframework.web.bind.annotation.RestController; * @return **/ @RestController -@RequestMapping("/business/screening") +@RequestMapping("/system/screening/record") @Api(tags = "筛查预约控制器") public class ScreeningRecordController extends BaseController { diff --git a/xinelu-nurse-manage/src/main/java/com/xinelu/manage/domain/scheduleplan/SchedulePlan.java b/xinelu-nurse-manage/src/main/java/com/xinelu/manage/domain/scheduleplan/SchedulePlan.java new file mode 100644 index 0000000..3905f9e --- /dev/null +++ b/xinelu-nurse-manage/src/main/java/com/xinelu/manage/domain/scheduleplan/SchedulePlan.java @@ -0,0 +1,85 @@ +package com.xinelu.manage.domain.scheduleplan; + +import com.xinelu.common.core.domain.BaseEntity; +import java.util.Date; +import lombok.Data; + +/** + * 医生排班计划表 + * @TableName schedule_plan + */ +@Data +public class SchedulePlan extends BaseEntity { + /** + * 主键 + */ + private Long id; + + /** + * 医院主键 + */ + private Long hospitalId; + + /** + * 医院名称 + */ + private String hospitalName; + + /** + * 所属部门id + */ + private Long departmentId; + + /** + * 所属部门名称 + */ + private String departmentName; + + /** + * 班次主键 + */ + private Long scheduleId; + + /** + * 班次名称 + */ + private String scheduleName; + + /** + * 医生主键 + */ + private Long doctorId; + + /** + * 医生姓名 + */ + private String doctorName; + + /** + * 排班开始日期 + */ + private Date scheduleStartDate; + + /** + * 排班结束日期 + */ + private Date scheduleEndDate; + + /** + * 每人可预约分钟数 + */ + private Integer minutesPerPatient; + + /** + * 每天可预约人数 + */ + private Integer patientsPerDay; + + /** + * 出诊地点 + */ + private String scheduleAddress; + + private static final long serialVersionUID = 1L; + +} \ No newline at end of file diff --git a/xinelu-nurse-manage/src/main/java/com/xinelu/manage/domain/scheduleplandetail/SchedulePlanDetail.java b/xinelu-nurse-manage/src/main/java/com/xinelu/manage/domain/scheduleplandetail/SchedulePlanDetail.java new file mode 100644 index 0000000..8896c2e --- /dev/null +++ b/xinelu-nurse-manage/src/main/java/com/xinelu/manage/domain/scheduleplandetail/SchedulePlanDetail.java @@ -0,0 +1,55 @@ +package com.xinelu.manage.domain.scheduleplandetail; + +import java.io.Serializable; +import java.util.Date; +import lombok.Data; + +/** + * 医生排班计划明细表 + * @TableName schedule_plan_detail + */ +@Data +public class SchedulePlanDetail implements Serializable { + /** + * 主键 + */ + private Long id; + + /** + * 排班计划主键 + */ + private Long schedulePlanId; + + /** + * 医生主键 + */ + private Long doctorId; + + /** + * 医生姓名 + */ + private String doctorName; + + /** + * 排班日期 + */ + private Date scheduleDate; + + /** + * 开始时间 + */ + private Date scheduleStartTime; + + /** + * 结束时间 + */ + private Date scheduleEndTime; + + /** + * 预约状态(0:未预约,1:已预约) + */ + private String applyState; + + private static final long serialVersionUID = 1L; + +} \ No newline at end of file diff --git a/xinelu-nurse-manage/src/main/java/com/xinelu/manage/mapper/scheduleplan/SchedulePlanMapper.java b/xinelu-nurse-manage/src/main/java/com/xinelu/manage/mapper/scheduleplan/SchedulePlanMapper.java new file mode 100644 index 0000000..4b3b36d --- /dev/null +++ b/xinelu-nurse-manage/src/main/java/com/xinelu/manage/mapper/scheduleplan/SchedulePlanMapper.java @@ -0,0 +1,25 @@ +package com.xinelu.manage.mapper.scheduleplan; + +import com.xinelu.manage.domain.scheduleplan.SchedulePlan; + +/** +* @author haown +* @description 针对表【schedule_plan(医生排班计划表)】的数据库操作Mapper +* @createDate 2023-09-21 17:17:23 +* @Entity com.xinelu.manage.domain.scheduleplan.SchedulePlan +*/ +public interface SchedulePlanMapper { + + int deleteByPrimaryKey(Long id); + + int insert(SchedulePlan record); + + int insertSelective(SchedulePlan record); + + SchedulePlan selectByPrimaryKey(Long id); + + int updateByPrimaryKeySelective(SchedulePlan record); + + int updateByPrimaryKey(SchedulePlan record); + +} diff --git a/xinelu-nurse-manage/src/main/java/com/xinelu/manage/mapper/scheduleplandetail/SchedulePlanDetailMapper.java b/xinelu-nurse-manage/src/main/java/com/xinelu/manage/mapper/scheduleplandetail/SchedulePlanDetailMapper.java new file mode 100644 index 0000000..fcf3c37 --- /dev/null +++ b/xinelu-nurse-manage/src/main/java/com/xinelu/manage/mapper/scheduleplandetail/SchedulePlanDetailMapper.java @@ -0,0 +1,25 @@ +package com.xinelu.manage.mapper.scheduleplandetail; + +import com.xinelu.manage.domain.scheduleplandetail.SchedulePlanDetail; + +/** +* @author haown +* @description 针对表【schedule_plan_detail(医生排班计划明细表)】的数据库操作Mapper +* @createDate 2023-09-21 17:22:22 +* @Entity com.xinelu.manage.domain.scheduleplandetail.SchedulePlanDetail +*/ +public interface SchedulePlanDetailMapper { + + int deleteByPrimaryKey(Long id); + + int insert(SchedulePlanDetail record); + + int insertSelective(SchedulePlanDetail record); + + SchedulePlanDetail selectByPrimaryKey(Long id); + + int updateByPrimaryKeySelective(SchedulePlanDetail record); + + int updateByPrimaryKey(SchedulePlanDetail record); + +} diff --git a/xinelu-nurse-manage/src/main/java/com/xinelu/manage/service/scheduleplan/SchedulePlanService.java b/xinelu-nurse-manage/src/main/java/com/xinelu/manage/service/scheduleplan/SchedulePlanService.java new file mode 100644 index 0000000..ef4dec8 --- /dev/null +++ b/xinelu-nurse-manage/src/main/java/com/xinelu/manage/service/scheduleplan/SchedulePlanService.java @@ -0,0 +1,10 @@ +package com.xinelu.manage.service.scheduleplan; + +/** +* @author haown +* @description 针对表【schedule_plan(医生排班计划表)】的数据库操作Service +* @createDate 2023-09-21 17:16:40 +*/ +public interface SchedulePlanService { + +} diff --git a/xinelu-nurse-manage/src/main/java/com/xinelu/manage/service/scheduleplan/impl/SchedulePlanServiceImpl.java b/xinelu-nurse-manage/src/main/java/com/xinelu/manage/service/scheduleplan/impl/SchedulePlanServiceImpl.java new file mode 100644 index 0000000..9ef331f --- /dev/null +++ b/xinelu-nurse-manage/src/main/java/com/xinelu/manage/service/scheduleplan/impl/SchedulePlanServiceImpl.java @@ -0,0 +1,18 @@ +package com.xinelu.manage.service.scheduleplan.impl; + +import com.xinelu.manage.service.scheduleplan.SchedulePlanService; +import org.springframework.stereotype.Service; + +/** +* @author haown +* @description 针对表【schedule_plan(医生排班计划表)】的数据库操作Service实现 +* @createDate 2023-09-21 17:16:40 +*/ +@Service +public class SchedulePlanServiceImpl implements SchedulePlanService{ + +} + + + + diff --git a/xinelu-nurse-manage/src/main/java/com/xinelu/manage/service/scheduleplandetail/SchedulePlanDetailService.java b/xinelu-nurse-manage/src/main/java/com/xinelu/manage/service/scheduleplandetail/SchedulePlanDetailService.java new file mode 100644 index 0000000..3bf46b5 --- /dev/null +++ b/xinelu-nurse-manage/src/main/java/com/xinelu/manage/service/scheduleplandetail/SchedulePlanDetailService.java @@ -0,0 +1,10 @@ +package com.xinelu.manage.service.scheduleplandetail; + +/** +* @author haown +* @description 针对表【schedule_plan_detail(医生排班计划明细表)】的数据库操作Service +* @createDate 2023-09-21 17:21:38 +*/ +public interface SchedulePlanDetailService { + +} diff --git a/xinelu-nurse-manage/src/main/java/com/xinelu/manage/service/scheduleplandetail/impl/SchedulePlanDetailServiceImpl.java b/xinelu-nurse-manage/src/main/java/com/xinelu/manage/service/scheduleplandetail/impl/SchedulePlanDetailServiceImpl.java new file mode 100644 index 0000000..f71e778 --- /dev/null +++ b/xinelu-nurse-manage/src/main/java/com/xinelu/manage/service/scheduleplandetail/impl/SchedulePlanDetailServiceImpl.java @@ -0,0 +1,18 @@ +package com.xinelu.manage.service.scheduleplandetail.impl; + +import com.xinelu.manage.service.scheduleplandetail.SchedulePlanDetailService; +import org.springframework.stereotype.Service; + +/** +* @author haown +* @description 针对表【schedule_plan_detail(医生排班计划明细表)】的数据库操作Service实现 +* @createDate 2023-09-21 17:21:38 +*/ +@Service +public class SchedulePlanDetailServiceImpl implements SchedulePlanDetailService{ + +} + + + + diff --git a/xinelu-nurse-manage/src/main/resources/mapper/manage/scheduleplan/SchedulePlanMapper.xml b/xinelu-nurse-manage/src/main/resources/mapper/manage/scheduleplan/SchedulePlanMapper.xml new file mode 100644 index 0000000..d2b9267 --- /dev/null +++ b/xinelu-nurse-manage/src/main/resources/mapper/manage/scheduleplan/SchedulePlanMapper.xml @@ -0,0 +1,187 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + id,hospital_id,hospital_name, + department_id,department_name,schedule_id, + schedule_name,doctor_id,doctor_name, + schedule_start_date,schedule_end_date,minutes_per_patient, + patients_per_day,schedule_address,create_by, + create_time,update_by,update_time + + + + + + delete from schedule_plan + where id = #{id,jdbcType=BIGINT} + + + insert into schedule_plan + ( id,hospital_id,hospital_name + ,department_id,department_name,schedule_id + ,schedule_name,doctor_id,doctor_name + ,schedule_start_date,schedule_end_date,minutes_per_patient + ,patients_per_day,schedule_address,create_by + ,create_time,update_by,update_time + ) + values (#{id,jdbcType=BIGINT},#{hospitalId,jdbcType=BIGINT},#{hospitalName,jdbcType=VARCHAR} + ,#{departmentId,jdbcType=BIGINT},#{departmentName,jdbcType=VARCHAR},#{scheduleId,jdbcType=BIGINT} + ,#{scheduleName,jdbcType=VARCHAR},#{doctorId,jdbcType=BIGINT},#{doctorName,jdbcType=VARCHAR} + ,#{scheduleStartDate,jdbcType=DATE},#{scheduleEndDate,jdbcType=DATE},#{minutesPerPatient,jdbcType=INTEGER} + ,#{patientsPerDay,jdbcType=INTEGER},#{scheduleAddress,jdbcType=VARCHAR},#{createBy,jdbcType=VARCHAR} + ,#{createTime,jdbcType=TIMESTAMP},#{updateBy,jdbcType=VARCHAR},#{updateTime,jdbcType=TIMESTAMP} + ) + + + insert into schedule_plan + + id, + hospital_id, + hospital_name, + department_id, + department_name, + schedule_id, + schedule_name, + doctor_id, + doctor_name, + schedule_start_date, + schedule_end_date, + minutes_per_patient, + patients_per_day, + schedule_address, + create_by, + create_time, + update_by, + update_time, + + + #{id,jdbcType=BIGINT}, + #{hospitalId,jdbcType=BIGINT}, + #{hospitalName,jdbcType=VARCHAR}, + #{departmentId,jdbcType=BIGINT}, + #{departmentName,jdbcType=VARCHAR}, + #{scheduleId,jdbcType=BIGINT}, + #{scheduleName,jdbcType=VARCHAR}, + #{doctorId,jdbcType=BIGINT}, + #{doctorName,jdbcType=VARCHAR}, + #{scheduleStartDate,jdbcType=DATE}, + #{scheduleEndDate,jdbcType=DATE}, + #{minutesPerPatient,jdbcType=INTEGER}, + #{patientsPerDay,jdbcType=INTEGER}, + #{scheduleAddress,jdbcType=VARCHAR}, + #{createBy,jdbcType=VARCHAR}, + #{createTime,jdbcType=TIMESTAMP}, + #{updateBy,jdbcType=VARCHAR}, + #{updateTime,jdbcType=TIMESTAMP}, + + + + update schedule_plan + + + hospital_id = #{hospitalId,jdbcType=BIGINT}, + + + hospital_name = #{hospitalName,jdbcType=VARCHAR}, + + + department_id = #{departmentId,jdbcType=BIGINT}, + + + department_name = #{departmentName,jdbcType=VARCHAR}, + + + schedule_id = #{scheduleId,jdbcType=BIGINT}, + + + schedule_name = #{scheduleName,jdbcType=VARCHAR}, + + + doctor_id = #{doctorId,jdbcType=BIGINT}, + + + doctor_name = #{doctorName,jdbcType=VARCHAR}, + + + schedule_start_date = #{scheduleStartDate,jdbcType=DATE}, + + + schedule_end_date = #{scheduleEndDate,jdbcType=DATE}, + + + minutes_per_patient = #{minutesPerPatient,jdbcType=INTEGER}, + + + patients_per_day = #{patientsPerDay,jdbcType=INTEGER}, + + + schedule_address = #{scheduleAddress,jdbcType=VARCHAR}, + + + create_by = #{createBy,jdbcType=VARCHAR}, + + + create_time = #{createTime,jdbcType=TIMESTAMP}, + + + update_by = #{updateBy,jdbcType=VARCHAR}, + + + update_time = #{updateTime,jdbcType=TIMESTAMP}, + + + where id = #{id,jdbcType=BIGINT} + + + update schedule_plan + set + hospital_id = #{hospitalId,jdbcType=BIGINT}, + hospital_name = #{hospitalName,jdbcType=VARCHAR}, + department_id = #{departmentId,jdbcType=BIGINT}, + department_name = #{departmentName,jdbcType=VARCHAR}, + schedule_id = #{scheduleId,jdbcType=BIGINT}, + schedule_name = #{scheduleName,jdbcType=VARCHAR}, + doctor_id = #{doctorId,jdbcType=BIGINT}, + doctor_name = #{doctorName,jdbcType=VARCHAR}, + schedule_start_date = #{scheduleStartDate,jdbcType=DATE}, + schedule_end_date = #{scheduleEndDate,jdbcType=DATE}, + minutes_per_patient = #{minutesPerPatient,jdbcType=INTEGER}, + patients_per_day = #{patientsPerDay,jdbcType=INTEGER}, + schedule_address = #{scheduleAddress,jdbcType=VARCHAR}, + create_by = #{createBy,jdbcType=VARCHAR}, + create_time = #{createTime,jdbcType=TIMESTAMP}, + update_by = #{updateBy,jdbcType=VARCHAR}, + update_time = #{updateTime,jdbcType=TIMESTAMP} + where id = #{id,jdbcType=BIGINT} + + diff --git a/xinelu-nurse-manage/src/main/resources/mapper/manage/scheduleplandetail/SchedulePlanDetailMapper.xml b/xinelu-nurse-manage/src/main/resources/mapper/manage/scheduleplandetail/SchedulePlanDetailMapper.xml new file mode 100644 index 0000000..db19e6d --- /dev/null +++ b/xinelu-nurse-manage/src/main/resources/mapper/manage/scheduleplandetail/SchedulePlanDetailMapper.xml @@ -0,0 +1,106 @@ + + + + + + + + + + + + + + + + + id,schedule_plan_id,doctor_id, + doctor_name,schedule_date,schedule_start_time, + schedule_end_time,apply_state + + + + + + delete from schedule_plan_detail + where id = #{id,jdbcType=BIGINT} + + + insert into schedule_plan_detail + ( id,schedule_plan_id,doctor_id + ,doctor_name,schedule_date,schedule_start_time + ,schedule_end_time,apply_state) + values (#{id,jdbcType=BIGINT},#{schedulePlanId,jdbcType=BIGINT},#{doctorId,jdbcType=BIGINT} + ,#{doctorName,jdbcType=VARCHAR},#{scheduleDate,jdbcType=DATE},#{scheduleStartTime,jdbcType=TIME} + ,#{scheduleEndTime,jdbcType=TIME},#{applyState,jdbcType=VARCHAR}) + + + insert into schedule_plan_detail + + id, + schedule_plan_id, + doctor_id, + doctor_name, + schedule_date, + schedule_start_time, + schedule_end_time, + apply_state, + + + #{id,jdbcType=BIGINT}, + #{schedulePlanId,jdbcType=BIGINT}, + #{doctorId,jdbcType=BIGINT}, + #{doctorName,jdbcType=VARCHAR}, + #{scheduleDate,jdbcType=DATE}, + #{scheduleStartTime,jdbcType=TIME}, + #{scheduleEndTime,jdbcType=TIME}, + #{applyState,jdbcType=VARCHAR}, + + + + update schedule_plan_detail + + + schedule_plan_id = #{schedulePlanId,jdbcType=BIGINT}, + + + doctor_id = #{doctorId,jdbcType=BIGINT}, + + + doctor_name = #{doctorName,jdbcType=VARCHAR}, + + + schedule_date = #{scheduleDate,jdbcType=DATE}, + + + schedule_start_time = #{scheduleStartTime,jdbcType=TIME}, + + + schedule_end_time = #{scheduleEndTime,jdbcType=TIME}, + + + apply_state = #{applyState,jdbcType=VARCHAR}, + + + where id = #{id,jdbcType=BIGINT} + + + update schedule_plan_detail + set + schedule_plan_id = #{schedulePlanId,jdbcType=BIGINT}, + doctor_id = #{doctorId,jdbcType=BIGINT}, + doctor_name = #{doctorName,jdbcType=VARCHAR}, + schedule_date = #{scheduleDate,jdbcType=DATE}, + schedule_start_time = #{scheduleStartTime,jdbcType=TIME}, + schedule_end_time = #{scheduleEndTime,jdbcType=TIME}, + apply_state = #{applyState,jdbcType=VARCHAR} + where id = #{id,jdbcType=BIGINT} + +