Compare commits
133 Commits
Author | SHA1 | Date |
---|---|---|
CrazyIter_Bin | a70efb3c7b | 1 month ago |
PL | e8dfcf7f9f | 1 month ago |
zhouj1203@hotmail.com | 523dfa47b6 | 1 month ago |
zhouj1203@hotmail.com | 5200ec6f8c | 1 month ago |
PL | 678e7d302b | 1 month ago |
PL | d333e6809f | 1 month ago |
PL | 812456a8e1 | 1 month ago |
PL | 81a83a7566 | 1 month ago |
zhouj1203@hotmail.com | 6587bcca67 | 1 month ago |
PL | d880be517b | 1 month ago |
zhouj1203@hotmail.com | f46d24223f | 1 month ago |
zhouj1203@hotmail.com | 8e9d78dcc4 | 1 month ago |
Li | c20bbd2cf4 | 1 month ago |
zhouj1203@hotmail.com | 3451287df9 | 1 month ago |
zhouj1203@hotmail.com | a1aa8c7187 | 1 month ago |
zhouj1203@hotmail.com | db6c7d402f | 1 month ago |
zhouj1203@hotmail.com | a801724b75 | 1 month ago |
winter | c9e7268d87 | 5 months ago |
winter | b7126f7bd8 | 5 months ago |
winter | 5d33226f94 | 5 months ago |
winter | 5caf6de0ec | 5 months ago |
winter | 2698f759a4 | 5 months ago |
winter | f966dacb9c | 5 months ago |
winter | a53efd1f7a | 6 months ago |
winter | acf448e980 | 6 months ago |
winter | 9aec66d1a7 | 6 months ago |
winter | b53fe0f7d6 | 6 months ago |
winter | ea5c6fcb81 | 6 months ago |
winter | 52773b45ff | 6 months ago |
winter | bbef6070fd | 6 months ago |
winter | a00e0eaf2e | 6 months ago |
winter | 1a006ee304 | 7 months ago |
winter | 7aa14b1b82 | 7 months ago |
winter | c2c1f9cd0b | 7 months ago |
winter | 32f2414cd8 | 7 months ago |
winter | fa1c6a9b20 | 7 months ago |
winter | b9950c6022 | 7 months ago |
winter | da1bb7fc87 | 7 months ago |
winter | 09140678bc | 7 months ago |
winter | 9ff280a94e | 7 months ago |
winter | 29f1fed4f1 | 7 months ago |
winter | 94ac4286c3 | 7 months ago |
winter | 9f0cd7d01a | 7 months ago |
winter | a539afb9e8 | 7 months ago |
winter | 21790d5666 | 7 months ago |
winter | 53f9492eb0 | 7 months ago |
winter | 079f2fbf6f | 7 months ago |
winter | bc13f67a29 | 7 months ago |
winter | d5ad605b35 | 8 months ago |
winter | 118fc413d3 | 8 months ago |
winter | 0ac22b5353 | 8 months ago |
winter | 8b25e112e8 | 8 months ago |
winter | f3e3f48422 | 8 months ago |
winter | e74fee8812 | 8 months ago |
winter | 8bc0da460b | 8 months ago |
winter | 70ffad27a3 | 8 months ago |
winter | 2fb38aa62c | 8 months ago |
winter | 48588b12fc | 8 months ago |
winter | 256b233b4c | 8 months ago |
winter | 8c15427ff7 | 8 months ago |
winter | d155efe947 | 9 months ago |
winter | 1a18105096 | 9 months ago |
winter | 327e399d5a | 9 months ago |
winter | 9f77aa9ede | 9 months ago |
winter | 38699011e6 | 9 months ago |
winter | eec45f6921 | 9 months ago |
winter | 65c8fd598f | 9 months ago |
winter | 5c01fca6c1 | 9 months ago |
winter | 0ded5af82d | 9 months ago |
winter | ab2aaa1757 | 9 months ago |
winter | f2e0c0b19e | 9 months ago |
winter | fb49fea68e | 9 months ago |
winter | d1bb7d56a9 | 9 months ago |
winter | 56700192d9 | 9 months ago |
winter | c9309087c3 | 9 months ago |
winter | 8301c973c0 | 9 months ago |
winter | 178e3cfc03 | 9 months ago |
winter | 83bdf4d6c1 | 9 months ago |
winter | 2f972c65e3 | 9 months ago |
winter | ae729c05e8 | 9 months ago |
winter | 40d4826695 | 9 months ago |
winter | ef1979cbd7 | 9 months ago |
winter | cec0a7e58d | 9 months ago |
winter | 6cf0ebaef3 | 9 months ago |
winter | 316f9daa4a | 9 months ago |
winter | d185a3ffe7 | 9 months ago |
winter | 646caaa926 | 9 months ago |
winter | 76499c3904 | 9 months ago |
winter | fede04c70d | 9 months ago |
winter | fa03927579 | 9 months ago |
winter | 6ea7454973 | 9 months ago |
winter | ccbd59f18f | 9 months ago |
winter | fee64cb1ac | 9 months ago |
winter | 0bae71207b | 9 months ago |
winter | aa35b01844 | 9 months ago |
winter | 8ddf66c97d | 9 months ago |
CrazyIter_Bin | 890d23ae6a | 9 months ago |
winter | b610194f30 | 9 months ago |
winter | 7918786641 | 9 months ago |
winter | 79db32c215 | 9 months ago |
winter | 3477592027 | 10 months ago |
winter | 83c980dcae | 10 months ago |
winter | a138e16b40 | 10 months ago |
winter | 6d631563c6 | 10 months ago |
winter | 8f7b1f9cc1 | 10 months ago |
winter | c54edf3cfe | 10 months ago |
winter | a3922d8c2d | 10 months ago |
winter | 6c47e0c59e | 10 months ago |
winter | 18d42a36e6 | 10 months ago |
winter | a09d61b020 | 10 months ago |
winter | c34e87ca0d | 10 months ago |
winter | 18cfb50a46 | 10 months ago |
winter | a2d75a1c3d | 10 months ago |
winter | 29d21f0233 | 10 months ago |
winter | 6dffd636ec | 10 months ago |
winter | 8301cfd96d | 10 months ago |
winter | aa582361f8 | 10 months ago |
winter | 38f5bf39ef | 10 months ago |
winter | edc79455cc | 10 months ago |
winter | 610c6d4e81 | 10 months ago |
winter | 5e06e8ed1e | 10 months ago |
winter | d9a19736f2 | 10 months ago |
winter | 2b271547e7 | 10 months ago |
winter | 148c31b323 | 10 months ago |
winter | 68f5303a4c | 10 months ago |
winter | 1be6ca7e02 | 10 months ago |
winter | 46b88742d7 | 10 months ago |
winter | 676f6fc46b | 10 months ago |
winter | a16e8f8de8 | 10 months ago |
winter | f0a9ba9243 | 10 months ago |
winter | 46fa7bf45e | 10 months ago |
winter | c1e2a62527 | 10 months ago |
winter | 38bb428c59 | 11 months ago |
@ -0,0 +1,31 @@
|
||||
# TeamModel extension
|
||||
> SpringBoot base version of TeamModel extension
|
||||
>
|
||||
> **注意**: 所有复盘输出均已脱敏,不包含任何业务,密码等关键信息
|
||||
|
||||
## 迁移目录:
|
||||
- Azure OIDC(SSO) 迁移
|
||||
- id-token(jwt) 验证迁移 (出现语言框架之间的签名算法规范问题,解决见: [输出复盘](https://juejin.cn/post/7300036605099163702))
|
||||
- 钉钉告警: 异常通知
|
||||
- 异常文件记录
|
||||
|
||||
### MILESTONE:
|
||||
- Java 框架搭建
|
||||
- 教育评价系统前后台实现
|
||||
- Chat with AI 接入
|
||||
|
||||
|
||||
|
||||
## 数据表规则:
|
||||
> ID 没有特殊之指明则为 UUID
|
||||
### 教育评价树
|
||||
分区键: `Appraise` , 表内 `schoolId` 区分学校, `periodId` 区分学区
|
||||
|
||||
### 教育评价项
|
||||
> 学生每学期所有的评价项都在一个项中,按学校进行分区
|
||||
>
|
||||
> 注意: 如果学生中途换班,也就是 classId 发生变动,那么会给这个学生在本学期新开一个文档,也就是说一个学生在一个学期的文档可能不止一个(小概率)
|
||||
>
|
||||
|
||||
分区键: `AppraiseRecord-{学校id}`
|
||||
academicYearId: `学年 + semesterId` -> eg: 2022.uuid
|
@ -0,0 +1,118 @@
|
||||
package cn.teammodel.ai;
|
||||
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import cn.teammodel.ai.cache.HistoryCache;
|
||||
import cn.teammodel.ai.domain.SparkChatRequestParam;
|
||||
import cn.teammodel.ai.listener.SparkGptStreamListener;
|
||||
import lombok.Data;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import okhttp3.HttpUrl;
|
||||
import okhttp3.OkHttpClient;
|
||||
import okhttp3.Request;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import javax.crypto.Mac;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
import java.net.URL;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* spark Gpt client
|
||||
* @author winter
|
||||
* @create 2023-12-15 14:29
|
||||
*/
|
||||
@Component
|
||||
@Data
|
||||
@Slf4j
|
||||
public class SparkGptClient implements InitializingBean {
|
||||
@Resource
|
||||
private SparkGptProperties sparkGptProperties;
|
||||
private OkHttpClient okHttpClient;
|
||||
// private String authUrl;
|
||||
|
||||
/**
|
||||
* 静态构造对象方法
|
||||
*/
|
||||
public void init() {
|
||||
// 初始化缓存
|
||||
HistoryCache.init(sparkGptProperties.getCache_timeout(), sparkGptProperties.getCache_context());
|
||||
// 初始化 authUrl
|
||||
// log.info("[SPARK CHAT] 鉴权 endpoint : {}", this.authUrl);
|
||||
this.okHttpClient = new OkHttpClient.Builder()
|
||||
.connectTimeout(180, TimeUnit.SECONDS)
|
||||
.readTimeout(180, TimeUnit.SECONDS) // sse 接口的 readTimeout 不能设置小了
|
||||
.writeTimeout(180, TimeUnit.SECONDS)
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 流式的生成结果,以 sse 的方式向客户端推送
|
||||
*/
|
||||
public void streamChatCompletion(SparkChatRequestParam param, SparkGptStreamListener listener) {
|
||||
try {
|
||||
param.setAppId(sparkGptProperties.getAppId());
|
||||
String authUrl = genAuthUrl(sparkGptProperties.getEndpoint(), sparkGptProperties.getApiKey(), sparkGptProperties.getApiSecret());
|
||||
authUrl = authUrl.replace("http://", "ws://").replace("https://", "wss://");
|
||||
Request request = new Request.Builder().url(authUrl).build();
|
||||
// 设置请求参数
|
||||
listener.setRequestJson(param.toJsonParams());
|
||||
log.info("[SPARK CHAT] 请求参数 {}", JSONUtil.parseObj(param.toJsonParams()).toStringPretty());
|
||||
// 不能使用单例 client
|
||||
okHttpClient.newWebSocket(request, listener);
|
||||
} catch (Exception e) {
|
||||
log.error("[SPARK CHAT] Spark AI 请求异常: {}", e.getMessage());
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成鉴权URL
|
||||
*/
|
||||
public static String genAuthUrl(String endpoint, String apiKey, String apiSecret) {
|
||||
URL url = null;
|
||||
String date = null;
|
||||
String preStr = null;
|
||||
Mac mac = null;
|
||||
try {
|
||||
url = new URL(endpoint);
|
||||
// 时间
|
||||
SimpleDateFormat format = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss z", Locale.US);
|
||||
format.setTimeZone(TimeZone.getTimeZone("GMT"));
|
||||
date = format.format(new Date());
|
||||
// 拼接
|
||||
preStr = "host: " + url.getHost() + "\n" +
|
||||
"date: " + date + "\n" +
|
||||
"GET " + url.getPath() + " HTTP/1.1";
|
||||
// SHA256加密
|
||||
mac = Mac.getInstance("hmacsha256");
|
||||
SecretKeySpec spec = new SecretKeySpec(apiSecret.getBytes(StandardCharsets.UTF_8), "hmacsha256");
|
||||
mac.init(spec);
|
||||
} catch (Exception e) {
|
||||
log.error("[SPARK CHAT] 生成鉴权URL失败, endpoint: {}, apiKey: {}, apiSecret: {}", endpoint, apiKey, apiSecret);
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
byte[] hexDigits = mac.doFinal(preStr.getBytes(StandardCharsets.UTF_8));
|
||||
// Base64加密
|
||||
String sha = Base64.getEncoder().encodeToString(hexDigits);
|
||||
// 拼接
|
||||
String authorization = String.format("api_key=\"%s\", algorithm=\"%s\", headers=\"%s\", signature=\"%s\"", apiKey, "hmac-sha256", "host date request-line", sha);
|
||||
// 拼接地址
|
||||
HttpUrl httpUrl = Objects.requireNonNull(HttpUrl.parse("https://" + url.getHost() + url.getPath())).newBuilder().
|
||||
addQueryParameter("authorization", Base64.getEncoder().encodeToString(authorization.getBytes(StandardCharsets.UTF_8))).
|
||||
addQueryParameter("date", date).//
|
||||
addQueryParameter("host", url.getHost()).//
|
||||
build();
|
||||
return httpUrl.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
init();
|
||||
}
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
package cn.teammodel.ai;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* @author winter
|
||||
* @create 2023-12-15 14:29
|
||||
*/
|
||||
|
||||
@Data
|
||||
@Configuration
|
||||
@ConfigurationProperties(prefix = "spark.gpt")
|
||||
public class SparkGptProperties {
|
||||
private String endpoint;
|
||||
private String appId;
|
||||
private String apiKey;
|
||||
private String apiSecret;
|
||||
/**
|
||||
* 单个会话的缓存过期时间
|
||||
*/
|
||||
private Long cache_timeout;
|
||||
/**
|
||||
* 历史上下文数
|
||||
*/
|
||||
private Integer cache_context;
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
package cn.teammodel.ai;
|
||||
|
||||
import lombok.experimental.UtilityClass;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
|
||||
|
||||
@UtilityClass
|
||||
@Slf4j
|
||||
public class SseHelper {
|
||||
/**
|
||||
* 断开 server 与 client 的 sse 连接
|
||||
*/
|
||||
public void complete(SseEmitter sseEmitter) {
|
||||
try {
|
||||
sseEmitter.complete();
|
||||
} catch (Exception e) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 向 client 发送消息
|
||||
*/
|
||||
public void send(SseEmitter sseEmitter, Object data) {
|
||||
try {
|
||||
sseEmitter.send(data);
|
||||
} catch (Exception e) {
|
||||
log.error("sseEmitter send error", e);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,62 @@
|
||||
package cn.teammodel.ai.cache;
|
||||
|
||||
import cn.hutool.cache.CacheUtil;
|
||||
import cn.hutool.cache.impl.TimedCache;
|
||||
import cn.hutool.core.collection.ListUtil;
|
||||
import cn.teammodel.model.entity.ai.ChatSession.Message;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.ObjectUtils;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 聊天记录上下文的缓存
|
||||
* @author winter
|
||||
* @create 2023-12-20 11:02
|
||||
*/
|
||||
@Slf4j
|
||||
@SuppressWarnings("unchecked")
|
||||
public class HistoryCache {
|
||||
private static TimedCache<Object, Object> HISTORY;
|
||||
private static Integer contextSize = 3;
|
||||
|
||||
/**
|
||||
* 初始化缓存
|
||||
*/
|
||||
public static void init(Long timeout, Integer contextNum) {
|
||||
contextSize = contextNum;
|
||||
HISTORY = CacheUtil.newTimedCache(timeout);
|
||||
// 一分钟清理一次
|
||||
HISTORY.schedulePrune(60 * 1000);
|
||||
}
|
||||
|
||||
public static List<Message> getContext(String sessionId) {
|
||||
return (List<Message>) HISTORY.get(sessionId);
|
||||
}
|
||||
|
||||
public static void putContext(String sessionId, List<Message> context) {
|
||||
HISTORY.put(sessionId, context);
|
||||
}
|
||||
|
||||
public static void removeContext(String sessionId) {
|
||||
HISTORY.remove(sessionId);}
|
||||
|
||||
/**
|
||||
* 更新上下文, 保证上下文的数量在 contextSize 之内
|
||||
*/
|
||||
public static void updateContext(String sessionId, Message message) {
|
||||
List<Message> messages = (List<Message>)HISTORY.get(sessionId);
|
||||
|
||||
if (ObjectUtils.isEmpty(messages)) {
|
||||
List<Message> context = ListUtil.of(message);
|
||||
HISTORY.put(sessionId, context);
|
||||
} else if (messages.size() >= contextSize) {
|
||||
// 队列
|
||||
messages.remove(0);
|
||||
messages.add(message);
|
||||
} else {
|
||||
messages.add(message);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,95 @@
|
||||
package cn.teammodel.ai.listener;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import cn.teammodel.ai.SseHelper;
|
||||
import cn.teammodel.ai.domain.SparkChatResponse;
|
||||
import cn.teammodel.common.ErrorCode;
|
||||
import cn.teammodel.config.exception.ServiceException;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import okhttp3.Response;
|
||||
import okhttp3.WebSocket;
|
||||
import okhttp3.WebSocketListener;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* okhttp 调用 ws 接口时的 listener
|
||||
* @author winter
|
||||
* @create 2023-12-15 16:17
|
||||
*/
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Data
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
public class SparkGptStreamListener extends WebSocketListener {
|
||||
private final SseEmitter sseEmitter;
|
||||
private String requestJson;
|
||||
/**
|
||||
* 模型响应的完整回复
|
||||
*/
|
||||
private String answer = "";
|
||||
|
||||
@Override
|
||||
public void onOpen(WebSocket webSocket, @NotNull Response response) {
|
||||
// ws 建立连接后发送请求的数据, 在 onMessage 中接受 server 的响应数据
|
||||
webSocket.send(requestJson);
|
||||
// 执行成功回调
|
||||
try {
|
||||
onOpen.accept(webSocket);
|
||||
} catch (Exception e) {
|
||||
this.onFailure(webSocket, e, response);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMessage(WebSocket webSocket, String text) {
|
||||
// 向 sse 推送消息(一次 onMessage 事件推送一次)
|
||||
SparkChatResponse sparkChatResponse = JSONUtil.toBean(text, SparkChatResponse.class);
|
||||
// 请求是否异常
|
||||
if (sparkChatResponse.getHeader().getCode() != 0) {
|
||||
this.onFailure(
|
||||
webSocket,
|
||||
new ServiceException(ErrorCode.SYSTEM_ERROR.getCode(),
|
||||
sparkChatResponse.getHeader().getMessage()),
|
||||
null
|
||||
);
|
||||
}
|
||||
// 推送回答 segment
|
||||
String msgSegment = sparkChatResponse.getPayload().getChoices().getText().get(0).getContent();
|
||||
// 处理消息格式(空格和换行符)
|
||||
// msgSegment = StrUtil.replace(msgSegment, " ", " ").replaceAll("\n", "\n");
|
||||
msgSegment = StrUtil.replace(msgSegment, " ", " ").replaceAll("\n", "<br>");
|
||||
answer += msgSegment;
|
||||
SseHelper.send(sseEmitter, msgSegment);
|
||||
|
||||
// 处理模型的最终响应
|
||||
if (sparkChatResponse.getHeader().getStatus() == 2) {
|
||||
// 其实 spark 会主动断开连接
|
||||
webSocket.close(1000, "done");
|
||||
onComplete.accept(answer);
|
||||
SseHelper.complete(sseEmitter);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(WebSocket webSocket, Throwable t, @Nullable Response response) {
|
||||
webSocket.close(1000, t.getMessage());
|
||||
this.onError.accept(t);
|
||||
// 失败时结束 sse 连接
|
||||
SseHelper.send(sseEmitter,t.getMessage() + "[DONE]");
|
||||
SseHelper.complete(sseEmitter);
|
||||
}
|
||||
|
||||
|
||||
// 这几个 function 可以在 listener 被调用时设置, 实现类似事件的回调(可以使用模板方法模式实现)
|
||||
protected Consumer<WebSocket> onOpen = (s) -> {};
|
||||
protected Consumer<Throwable> onError = (s) -> {};
|
||||
protected Consumer<String> onComplete = (s) -> {};
|
||||
}
|
@ -0,0 +1,46 @@
|
||||
package cn.teammodel.common;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* @author winter
|
||||
* @create 2024-02-01 15:30
|
||||
*/
|
||||
|
||||
public enum ChatAppScopeEnum {
|
||||
PUBLIC("public", "公共"),
|
||||
SCHOOL("school", "学校"),
|
||||
PRIVATE("private", "私人");
|
||||
private final String code;
|
||||
private final String name;
|
||||
public static List<String> codes() {
|
||||
return Arrays.stream(values()).map(ChatAppScopeEnum::getCode).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据 name 获取 code
|
||||
*/
|
||||
public static String getCodeByName(String name) {
|
||||
ChatAppScopeEnum chatAppScopeEnum = Arrays.stream(values()).filter(item -> item.getName().equals(name)).findFirst().orElse(null);
|
||||
String res = null;
|
||||
if (chatAppScopeEnum != null) {
|
||||
res = chatAppScopeEnum.getCode();
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
ChatAppScopeEnum(String code, String name) {
|
||||
this.code = code;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
package cn.teammodel.common;
|
||||
|
||||
/**
|
||||
* @author winter
|
||||
* @create 2023-11-29 14:26
|
||||
*/
|
||||
public interface CommonConstant {
|
||||
String DASH = "-";
|
||||
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
package cn.teammodel.common;
|
||||
|
||||
public enum ErrorCode {
|
||||
|
||||
SUCCESS(200, "ok"),
|
||||
PARAMS_ERROR(40000, "请求参数错误"),
|
||||
NOT_LOGIN_ERROR(40100, "未登录"),
|
||||
NO_AUTH_ERROR(40101, "无权限"),
|
||||
NOT_FOUND_ERROR(40400, "请求数据不存在"),
|
||||
FORBIDDEN_ERROR(40300, "禁止访问"),
|
||||
SYSTEM_ERROR(50000, "系统内部异常"),
|
||||
OPERATION_ERROR(50001, "操作失败");
|
||||
|
||||
/**
|
||||
* 状态码
|
||||
*/
|
||||
private final int code;
|
||||
|
||||
/**
|
||||
* 信息
|
||||
*/
|
||||
private final String message;
|
||||
|
||||
ErrorCode(int code, String message) {
|
||||
this.code = code;
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
public int getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,50 @@
|
||||
package cn.teammodel.common;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 五育枚举
|
||||
* @author winter
|
||||
* @create 2023-12-14 12:19
|
||||
*/
|
||||
public enum FiveEducations {
|
||||
VIRTUE("virtue", "德育"),
|
||||
INTELLIGENCE("intelligence","智育"),
|
||||
SPORTS("sports", "体育"),
|
||||
ART("art", "美育"),
|
||||
LABOUR("labour", "劳育");
|
||||
|
||||
private final String code;
|
||||
private final String name;
|
||||
|
||||
public static List<String> codes() {
|
||||
return Arrays.stream(values()).map(FiveEducations::getCode).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据 name 获取 code
|
||||
*/
|
||||
public static String getCodeByName(String name) {
|
||||
FiveEducations fiveEducation = Arrays.stream(values()).filter(item -> item.getName().equals(name)).findFirst().orElse(null);
|
||||
String res = null;
|
||||
if (fiveEducation != null) {
|
||||
res = fiveEducation.getCode();
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
FiveEducations(String code, String name) {
|
||||
this.code = code;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
package cn.teammodel.common;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
|
||||
/**
|
||||
* @author winter
|
||||
* @create 2023-12-04 9:55
|
||||
*/
|
||||
@Data
|
||||
public class IdRequest {
|
||||
@NotBlank(message = "id 不能为空")
|
||||
private String id;
|
||||
}
|
@ -0,0 +1,48 @@
|
||||
package cn.teammodel.common;
|
||||
|
||||
import com.azure.cosmos.models.PartitionKey;
|
||||
|
||||
/**
|
||||
* 分区键常量
|
||||
* @author winter
|
||||
* @create 2023-11-22 15:31
|
||||
*/
|
||||
public interface PK {
|
||||
/**
|
||||
* 评价树
|
||||
*/
|
||||
String PK_APPRAISE = "Appraise";
|
||||
/**
|
||||
* 学生的评价记录
|
||||
* 参数数量: 1 , 拼接: SchoolId
|
||||
*/
|
||||
String PK_APPRAISE_RECORD = "AppraiseRecord-%s";
|
||||
String COMMON_BASE = "Base";
|
||||
/**
|
||||
* 学生,拼接学校 id
|
||||
*/
|
||||
String STUDENT = "Base-%s";
|
||||
String CLASS = "Class-%s";
|
||||
String CHAT_SESSION = "ChatSession";
|
||||
String WEEK_DUTY = "Duty";
|
||||
String WEEK_DUTY_RECORD = "DutyRecord-%s";
|
||||
String CHAT_APP = "ChatApp";
|
||||
String NEWS = "News-%s";
|
||||
|
||||
/**
|
||||
* 构建分区键
|
||||
*/
|
||||
static PartitionKey of(String pk) {
|
||||
return new PartitionKey(pk);
|
||||
}
|
||||
|
||||
/**
|
||||
* 需要传参构建分区键<br/>
|
||||
* 注意: 参数的个数不会在编译时被校验,需自己注意
|
||||
*/
|
||||
static PartitionKey buildOf(String pk, String... args) {
|
||||
String pkStr = String.format(pk, args);
|
||||
return new PartitionKey(pkStr);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
package cn.teammodel.common;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author winter
|
||||
* @create 2023-12-08 17:30
|
||||
*/
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
public class PageVo<T> {
|
||||
private Integer totalPages;
|
||||
private Long totalItems;
|
||||
private List<T> content;
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
package cn.teammodel.common;
|
||||
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* @author winter
|
||||
* @create 2023-12-05 14:58
|
||||
*/
|
||||
@Data
|
||||
public class PageableRequest {
|
||||
@ApiModelProperty("当前页码, 默认: 0")
|
||||
private Integer current = 0;
|
||||
@ApiModelProperty("每页数量, 默认: 10")
|
||||
private Integer size = 10;
|
||||
}
|
@ -1,37 +1,48 @@
|
||||
package cn.teammodel.common;
|
||||
|
||||
public class R {
|
||||
private Integer code;
|
||||
private String message;
|
||||
private String data;
|
||||
|
||||
public R(Integer code, String message, String data) {
|
||||
this.code = code;
|
||||
this.message = message;
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
public Integer getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public void setCode(Integer code) {
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
public void setMessage(String message) {
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
public String getData() {
|
||||
return data;
|
||||
}
|
||||
|
||||
public void setData(String data) {
|
||||
this.data = data;
|
||||
}
|
||||
}
|
||||
package cn.teammodel.common;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
@Data
|
||||
public class R<T> implements Serializable {
|
||||
|
||||
private int code;
|
||||
|
||||
private T data;
|
||||
|
||||
private String message;
|
||||
|
||||
public R(int code, T data, String message) {
|
||||
this.code = code;
|
||||
this.data = data;
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
public R(int code, T data) {
|
||||
this(code, data, "");
|
||||
}
|
||||
|
||||
public R(ErrorCode errorCode) {
|
||||
this(errorCode.getCode(), null, errorCode.getMessage());
|
||||
}
|
||||
public R(ErrorCode errorCode, T data) {
|
||||
this(errorCode.getCode(), data, errorCode.getMessage());
|
||||
}
|
||||
|
||||
public static <T> R<T> success(T data) {
|
||||
return new R<>(ErrorCode.SUCCESS, data);
|
||||
}
|
||||
|
||||
public static <T> R<T> error(String msg) {
|
||||
return new R<>(ErrorCode.SYSTEM_ERROR.getCode(), null, msg);
|
||||
}
|
||||
|
||||
public static <T> R<T> error(Integer code, String msg) {
|
||||
return new R<>(code, null, msg);
|
||||
}
|
||||
|
||||
public static <T> R<T> error(ErrorCode errorCode) {
|
||||
return new R<>(errorCode);
|
||||
}
|
||||
}
|
@ -0,0 +1,41 @@
|
||||
package cn.teammodel.config;
|
||||
|
||||
import cn.teammodel.config.intercepter.EnhanceRequestServletFilter;
|
||||
import cn.teammodel.config.intercepter.UploadApiLogInterceptor;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.springframework.boot.web.servlet.FilterRegistrationBean;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
|
||||
/**
|
||||
* WebMvc配置 : 静态文件、拦截器等
|
||||
* @author winter
|
||||
* @create 2024-01-15 17:06
|
||||
*/
|
||||
@Configuration
|
||||
@RequiredArgsConstructor
|
||||
public class WebMvcConfig implements WebMvcConfigurer {
|
||||
private final UploadApiLogInterceptor uploadApiLogInterceptor;
|
||||
/**
|
||||
* 拦截器配置
|
||||
*/
|
||||
@Override
|
||||
public void addInterceptors(@NotNull InterceptorRegistry registry) {
|
||||
registry.addInterceptor(uploadApiLogInterceptor);
|
||||
}
|
||||
|
||||
// 注册过滤器
|
||||
@Bean
|
||||
public FilterRegistrationBean<EnhanceRequestServletFilter> filterRegistrationBean(){
|
||||
FilterRegistrationBean<EnhanceRequestServletFilter> filterRegistrationBean=new FilterRegistrationBean<>();
|
||||
filterRegistrationBean.setFilter(new EnhanceRequestServletFilter());
|
||||
filterRegistrationBean.addUrlPatterns("/*");
|
||||
//order的数值越小 则优先级越高,这里直接使用的最高优先级
|
||||
filterRegistrationBean.setOrder(Ordered.HIGHEST_PRECEDENCE);
|
||||
return filterRegistrationBean;
|
||||
}
|
||||
}
|
@ -0,0 +1,126 @@
|
||||
package cn.teammodel.config.exception;
|
||||
|
||||
import cn.teammodel.common.ErrorCode;
|
||||
import cn.teammodel.common.R;
|
||||
import cn.teammodel.manager.notification.NotificationService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.ObjectUtils;
|
||||
import org.springframework.http.converter.HttpMessageNotReadableException;
|
||||
import org.springframework.validation.BindException;
|
||||
import org.springframework.web.bind.MethodArgumentNotValidException;
|
||||
import org.springframework.web.bind.MissingPathVariableException;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.RestControllerAdvice;
|
||||
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
|
||||
/**
|
||||
* 全局异常处理器
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
@RestControllerAdvice
|
||||
@Slf4j
|
||||
public class GlobalExceptionHandler {
|
||||
|
||||
@Resource
|
||||
private NotificationService notificationService;
|
||||
/**
|
||||
* 业务异常
|
||||
*/
|
||||
@ExceptionHandler(ServiceException.class)
|
||||
public R<?> handleServiceException(ServiceException e, HttpServletRequest request)
|
||||
{
|
||||
log.error(e.getMessage(), e);
|
||||
Integer code = e.getCode();
|
||||
return ObjectUtils.isNotEmpty(code) ? R.error(code, e.getMessage()) : R.error(e.getMessage());
|
||||
}
|
||||
|
||||
@ExceptionHandler(HttpMessageNotReadableException.class)
|
||||
public R<?> handleHttpMsgNotReadableException(HttpMessageNotReadableException e, HttpServletRequest request)
|
||||
{
|
||||
log.error(e.getMessage(), e);
|
||||
return R.error(ErrorCode.PARAMS_ERROR.getCode(), "参数格式有误");
|
||||
}
|
||||
|
||||
/**
|
||||
* 请求路径中缺少必需的路径变量
|
||||
*/
|
||||
@ExceptionHandler(MissingPathVariableException.class)
|
||||
public R<?> handleMissingPathVariableException(MissingPathVariableException e, HttpServletRequest request)
|
||||
{
|
||||
String requestURI = request.getRequestURI();
|
||||
log.error("请求路径中缺少必需的路径变量'{}',发生系统异常.", requestURI, e);
|
||||
return R.error(String.format("请求路径中缺少必需的路径变量[%s]", e.getVariableName()));
|
||||
}
|
||||
|
||||
/**
|
||||
* 请求参数类型不匹配
|
||||
*/
|
||||
@ExceptionHandler(MethodArgumentTypeMismatchException.class)
|
||||
public R<?> handleMethodArgumentTypeMismatchException(MethodArgumentTypeMismatchException e, HttpServletRequest request)
|
||||
{
|
||||
String requestURI = request.getRequestURI();
|
||||
log.error("请求参数类型不匹配'{}',发生系统异常.", requestURI, e);
|
||||
return R.error(String.format("请求参数类型不匹配,参数[%s]要求类型为:'%s',但输入值为:'%s'", e.getName(), e.getRequiredType().getName(), e.getValue()));
|
||||
}
|
||||
|
||||
/**
|
||||
* 拦截未知的运行时异常
|
||||
*/
|
||||
@ExceptionHandler(RuntimeException.class)
|
||||
public R<?> handleRuntimeException(RuntimeException e, HttpServletRequest request)
|
||||
{
|
||||
String requestURI = request.getRequestURI();
|
||||
log.error("请求地址'{}',发生未知异常.", requestURI, e);
|
||||
notificationService.send("RuntimeException 告警: " + getCausesAsString(e, 5));
|
||||
return R.error(ErrorCode.SYSTEM_ERROR);
|
||||
}
|
||||
|
||||
/**
|
||||
* 系统异常
|
||||
*/
|
||||
@ExceptionHandler(Exception.class)
|
||||
public R<?> handleException(Exception e, HttpServletRequest request)
|
||||
{
|
||||
String requestURI = request.getRequestURI();
|
||||
log.error("请求地址'{}',发生系统异常.", requestURI, e);
|
||||
|
||||
notificationService.send("Exception 告警: " + getCausesAsString(e, 5));
|
||||
return R.error(e.getMessage());
|
||||
}
|
||||
|
||||
/**
|
||||
* 自定义验证异常
|
||||
*/
|
||||
@ExceptionHandler(BindException.class)
|
||||
public R<?> handleBindException(BindException e)
|
||||
{
|
||||
log.error(e.getMessage(), e);
|
||||
String message = e.getAllErrors().get(0).getDefaultMessage();
|
||||
return R.error(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* 自定义验证异常
|
||||
*/
|
||||
@ExceptionHandler(MethodArgumentNotValidException.class)
|
||||
public Object handleMethodArgumentNotValidException(MethodArgumentNotValidException e)
|
||||
{
|
||||
log.error(e.getMessage(), e);
|
||||
String message = Objects.requireNonNull(e.getBindingResult().getFieldError()).getDefaultMessage();
|
||||
return R.error(message);
|
||||
}
|
||||
private static String getCausesAsString(Throwable throwable, int maxDepth) {
|
||||
return Stream.iterate(throwable, Throwable::getCause)
|
||||
.limit(maxDepth)
|
||||
.map(e -> e.getClass().getSimpleName() + ": " + e.getMessage())
|
||||
.collect(Collectors.joining(" <- "));
|
||||
}
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
package cn.teammodel.config.ies;
|
||||
|
||||
import lombok.Getter;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
@Getter
|
||||
@Configuration
|
||||
public class IESConfig {
|
||||
@Value("${ies.server-url}")
|
||||
private String serverUrl;
|
||||
|
||||
}
|
@ -0,0 +1,144 @@
|
||||
package cn.teammodel.config.intercepter;
|
||||
|
||||
import cn.hutool.crypto.SecureUtil;
|
||||
import cn.hutool.extra.servlet.ServletUtil;
|
||||
import cn.hutool.jwt.JWTUtil;
|
||||
import cn.teammodel.manager.notification.NotificationService;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import okhttp3.*;
|
||||
import org.apache.commons.lang3.ObjectUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.servlet.HandlerInterceptor;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.time.Instant;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 上传接口调用日志拦截器
|
||||
* @author winter
|
||||
* @create 2024-03-18 10:00
|
||||
*/
|
||||
@Component
|
||||
@Slf4j
|
||||
public class UploadApiLogInterceptor implements HandlerInterceptor {
|
||||
@Value("${spring.env}")
|
||||
private String env;
|
||||
@Resource
|
||||
private NotificationService notificationService;
|
||||
|
||||
private final ObjectMapper mapper = new ObjectMapper();
|
||||
|
||||
@Override
|
||||
public boolean preHandle(@NotNull HttpServletRequest request, @NotNull HttpServletResponse response, @NotNull Object handler) throws Exception {
|
||||
if (!env.equals("dev")) {
|
||||
doUploadLog(request);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private void doUploadLog(@NotNull HttpServletRequest request) throws IOException {
|
||||
String ip = ServletUtil.getClientIP(request);
|
||||
String client = "";
|
||||
String tokenSha = "";
|
||||
String id = "";
|
||||
String name = "";
|
||||
String school = "";
|
||||
String scope = "";
|
||||
String referer = request.getHeader("Referer");
|
||||
Long requestTime = Instant.now().toEpochMilli();
|
||||
// 获取 json 请求参数(这里读了后,后面就再也读不到了,需要处理)
|
||||
BufferedReader streamReader = new BufferedReader(new InputStreamReader(request.getInputStream(), StandardCharsets.UTF_8));
|
||||
StringBuilder responseStrBuilder = new StringBuilder();
|
||||
String inputStr;
|
||||
while ((inputStr = streamReader.readLine()) != null)
|
||||
responseStrBuilder.append(inputStr);
|
||||
|
||||
JsonNode jsonNode = mapper.readTree(responseStrBuilder.toString());
|
||||
String xAuthToken = request.getHeader("x-auth-AuthToken");
|
||||
// 如果有 authorization
|
||||
String authorization = request.getHeader("Authorization");
|
||||
if (StringUtils.isNotBlank(authorization)) {
|
||||
authorization = authorization.replace("Bearer ", "");
|
||||
List<String> roles = (List<String>) JWTUtil.parseToken(authorization).getPayload("roles");
|
||||
if (ObjectUtils.isNotEmpty(roles)) {
|
||||
client = roles.get(0);
|
||||
}
|
||||
tokenSha = SecureUtil.sha1("Bearer " + authorization);
|
||||
}
|
||||
String xAuthIdToken = request.getHeader("X-Auth-IdToken");
|
||||
if (StringUtils.isNotBlank(xAuthIdToken)) {
|
||||
id = (String) JWTUtil.parseToken(xAuthIdToken).getPayload("sub");
|
||||
name = (String) JWTUtil.parseToken(xAuthIdToken).getPayload("name");
|
||||
if (StringUtils.isNotBlank(tokenSha)) {
|
||||
tokenSha = SecureUtil.sha1(xAuthIdToken);
|
||||
}
|
||||
}
|
||||
String xAuthSchool = request.getHeader("X-Auth-School");
|
||||
if (StringUtils.isNotBlank(xAuthSchool)) {
|
||||
school = xAuthSchool;
|
||||
}
|
||||
if (StringUtils.isNotBlank(xAuthToken)) {
|
||||
id = (String) JWTUtil.parseToken(xAuthToken).getPayload("sub");
|
||||
name = (String) JWTUtil.parseToken(xAuthToken).getPayload("name");
|
||||
school = (String) JWTUtil.parseToken(xAuthToken).getPayload("azp");
|
||||
scope = (String) JWTUtil.parseToken(xAuthToken).getPayload("scope");
|
||||
if (StringUtils.isNotBlank(tokenSha)) {
|
||||
tokenSha = SecureUtil.sha1(xAuthToken);
|
||||
}
|
||||
}
|
||||
|
||||
ObjectNode root = mapper.createObjectNode();
|
||||
root.put("ip", ip);
|
||||
root.put("time", requestTime);
|
||||
root.put("id", id);
|
||||
root.put("name", name);
|
||||
root.set("param", jsonNode);
|
||||
root.put("school", school);
|
||||
root.put("client", client);
|
||||
root.put("tid", tokenSha);
|
||||
root.put("scope", scope);
|
||||
root.put("path", request.getContextPath() + request.getRequestURI());
|
||||
root.put("host", request.getServerName());
|
||||
root.put("p", "appraisal");
|
||||
|
||||
// 发送请求
|
||||
OkHttpClient okHttpClient = new OkHttpClient();
|
||||
String requestData = root.toString();
|
||||
RequestBody requestBody = RequestBody.create(MediaType.parse("application/json"), requestData);
|
||||
Request okRequest = new Request.Builder()
|
||||
.url("http://cdhabook.teammodel.cn:8805/api/http-log")
|
||||
.post(requestBody)
|
||||
.build();
|
||||
|
||||
okHttpClient.newCall(okRequest).enqueue(new Callback() {
|
||||
@Override
|
||||
public void onFailure(Call call, IOException e) {
|
||||
log.error("UploadApiLogIntercepter error") ;
|
||||
notificationService.send("日志上传告警: 请求发送失败,检查请求发送客户端");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResponse(Call call, Response response) throws IOException {
|
||||
if (!response.isSuccessful()) {
|
||||
log.error("UploadApiLogIntercepter error" ) ;
|
||||
notificationService.send("日志上传告警: 请求响应异常,检查 API 源是否正常");
|
||||
} else {
|
||||
log.info("UploadApiLogIntercepter success");
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
package cn.teammodel.config.knife;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import springfox.documentation.builders.ApiInfoBuilder;
|
||||
import springfox.documentation.builders.PathSelectors;
|
||||
import springfox.documentation.builders.RequestHandlerSelectors;
|
||||
import springfox.documentation.spi.DocumentationType;
|
||||
import springfox.documentation.spring.web.plugins.Docket;
|
||||
import springfox.documentation.swagger2.annotations.EnableSwagger2;
|
||||
|
||||
/**
|
||||
* Knife4j 接口文档配置 <br/>
|
||||
* https://doc.xiaominfo.com/knife4j/documentation/get_start.html
|
||||
*/
|
||||
@Configuration
|
||||
@EnableSwagger2
|
||||
//@Profile({"dev", "test"})
|
||||
public class Knife4jConfig {
|
||||
|
||||
@Bean
|
||||
public Docket defaultApi2() {
|
||||
return new Docket(DocumentationType.SWAGGER_2)
|
||||
.apiInfo(new ApiInfoBuilder()
|
||||
.title("五育评价接口文档")
|
||||
.description("五育评价接口描述")
|
||||
.version("1.0.1")
|
||||
.build())
|
||||
.select()
|
||||
// 指定 Controller 扫描包路径
|
||||
.apis(RequestHandlerSelectors.basePackage("cn.teammodel.controller"))
|
||||
.paths(PathSelectors.any())
|
||||
.build();
|
||||
}
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
package cn.teammodel.config.redis;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
|
||||
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.data.redis.core.StringRedisTemplate;
|
||||
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
|
||||
import org.springframework.data.redis.serializer.StringRedisSerializer;
|
||||
|
||||
@Configuration
|
||||
public class RedisConfig {
|
||||
|
||||
|
||||
|
||||
@Bean
|
||||
public LettuceConnectionFactory dbConnectionFactory() {
|
||||
RedisStandaloneConfiguration config = new RedisStandaloneConfiguration("52.130.252.100", 6379);
|
||||
config.setDatabase(8); // 设置数据库编号为8
|
||||
config.setPassword("habook");
|
||||
return new LettuceConnectionFactory(config);
|
||||
}
|
||||
|
||||
|
||||
@Bean
|
||||
public StringRedisTemplate db1Template(LettuceConnectionFactory dbConnectionFactory) {
|
||||
StringRedisTemplate template = new StringRedisTemplate();
|
||||
template.setConnectionFactory(dbConnectionFactory);
|
||||
template.setKeySerializer(new StringRedisSerializer());
|
||||
template.setValueSerializer(new GenericJackson2JsonRedisSerializer());
|
||||
return template;
|
||||
}
|
||||
}
|
@ -1,16 +0,0 @@
|
||||
package cn.teammodel.controller;
|
||||
|
||||
import cn.teammodel.common.R;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/")
|
||||
public class HelloController {
|
||||
|
||||
@GetMapping("hello")
|
||||
public R helo() {
|
||||
return new R(200, "sucess","hello world");
|
||||
}
|
||||
}
|
@ -0,0 +1,39 @@
|
||||
package cn.teammodel.controller.admin.controller;
|
||||
|
||||
import cn.teammodel.common.R;
|
||||
import cn.teammodel.controller.admin.service.AdminAppraiseService;
|
||||
import cn.teammodel.model.dto.admin.appraise.UpdateAchievementRuleDto;
|
||||
import cn.teammodel.model.entity.appraise.AchievementRule;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import javax.validation.Valid;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author winter
|
||||
* @create 2023-12-13 16:07
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("admin/appraise")
|
||||
@Api(tags = "管理员端-学生评价管理")
|
||||
public class AdminAppraiseController {
|
||||
@Resource
|
||||
private AdminAppraiseService adminAppraiseService;
|
||||
|
||||
@PostMapping("updateAchieveRule")
|
||||
@ApiOperation("更新的 rule 节点将会直接覆盖老节点")
|
||||
public R<List<AchievementRule>> updateAchieveRule(@RequestBody @Valid UpdateAchievementRuleDto ruleDto) {
|
||||
List<AchievementRule> res = adminAppraiseService.updateAchieveRule(ruleDto);
|
||||
return R.success(res);
|
||||
}
|
||||
|
||||
@GetMapping("getAchieveRules/{periodId}")
|
||||
@ApiOperation("获取当前学段下的成就规则")
|
||||
public R<List<AchievementRule>> getAchieveRules(@PathVariable String periodId){
|
||||
List<AchievementRule> res = adminAppraiseService.getAchieveRules(periodId);
|
||||
return R.success(res);
|
||||
}
|
||||
}
|
@ -0,0 +1,80 @@
|
||||
package cn.teammodel.controller.admin.controller;
|
||||
|
||||
import cn.teammodel.common.R;
|
||||
import cn.teammodel.controller.admin.service.AdminIndexDutyService;
|
||||
import cn.teammodel.model.dto.admin.appraise.TimeRangeDto;
|
||||
import cn.teammodel.model.dto.admin.weekduty.AdminFindDutyRecordDto;
|
||||
import cn.teammodel.model.dto.weekDuty.LessonRecordDto;
|
||||
import cn.teammodel.model.vo.admin.AppraiseNodeRankVo;
|
||||
import cn.teammodel.model.vo.admin.DutyIndexData;
|
||||
import cn.teammodel.model.vo.admin.DutyNodeRankVo;
|
||||
import cn.teammodel.model.vo.admin.DutyRankPo;
|
||||
import cn.teammodel.model.vo.weekDuty.DutyRecordVo;
|
||||
import cn.teammodel.service.DutyService;
|
||||
import com.google.gson.JsonElement;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import org.springframework.security.core.context.SecurityContext;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.validation.Valid;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author winter
|
||||
* @create 2024-01-09 17:59
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("admin/duty")
|
||||
@Api(tags = "管理员端-值周巡检")
|
||||
public class AdminDutyController {
|
||||
@Resource
|
||||
private DutyService dutyService;
|
||||
|
||||
@Resource
|
||||
private AdminIndexDutyService adminIndexDutyService;
|
||||
|
||||
@PostMapping("records")
|
||||
@ApiOperation("获取班级评价数据")
|
||||
public R<List<DutyRecordVo>> findRecords(@Valid @RequestBody AdminFindDutyRecordDto adminFindDutyRecordDto) {
|
||||
List<DutyRecordVo> res = dutyService.findAdminRecords(adminFindDutyRecordDto);
|
||||
return R.success(res);
|
||||
}
|
||||
|
||||
@GetMapping("index/{periodId}")
|
||||
@ApiOperation("获取首页数据")
|
||||
public R<DutyIndexData> index(@PathVariable String periodId){
|
||||
DutyIndexData dutyIndexData = adminIndexDutyService.getIndexData(periodId);
|
||||
return R.success(dutyIndexData);
|
||||
}
|
||||
|
||||
@PostMapping("classRank")
|
||||
@ApiOperation("班级评价活跃排行榜: Top10")
|
||||
public R<List<DutyRankPo>> classRank(@Valid @RequestBody TimeRangeDto timeRangeDto) {
|
||||
List<DutyRankPo> res = adminIndexDutyService.classRank(timeRangeDto);
|
||||
return R.success(res);
|
||||
}
|
||||
|
||||
@PostMapping("teacherRank")
|
||||
@ApiOperation("老师评价活跃排行榜: Top10")
|
||||
public R<List<DutyRankPo>> teacherRank(@Valid @RequestBody TimeRangeDto timeRangeDto) {
|
||||
List<DutyRankPo> res = adminIndexDutyService.teacherRank(timeRangeDto);
|
||||
return R.success(res);
|
||||
}
|
||||
@PostMapping("dutyNodeRank")
|
||||
@ApiOperation("评价指标活跃排行榜: Top10")
|
||||
public R<List<DutyNodeRankVo>> appraiseNodeRank(@Valid @RequestBody TimeRangeDto timeRangeDto) {
|
||||
List<DutyNodeRankVo> res = adminIndexDutyService.appraiseNodeRank(timeRangeDto);
|
||||
return R.success(res);
|
||||
}
|
||||
@PostMapping("getLessonRecord")
|
||||
@ApiOperation("获取课堂记录")
|
||||
public R<Map<String, Object> > appraiseNodeRank(@Valid @RequestBody LessonRecordDto lessonRecordDto , HttpServletRequest request) {
|
||||
Map<String, Object> res = adminIndexDutyService.getLessonRecord(lessonRecordDto,request);
|
||||
return R.success(res);
|
||||
}
|
||||
}
|
@ -0,0 +1,39 @@
|
||||
package cn.teammodel.controller.admin.controller;
|
||||
|
||||
import cn.teammodel.common.R;
|
||||
import cn.teammodel.controller.admin.service.ArtService;
|
||||
import cn.teammodel.model.dto.admin.art.ArtFindDto;
|
||||
import cn.teammodel.model.dto.admin.common.GroupDto;
|
||||
import cn.teammodel.model.dto.admin.common.RGroupList;
|
||||
import cn.teammodel.model.vo.admin.ArtElementsVo;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.validation.Valid;
|
||||
import java.util.List;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("admin/art")
|
||||
@Api(tags = "管理员端-艺术评测")
|
||||
public class ArtController {
|
||||
|
||||
@Resource
|
||||
private ArtService ArtService;
|
||||
|
||||
@PostMapping("getArtList")
|
||||
@ApiOperation("获取当前学校艺术评测列表")
|
||||
public R<List<ArtElementsVo>> findRecords(@Valid @RequestBody ArtFindDto artFindDto,HttpServletRequest request) {
|
||||
List<ArtElementsVo> res = ArtService.getArtList(artFindDto,request);
|
||||
return R.success(res);
|
||||
}
|
||||
@PostMapping("getGroupList")
|
||||
@ApiOperation("获取当前学校指定成员名单详细信息")
|
||||
public R<List<RGroupList>> findGroups(@Valid @RequestBody GroupDto groupDto, HttpServletRequest request) {
|
||||
List<RGroupList> res = ArtService.getGroupList(groupDto,request);
|
||||
return R.success(res);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
package cn.teammodel.controller.admin.controller;
|
||||
|
||||
import cn.teammodel.common.R;
|
||||
import cn.teammodel.controller.admin.service.ArtService;
|
||||
import cn.teammodel.controller.admin.service.CommonService;
|
||||
import cn.teammodel.model.dto.admin.art.ArtFindDto;
|
||||
import cn.teammodel.model.dto.admin.common.GCDto;
|
||||
import cn.teammodel.model.vo.admin.ArtElementsVo;
|
||||
import cn.teammodel.model.vo.admin.GradeAndClassVo;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
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;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.validation.Valid;
|
||||
import java.util.List;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("admin/common")
|
||||
@Api(tags = "管理员端-公共组件")
|
||||
public class CommonController {
|
||||
|
||||
@Resource
|
||||
private CommonService commonService ;
|
||||
@PostMapping("getGradeAndClass")
|
||||
@ApiOperation("获取当前学校当前学段年级班级信息")
|
||||
public R<List<GradeAndClassVo>> findRecords(@Valid @RequestBody GCDto gcDto) {
|
||||
List<GradeAndClassVo> res = commonService.getGradeAndClass(gcDto);
|
||||
return R.success(res);
|
||||
}
|
||||
}
|
@ -0,0 +1,68 @@
|
||||
package cn.teammodel.controller.admin.controller;
|
||||
|
||||
import cn.teammodel.common.R;
|
||||
import cn.teammodel.controller.admin.service.AdminAppraiseService;
|
||||
import cn.teammodel.model.dto.admin.appraise.TimeRangeDto;
|
||||
import cn.teammodel.model.vo.admin.*;
|
||||
import cn.teammodel.model.vo.appraise.RecordVo;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import javax.validation.Valid;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 管理员首页
|
||||
* @author winter
|
||||
* @create 2023-12-06 14:37
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("admin/index")
|
||||
@Api(tags = "管理员端-首页数据")
|
||||
public class IndexController {
|
||||
@Resource
|
||||
private AdminAppraiseService adminAppraiseService;
|
||||
|
||||
|
||||
@GetMapping("/{periodId}")
|
||||
@ApiOperation("获取首页数据")
|
||||
public R<AppraiseIndexData> index(@PathVariable String periodId){
|
||||
AppraiseIndexData appraiseIndexData = adminAppraiseService.getIndexData(periodId);
|
||||
return R.success(appraiseIndexData);
|
||||
}
|
||||
|
||||
@PostMapping("latestRecord")
|
||||
@ApiOperation("查询时间范围最近评价(无参则当前周的所有记录)")
|
||||
public R<List<RecordVo>> latestRecord(@Valid @RequestBody TimeRangeDto timeRangeDto) {
|
||||
List<RecordVo> res = adminAppraiseService.conditionLatestRecord(timeRangeDto);
|
||||
return R.success(res);
|
||||
}
|
||||
|
||||
@PostMapping("classRank")
|
||||
@ApiOperation("班级评价活跃排行榜: Top10")
|
||||
public R<List<RankPo>> classRank(@Valid @RequestBody TimeRangeDto timeRangeDto) {
|
||||
List<RankPo> res = adminAppraiseService.classRank(timeRangeDto);
|
||||
return R.success(res);
|
||||
}
|
||||
@PostMapping("studentRank")
|
||||
@ApiOperation("学生评价活跃排行榜: Top10")
|
||||
public R<List<StudentRankVo>> studentRank(@Valid @RequestBody TimeRangeDto timeRangeDto) {
|
||||
List<StudentRankVo> res = adminAppraiseService.studentRank(timeRangeDto);
|
||||
return R.success(res);
|
||||
}
|
||||
@PostMapping("teacherRank")
|
||||
@ApiOperation("老师评价活跃排行榜: Top10")
|
||||
public R<List<RankVo>> teacherRank(@Valid @RequestBody TimeRangeDto timeRangeDto) {
|
||||
List<RankVo> res = adminAppraiseService.teacherRank(timeRangeDto);
|
||||
return R.success(res);
|
||||
}
|
||||
@PostMapping("appraiseNodeRank")
|
||||
@ApiOperation("评价指标活跃排行榜: Top10")
|
||||
public R<List<AppraiseNodeRankVo>> appraiseNodeRank(@Valid @RequestBody TimeRangeDto timeRangeDto) {
|
||||
List<AppraiseNodeRankVo> res = adminAppraiseService.appraiseNodeRank(timeRangeDto);
|
||||
return R.success(res);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
package cn.teammodel.controller.admin.controller;
|
||||
|
||||
import cn.teammodel.common.R;
|
||||
import cn.teammodel.test.RedisService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.validation.Valid;
|
||||
import java.util.Map;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/test")
|
||||
public class RedisController {
|
||||
|
||||
private final RedisService redisService;
|
||||
|
||||
@Autowired
|
||||
public RedisController(RedisService redisService) {
|
||||
this.redisService = redisService;
|
||||
}
|
||||
@PostMapping("/redis")
|
||||
public Map<Object, Object> getValueByKey(@RequestBody @Valid String key) {
|
||||
Map<Object,Object> juri = redisService.getValueByKey(key);
|
||||
return R.success(juri).getData();
|
||||
}
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
package cn.teammodel.controller.admin.service;
|
||||
|
||||
import cn.teammodel.model.dto.admin.appraise.TimeRangeDto;
|
||||
import cn.teammodel.model.dto.admin.appraise.UpdateAchievementRuleDto;
|
||||
import cn.teammodel.model.entity.appraise.AchievementRule;
|
||||
import cn.teammodel.model.vo.admin.*;
|
||||
import cn.teammodel.model.vo.appraise.RecordVo;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author winter
|
||||
* @create 2023-12-06 14:45
|
||||
*/
|
||||
public interface AdminAppraiseService {
|
||||
AppraiseIndexData getIndexData(String periodId);
|
||||
|
||||
/**
|
||||
* 按时期分页获取最新的评价数据
|
||||
*/
|
||||
List<RecordVo> conditionLatestRecord(TimeRangeDto timeRangeDto);
|
||||
|
||||
List<RankPo> classRank(TimeRangeDto timeRangeDto);
|
||||
|
||||
List<RankVo> teacherRank(TimeRangeDto timeRangeDto);
|
||||
|
||||
List<AppraiseNodeRankVo> appraiseNodeRank(TimeRangeDto timeRangeDto);
|
||||
|
||||
List<StudentRankVo> studentRank(TimeRangeDto timeRangeDto);
|
||||
|
||||
List<AchievementRule> updateAchieveRule(UpdateAchievementRuleDto ruleDto);
|
||||
|
||||
List<AchievementRule> getAchieveRules(String periodId);
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
package cn.teammodel.controller.admin.service;
|
||||
|
||||
import cn.teammodel.model.dto.admin.appraise.TimeRangeDto;
|
||||
import cn.teammodel.model.dto.weekDuty.LessonRecordDto;
|
||||
import cn.teammodel.model.vo.admin.DutyIndexData;
|
||||
import cn.teammodel.model.vo.admin.DutyNodeRankVo;
|
||||
import cn.teammodel.model.vo.admin.DutyRankPo;
|
||||
import com.google.gson.JsonElement;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author winter
|
||||
* @create 2024-02-28 15:07
|
||||
*/
|
||||
public interface AdminIndexDutyService {
|
||||
DutyIndexData getIndexData(String periodId);
|
||||
|
||||
List<DutyRankPo> classRank(TimeRangeDto timeRangeDto);
|
||||
|
||||
List<DutyRankPo> teacherRank(TimeRangeDto timeRangeDto);
|
||||
|
||||
List<DutyNodeRankVo> appraiseNodeRank(TimeRangeDto timeRangeDto);
|
||||
|
||||
Map<String, Object> getLessonRecord (LessonRecordDto lessonRecordDto, HttpServletRequest request);
|
||||
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
package cn.teammodel.controller.admin.service;
|
||||
|
||||
import cn.teammodel.model.dto.admin.art.ArtFindDto;
|
||||
import cn.teammodel.model.dto.admin.common.GroupDto;
|
||||
import cn.teammodel.model.dto.admin.common.RGroupList;
|
||||
import cn.teammodel.model.vo.admin.ArtElementsVo;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.util.List;
|
||||
|
||||
public interface ArtService {
|
||||
List<ArtElementsVo> getArtList(ArtFindDto artFindDto, HttpServletRequest request);
|
||||
List<RGroupList> getGroupList(GroupDto groupDto, HttpServletRequest request);
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
package cn.teammodel.controller.admin.service;
|
||||
|
||||
import cn.teammodel.model.dto.admin.common.GCDto;
|
||||
import cn.teammodel.model.vo.admin.GradeAndClassVo;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.List;
|
||||
public interface CommonService {
|
||||
List<GradeAndClassVo> getGradeAndClass(GCDto gcDto);
|
||||
}
|
@ -0,0 +1,403 @@
|
||||
package cn.teammodel.controller.admin.service.impl;
|
||||
|
||||
import cn.teammodel.common.ErrorCode;
|
||||
import cn.teammodel.common.PK;
|
||||
import cn.teammodel.config.exception.ServiceException;
|
||||
import cn.teammodel.controller.admin.service.AdminAppraiseService;
|
||||
import cn.teammodel.repository.*;
|
||||
import cn.teammodel.model.dto.admin.appraise.TimeRangeDto;
|
||||
import cn.teammodel.model.dto.admin.appraise.UpdateAchievementRuleDto;
|
||||
import cn.teammodel.model.entity.User;
|
||||
import cn.teammodel.model.entity.appraise.AchievementRule;
|
||||
import cn.teammodel.model.entity.appraise.Appraise;
|
||||
import cn.teammodel.model.entity.appraise.AppraiseTreeNode;
|
||||
import cn.teammodel.model.entity.school.ClassInfo;
|
||||
import cn.teammodel.model.entity.school.School;
|
||||
import cn.teammodel.model.entity.school.Student;
|
||||
import cn.teammodel.model.entity.school.Teacher;
|
||||
import cn.teammodel.model.vo.admin.*;
|
||||
import cn.teammodel.model.vo.appraise.RecordVo;
|
||||
import cn.teammodel.security.utils.SecurityUtil;
|
||||
import cn.teammodel.utils.RepositoryUtil;
|
||||
import cn.teammodel.utils.SchoolDateUtil;
|
||||
import com.azure.cosmos.models.CosmosPatchOperations;
|
||||
import com.azure.cosmos.models.PartitionKey;
|
||||
import com.azure.spring.data.cosmos.core.query.CosmosPageRequest;
|
||||
import org.apache.commons.lang3.ObjectUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.data.domain.Slice;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.time.*;
|
||||
import java.time.temporal.TemporalAdjusters;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static cn.teammodel.utils.SchoolDateUtil.calculateWeekNum;
|
||||
|
||||
/**
|
||||
* @author winter
|
||||
* @create 2023-12-06 14:46
|
||||
*/
|
||||
@Service
|
||||
public class AdminAppraiseServiceImpl implements AdminAppraiseService {
|
||||
@Resource
|
||||
private SchoolRepository schoolRepository;
|
||||
@Resource
|
||||
private ClassRepository classRepository;
|
||||
@Resource
|
||||
private TeacherRepository teacherRepository;
|
||||
@Resource
|
||||
private AppraiseRepository appraiseRepository;
|
||||
@Resource
|
||||
private StudentRepository studentRepository;
|
||||
@Resource
|
||||
private AppraiseRecordRepository appraiseRecordRepository;
|
||||
|
||||
@Override
|
||||
public AppraiseIndexData getIndexData(String periodId) {
|
||||
final int SLICE_SIZE = 100;
|
||||
if (StringUtils.isBlank(periodId)) {
|
||||
throw new ServiceException(ErrorCode.PARAMS_ERROR.getCode(), "不能为空");
|
||||
}
|
||||
|
||||
final AppraiseIndexData appraiseIndexData = new AppraiseIndexData();
|
||||
int totalCount = 0;
|
||||
int criticalCount = 0;
|
||||
Set<String> creatorIdSet = new HashSet<>();
|
||||
Set<String> studentSet = new HashSet<>();
|
||||
User loginUser = SecurityUtil.getLoginUser();
|
||||
String schoolId = loginUser.getSchoolId();
|
||||
|
||||
// 获取学期起止时间
|
||||
List<School.Semester> semesters = schoolRepository.findSemestersById(schoolId, periodId);
|
||||
SchoolDateUtil.semesterModel semesterModel = SchoolDateUtil.getSemesterByNow(semesters, LocalDate.now());
|
||||
String academicYearId = semesterModel.getAcademicYearId();
|
||||
LocalDateTime startDatetime = semesterModel.getStartDatetime();
|
||||
LocalDateTime endDatetime = semesterModel.getEndDatetime();
|
||||
if (startDatetime == null || endDatetime == null) throw new ServiceException(ErrorCode.PARAMS_ERROR);
|
||||
long totalWeek = calculateWeekNum(startDatetime, endDatetime, null);
|
||||
|
||||
// slice 分段读取
|
||||
CosmosPageRequest pageRequest = new CosmosPageRequest(0, SLICE_SIZE, null);
|
||||
Slice<RecordVo> slice;
|
||||
Map<Long, Integer> countByWeek = SchoolDateUtil.createEmptyWeekMap(totalWeek);
|
||||
|
||||
do {
|
||||
slice = appraiseRecordRepository.findAllByAcademicYearId(String.format(PK.PK_APPRAISE_RECORD, schoolId), academicYearId, pageRequest);
|
||||
List<RecordVo> content = slice.getContent();
|
||||
if (ObjectUtils.isEmpty(content)) {
|
||||
break;
|
||||
}
|
||||
|
||||
// 分批次计算
|
||||
for (RecordVo item : content) {
|
||||
// 处理每周的评价数
|
||||
long weekNum = calculateWeekNum(startDatetime, endDatetime, item.getCreateTime());
|
||||
countByWeek.put(weekNum, countByWeek.getOrDefault(weekNum, 0) + 1);
|
||||
// 处理总评价数
|
||||
totalCount++;
|
||||
// 处理批评数
|
||||
if (!item.isPraise()) {
|
||||
criticalCount++;
|
||||
}
|
||||
// 处理已评价老师总数
|
||||
creatorIdSet.add(item.getCreatorId());
|
||||
// 处理被评价的学生数
|
||||
if ("student".equals(item.getTargetType())) {
|
||||
studentSet.add(item.getTargetId());
|
||||
}
|
||||
}
|
||||
|
||||
if (slice.hasNext()) {
|
||||
pageRequest = (CosmosPageRequest) slice.nextPageable();
|
||||
}
|
||||
} while (slice.hasNext());
|
||||
|
||||
// 组装数据
|
||||
appraiseIndexData.setCountByWeek(countByWeek);
|
||||
appraiseIndexData.setTotalCount(totalCount);
|
||||
appraiseIndexData.setCriticalCount(criticalCount);
|
||||
appraiseIndexData.setPraiseCount(totalCount - criticalCount);
|
||||
appraiseIndexData.setTeacherCount(creatorIdSet.size());
|
||||
appraiseIndexData.setStudentCount(studentSet.size());
|
||||
return appraiseIndexData;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<RecordVo> conditionLatestRecord(TimeRangeDto timeRangeDto) {
|
||||
Long startTime = timeRangeDto.getStartTime();
|
||||
Long endTime = timeRangeDto.getEndTime();
|
||||
String academicYearId = timeRangeDto.getAcademicYearId();
|
||||
String schoolId = SecurityUtil.getLoginUser().getSchoolId();
|
||||
|
||||
// fixme: 是否对时间范围做一些限制(不能确保当前周有数据)
|
||||
// 无参默认当前周
|
||||
if (startTime == null || endTime == null) {
|
||||
// 将时间范围调整为当前周的周一到当前时间
|
||||
LocalDateTime mondayOfCurWeek = LocalDateTime.now().with(TemporalAdjusters.previousOrSame(DayOfWeek.MONDAY))
|
||||
.withHour(0)
|
||||
.withMinute(0)
|
||||
.withSecond(0)
|
||||
.withNano(0);
|
||||
startTime = mondayOfCurWeek.atZone(ZoneOffset.systemDefault()).toInstant().toEpochMilli();
|
||||
endTime = Instant.now().toEpochMilli();
|
||||
}
|
||||
|
||||
List<RecordVo> res = appraiseRecordRepository.latestRecords(
|
||||
String.format(PK.PK_APPRAISE_RECORD, schoolId),
|
||||
academicYearId,
|
||||
startTime,
|
||||
endTime
|
||||
);
|
||||
|
||||
if (res != null) {
|
||||
res = res.stream().sorted((o1, o2) -> o2.getCreateTime().compareTo(o1.getCreateTime())).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<RankPo> classRank(TimeRangeDto timeRangeDto) {
|
||||
// todo 不要 page| 批评和表扬数量
|
||||
Long startTime = timeRangeDto.getStartTime();
|
||||
Long endTime = timeRangeDto.getEndTime();
|
||||
String academicYearId = timeRangeDto.getAcademicYearId();
|
||||
String schoolId = SecurityUtil.getLoginUser().getSchoolId();
|
||||
|
||||
List<RankPo> rankPoList = appraiseRecordRepository.classRank(
|
||||
String.format(PK.PK_APPRAISE_RECORD, schoolId),
|
||||
academicYearId,
|
||||
startTime,
|
||||
endTime
|
||||
);
|
||||
|
||||
if (ObjectUtils.isEmpty(rankPoList)) return null;
|
||||
Set<String> classIdSet = rankPoList.stream().map(RankPo::getId).collect(Collectors.toSet());
|
||||
// 注意: 如果查询 in 的查询集在数据库中不存在,则在结果集也不会为 null.
|
||||
if (ObjectUtils.isEmpty(classIdSet)) return rankPoList;
|
||||
List<ClassInfo> classes = classRepository.findAllByCodeAndIdIn(String.format(PK.CLASS, schoolId), classIdSet);
|
||||
Map<String, String> idNameMap = classes.stream().collect(Collectors.toMap(ClassInfo::getId, ClassInfo::getName));
|
||||
rankPoList.forEach(rankVo -> rankVo.setName(idNameMap.get(rankVo.getId())));
|
||||
return rankPoList;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<RankVo> teacherRank(TimeRangeDto timeRangeDto) {
|
||||
Long startTime = timeRangeDto.getStartTime();
|
||||
Long endTime = timeRangeDto.getEndTime();
|
||||
String academicYearId = timeRangeDto.getAcademicYearId();
|
||||
String schoolId = SecurityUtil.getLoginUser().getSchoolId();
|
||||
|
||||
List<RankPo> rankPoList = appraiseRecordRepository.teacherRank(
|
||||
String.format(PK.PK_APPRAISE_RECORD, schoolId),
|
||||
academicYearId,
|
||||
startTime,
|
||||
endTime
|
||||
);
|
||||
// 根据 id 分组
|
||||
Map<String, List<RankPo>> idRankPoMap = rankPoList.stream().collect(Collectors.groupingBy(RankPo::getId));
|
||||
final List<RankVo> rankVos = new ArrayList<>();
|
||||
|
||||
idRankPoMap.forEach((id, list) -> {
|
||||
RankVo rankVo = new RankVo();
|
||||
int praiseCount = 0;
|
||||
int criticalCount = 0;
|
||||
rankVo.setId(id);
|
||||
|
||||
for (RankPo po : list) {
|
||||
if (po.getIsPraise()) {
|
||||
praiseCount += po.getCount();
|
||||
} else {
|
||||
criticalCount += po.getCount();
|
||||
}
|
||||
}
|
||||
rankVo.setPraiseCount(praiseCount);
|
||||
rankVo.setCriticalCount(criticalCount);
|
||||
rankVo.setTotalCount(praiseCount + criticalCount);
|
||||
rankVos.add(rankVo);
|
||||
});
|
||||
// 排序
|
||||
List<RankVo> res = rankVos.stream()
|
||||
.sorted(Comparator.comparing(RankVo::getTotalCount).reversed())
|
||||
.collect(Collectors.toList());
|
||||
// 设置 name
|
||||
if (ObjectUtils.isEmpty(res)) return null;
|
||||
Set<String> teacherIdSet = res.stream().map(RankVo::getId).collect(Collectors.toSet());
|
||||
if (ObjectUtils.isEmpty(teacherIdSet)) return res;
|
||||
List<Teacher> teachers = teacherRepository.findAllByCodeAndIdIn(PK.COMMON_BASE, teacherIdSet);
|
||||
Map<String, String> idNameMap = teachers.stream().collect(Collectors.toMap(Teacher::getId, Teacher::getName));
|
||||
res.forEach(rankVo -> rankVo.setName(idNameMap.get(rankVo.getId())));
|
||||
return res;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<AppraiseNodeRankVo> appraiseNodeRank(TimeRangeDto timeRangeDto) {
|
||||
Long startTime = timeRangeDto.getStartTime();
|
||||
Long endTime = timeRangeDto.getEndTime();
|
||||
String academicYearId = timeRangeDto.getAcademicYearId();
|
||||
String schoolId = SecurityUtil.getLoginUser().getSchoolId();
|
||||
|
||||
List<AppraiseNodeRankVo> rankVoList = appraiseRecordRepository.appraiseNodeRank(
|
||||
String.format(PK.PK_APPRAISE_RECORD, schoolId),
|
||||
academicYearId,
|
||||
startTime,
|
||||
endTime
|
||||
);
|
||||
|
||||
if (rankVoList == null) return null;
|
||||
List<String> names = rankVoList.stream().map(AppraiseNodeRankVo::getName).collect(Collectors.toList());
|
||||
if (ObjectUtils.isEmpty(names)) return null;
|
||||
// 去重后的 nodes
|
||||
List<AppraiseTreeNode> nodesByName = appraiseRecordRepository.findAppraiseRecordInNames(String.format(PK.PK_APPRAISE_RECORD, schoolId), academicYearId, names);
|
||||
// 正常情况下 name 一一对应 todo: 临时解决
|
||||
Map<String, AppraiseTreeNode> nameNodeMap = nodesByName.stream()
|
||||
.collect(Collectors.toMap(AppraiseTreeNode::getName, item -> item, (existing, replacement) -> {
|
||||
if (replacement.getPath() != null) {
|
||||
return replacement;
|
||||
}
|
||||
return existing;
|
||||
}));
|
||||
|
||||
rankVoList = rankVoList.stream()
|
||||
.sorted(Comparator.comparing(RankPo::getCount).reversed())
|
||||
// 流中对元素操作但不改变流
|
||||
.peek(s -> {
|
||||
AppraiseTreeNode node = nameNodeMap.get(s.getName());
|
||||
if (node != null) {
|
||||
s.setPath(node.getPath());
|
||||
s.setIsPraise(node.isPraise());
|
||||
}
|
||||
})
|
||||
.collect(Collectors.toList());
|
||||
return rankVoList;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<StudentRankVo> studentRank(TimeRangeDto timeRangeDto) {
|
||||
Long startTime = timeRangeDto.getStartTime();
|
||||
Long endTime = timeRangeDto.getEndTime();
|
||||
String academicYearId = timeRangeDto.getAcademicYearId();
|
||||
String schoolId = SecurityUtil.getLoginUser().getSchoolId();
|
||||
|
||||
List<RankPo> rankPoList = appraiseRecordRepository.studentRank(
|
||||
String.format(PK.PK_APPRAISE_RECORD, schoolId),
|
||||
academicYearId,
|
||||
startTime,
|
||||
endTime
|
||||
);
|
||||
// 根据 id 分组
|
||||
Map<String, List<RankPo>> idRankPoMap = rankPoList.stream().collect(Collectors.groupingBy(RankPo::getId));
|
||||
final List<StudentRankVo> rankVos = new ArrayList<>();
|
||||
|
||||
idRankPoMap.forEach((id, list) -> {
|
||||
StudentRankVo rankVo = new StudentRankVo();
|
||||
int praiseCount = 0;
|
||||
int criticalCount = 0;
|
||||
rankVo.setId(id);
|
||||
|
||||
for (RankPo po : list) {
|
||||
if (po.getIsPraise()) {
|
||||
praiseCount += po.getCount();
|
||||
} else {
|
||||
criticalCount += po.getCount();
|
||||
}
|
||||
}
|
||||
rankVo.setPraiseCount(praiseCount);
|
||||
rankVo.setCriticalCount(criticalCount);
|
||||
rankVo.setTotalCount(praiseCount + criticalCount);
|
||||
rankVos.add(rankVo);
|
||||
});
|
||||
// 排序
|
||||
List<StudentRankVo> res = rankVos.stream()
|
||||
.sorted(Comparator.comparing(StudentRankVo::getTotalCount).reversed())
|
||||
.collect(Collectors.toList());
|
||||
// 设置 student name
|
||||
if (ObjectUtils.isEmpty(res)) return null;
|
||||
Set<String> studentIdSet = res.stream().map(StudentRankVo::getId).collect(Collectors.toSet());
|
||||
if (ObjectUtils.isEmpty(studentIdSet)) return res;
|
||||
|
||||
List<Student> students = studentRepository.findAllByCodeAndIdIn(String.format(PK.STUDENT, schoolId), studentIdSet);
|
||||
// 利用数组作为三元组,提取 Student 中的 name 和 classId, picture
|
||||
Map<String, String[]> idNameMap = students.stream().collect(Collectors.toMap(Student::getId, item -> {
|
||||
String[] studentInfo = new String[3];
|
||||
studentInfo[0] = item.getName();
|
||||
studentInfo[1] = item.getClassId();
|
||||
studentInfo[2] = item.getPicture();
|
||||
return studentInfo;
|
||||
}));
|
||||
res.forEach(rankVo -> {
|
||||
rankVo.setName(idNameMap.get(rankVo.getId())[0]);
|
||||
rankVo.setClassName(idNameMap.get(rankVo.getId())[1]);
|
||||
rankVo.setPicture(idNameMap.get(rankVo.getId())[2]);
|
||||
});
|
||||
|
||||
// 设置 class name
|
||||
Set<String> classIds = students.stream().map(Student::getClassId).collect(Collectors.toSet());
|
||||
List<ClassInfo> classes = classRepository.findAllByCodeAndIdIn(String.format(PK.CLASS, schoolId), classIds);
|
||||
Map<String, String> idClassNameMap = classes.stream().collect(Collectors.toMap(ClassInfo::getId, ClassInfo::getName));
|
||||
res.forEach(rankVo -> rankVo.setClassName(idClassNameMap.getOrDefault(rankVo.getClassName(), null)));
|
||||
return res;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<AchievementRule> updateAchieveRule(UpdateAchievementRuleDto ruleDto) {
|
||||
String periodId = ruleDto.getPeriodId();
|
||||
UpdateAchievementRuleDto.UpdateRule updateRule = ruleDto.getUpdateRule();
|
||||
|
||||
if (ObjectUtils.isEmpty(updateRule) || StringUtils.isBlank(updateRule.getId()) || ObjectUtils.isEmpty(updateRule.getName())) {
|
||||
throw new ServiceException(ErrorCode.PARAMS_ERROR.getCode(), "rule id/name 不能为空");
|
||||
}
|
||||
if (updateRule.getLevelCount() == null || updateRule.getLevelCount() <= 0 || updateRule.getPromotionLevel() == null || updateRule.getPromotionLevel() <= 0) {
|
||||
throw new ServiceException(ErrorCode.PARAMS_ERROR.getCode(), "规则不能为空或小于等于0");
|
||||
}
|
||||
User user = SecurityUtil.getLoginUser();
|
||||
String schoolId = user.getSchoolId();
|
||||
|
||||
Appraise appraise = RepositoryUtil.findOne(appraiseRepository.findRulesById(schoolId, periodId), "参数错误,找不到该学段下的评价规则");
|
||||
List<AchievementRule> rules = appraise.getAchievementRules();
|
||||
if (ObjectUtils.isEmpty(rules)) {
|
||||
throw new ServiceException(ErrorCode.OPERATION_ERROR.getCode(), "该学段暂无没有成就规则");
|
||||
}
|
||||
// sort rules by level
|
||||
rules = rules.stream().sorted(Comparator.comparing(AchievementRule::getLevel)).collect(Collectors.toList());
|
||||
boolean flag = false;
|
||||
int lastPromotionCount = 0;
|
||||
for (int i = 0, rulesSize = rules.size(); i < rulesSize; i++) {
|
||||
AchievementRule rule = rules.get(i);
|
||||
if (updateRule.getId().equals(rule.getId())) {
|
||||
BeanUtils.copyProperties(updateRule, rule);
|
||||
lastPromotionCount = rule.getLevelCount() * rule.getPromotionLevel();
|
||||
rule.setPromotionCount(lastPromotionCount);
|
||||
flag = true;
|
||||
continue;
|
||||
}
|
||||
// 处理后面的节点,将 promotionCount 依次修改
|
||||
if (flag) {
|
||||
lastPromotionCount = lastPromotionCount + rule.getLevelCount() * rule.getPromotionLevel();
|
||||
rule.setPromotionCount(lastPromotionCount);
|
||||
}
|
||||
}
|
||||
|
||||
CosmosPatchOperations operations = CosmosPatchOperations.create().replace("/achievementRules", rules);
|
||||
Appraise saved = appraiseRepository.save(appraise.getId(), new PartitionKey(PK.PK_APPRAISE), Appraise.class, operations);
|
||||
return saved.getAchievementRules();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<AchievementRule> getAchieveRules(String periodId) {
|
||||
User user = SecurityUtil.getLoginUser();
|
||||
String schoolId = user.getSchoolId();
|
||||
|
||||
Appraise appraise = RepositoryUtil.findOne(appraiseRepository.findRulesById(schoolId, periodId), "还未创建该学段下的评价规则");
|
||||
List<AchievementRule> rules = appraise.getAchievementRules();
|
||||
if (ObjectUtils.isEmpty(rules)) {
|
||||
rules = Collections.emptyList();
|
||||
}
|
||||
return rules;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,242 @@
|
||||
package cn.teammodel.controller.admin.service.impl;
|
||||
|
||||
import cn.teammodel.common.ErrorCode;
|
||||
import cn.teammodel.common.PK;
|
||||
import cn.teammodel.config.exception.ServiceException;
|
||||
import cn.teammodel.config.ies.IESConfig;
|
||||
import cn.teammodel.controller.admin.service.AdminIndexDutyService;
|
||||
import cn.teammodel.model.dto.admin.appraise.TimeRangeDto;
|
||||
import cn.teammodel.model.dto.weekDuty.LessonRecordDto;
|
||||
import cn.teammodel.model.entity.User;
|
||||
import cn.teammodel.model.entity.school.ClassInfo;
|
||||
import cn.teammodel.model.entity.school.School;
|
||||
import cn.teammodel.model.entity.school.Teacher;
|
||||
import cn.teammodel.model.entity.weekDuty.WeekDuty;
|
||||
import cn.teammodel.model.vo.admin.DutyIndexData;
|
||||
import cn.teammodel.model.vo.admin.DutyIndexPo;
|
||||
import cn.teammodel.model.vo.admin.DutyNodeRankVo;
|
||||
import cn.teammodel.model.vo.admin.DutyRankPo;
|
||||
import cn.teammodel.repository.ClassRepository;
|
||||
import cn.teammodel.repository.DutyRecordRepository;
|
||||
import cn.teammodel.repository.SchoolRepository;
|
||||
import cn.teammodel.repository.TeacherRepository;
|
||||
import cn.teammodel.security.utils.SecurityUtil;
|
||||
import cn.teammodel.utils.GroupUtil;
|
||||
import cn.teammodel.utils.JsonUtil;
|
||||
import cn.teammodel.utils.SchoolDateUtil;
|
||||
import com.azure.spring.data.cosmos.core.query.CosmosPageRequest;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.type.TypeFactory;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.JsonElement;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import lombok.val;
|
||||
import lombok.var;
|
||||
import org.apache.commons.lang3.ObjectUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.http.HttpEntity;
|
||||
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||
import org.apache.http.client.methods.HttpGet;
|
||||
import org.apache.http.client.methods.HttpPost;
|
||||
import org.apache.http.entity.StringEntity;
|
||||
import org.apache.http.impl.client.CloseableHttpClient;
|
||||
import org.apache.http.impl.client.HttpClients;
|
||||
import org.apache.http.util.EntityUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.data.domain.Slice;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.io.IOException;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static cn.teammodel.utils.SchoolDateUtil.calculateWeekNum;
|
||||
|
||||
/**
|
||||
* @author winter
|
||||
* @create 2024-02-28 15:07
|
||||
*/
|
||||
@Service
|
||||
@Slf4j
|
||||
public class AdminIndexDutyServiceImpl implements AdminIndexDutyService {
|
||||
|
||||
@Resource
|
||||
private SchoolRepository schoolRepository;
|
||||
|
||||
@Resource
|
||||
private TeacherRepository teacherRepository;
|
||||
|
||||
@Resource
|
||||
private ClassRepository classRepository;
|
||||
@Resource
|
||||
private DutyRecordRepository dutyRecordRepository;
|
||||
@Autowired
|
||||
private Environment env;
|
||||
|
||||
/* @Autowired
|
||||
public AdminIndexDutyServiceImpl(Environment env) {
|
||||
this.env = env;
|
||||
}*/
|
||||
|
||||
@Override
|
||||
public DutyIndexData getIndexData(String periodId) {
|
||||
User loginUser = SecurityUtil.getLoginUser();
|
||||
String schoolId = loginUser.getSchoolId();
|
||||
|
||||
final int SLICE_SIZE = 100;
|
||||
if (StringUtils.isBlank(periodId)) {
|
||||
throw new ServiceException(ErrorCode.PARAMS_ERROR.getCode(), "不能为空");
|
||||
}
|
||||
final DutyIndexData DutyIndexData = new DutyIndexData();
|
||||
int totalCount = 0;
|
||||
int negetiveCount = 0;
|
||||
Set<String> creatorIdSet = new HashSet<>();
|
||||
// todo
|
||||
Set<String> classIdSet = new HashSet<>();
|
||||
|
||||
// 获取学期起止时间
|
||||
List<School.Semester> semesters = schoolRepository.findSemestersById(schoolId, periodId);
|
||||
SchoolDateUtil.semesterModel semesterModel = SchoolDateUtil.getSemesterByNow(semesters, LocalDate.now());
|
||||
String academicYearId = semesterModel.getAcademicYearId();
|
||||
LocalDateTime startDatetime = semesterModel.getStartDatetime();
|
||||
LocalDateTime endDatetime = semesterModel.getEndDatetime();
|
||||
if (startDatetime == null || endDatetime == null) throw new ServiceException(ErrorCode.PARAMS_ERROR);
|
||||
long totalWeek = calculateWeekNum(startDatetime, endDatetime, null);
|
||||
|
||||
// slice 分段读取
|
||||
CosmosPageRequest pageRequest = new CosmosPageRequest(0, SLICE_SIZE, null);
|
||||
Slice<DutyIndexPo> slice;
|
||||
Map<Long, Integer> countByWeek = SchoolDateUtil.createEmptyWeekMap(totalWeek);
|
||||
|
||||
do {
|
||||
slice = dutyRecordRepository.findAllByAcademicYearId(String.format(PK.WEEK_DUTY_RECORD, schoolId), academicYearId, pageRequest);
|
||||
List<DutyIndexPo> content = slice.getContent();
|
||||
if (ObjectUtils.isEmpty(content)) {
|
||||
break;
|
||||
}
|
||||
|
||||
// 分批次计算
|
||||
for (DutyIndexPo item : content) {
|
||||
// 处理每周的评价数
|
||||
long weekNum = calculateWeekNum(startDatetime, endDatetime, item.getCreateTime());
|
||||
countByWeek.put(weekNum, countByWeek.getOrDefault(weekNum, 0) + 1);
|
||||
// 处理总评价数
|
||||
totalCount++;
|
||||
// 处理批评数
|
||||
if (item.getDutyTreeNodeScore() < 0) {
|
||||
negetiveCount++;
|
||||
}
|
||||
// 处理已评价老师总数
|
||||
creatorIdSet.add(item.getCreatorId());
|
||||
// 处理被评价的学生数
|
||||
classIdSet.add(item.getClassId());
|
||||
}
|
||||
|
||||
if (slice.hasNext()) {
|
||||
pageRequest = (CosmosPageRequest) slice.nextPageable();
|
||||
}
|
||||
} while (slice.hasNext());
|
||||
|
||||
// 组装数据
|
||||
DutyIndexData.setCountByWeek(countByWeek);
|
||||
DutyIndexData.setTotalCount(totalCount);
|
||||
DutyIndexData.setClassCount(classIdSet.size());
|
||||
DutyIndexData.setPositiveCount(totalCount - negetiveCount);
|
||||
DutyIndexData.setTeacherCount(creatorIdSet.size());
|
||||
return DutyIndexData;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<DutyRankPo> classRank(TimeRangeDto timeRangeDto) {
|
||||
Long startTime = timeRangeDto.getStartTime();
|
||||
Long endTime = timeRangeDto.getEndTime();
|
||||
String academicYearId = timeRangeDto.getAcademicYearId();
|
||||
String schoolId = SecurityUtil.getLoginUser().getSchoolId();
|
||||
List<DutyRankPo> res = dutyRecordRepository.classRank(String.format(PK.WEEK_DUTY_RECORD, schoolId), academicYearId, startTime, endTime);
|
||||
if (ObjectUtils.isEmpty(res)) return null;
|
||||
List<String> classIds = res.stream().map(DutyRankPo::getId).collect(Collectors.toList());
|
||||
if (ObjectUtils.isEmpty(classIds)) {
|
||||
return null;
|
||||
}
|
||||
List<ClassInfo> classes = classRepository.findAllByCodeAndIdIn(String.format(PK.CLASS, schoolId), classIds);
|
||||
Map<String, String> idNameMap = classes.stream().collect(Collectors.toMap(ClassInfo::getId, ClassInfo::getName));
|
||||
// reversed sort by score
|
||||
res = res.stream().peek(s -> s.setName(idNameMap.get(s.getId()))).sorted((a, b) -> b.getScore() - a.getScore()).collect(Collectors.toList());
|
||||
return res;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<DutyRankPo> teacherRank(TimeRangeDto timeRangeDto) {
|
||||
Long startTime = timeRangeDto.getStartTime();
|
||||
Long endTime = timeRangeDto.getEndTime();
|
||||
String academicYearId = timeRangeDto.getAcademicYearId();
|
||||
String schoolId = SecurityUtil.getLoginUser().getSchoolId();
|
||||
List<DutyRankPo> res = dutyRecordRepository.teacherRank(String.format(PK.WEEK_DUTY_RECORD, schoolId), academicYearId, startTime, endTime);
|
||||
// set teacher name
|
||||
if (ObjectUtils.isEmpty(res)) return null;
|
||||
Set<String> teacherIdSet = res.stream().map(DutyRankPo::getId).collect(Collectors.toSet());
|
||||
if (ObjectUtils.isEmpty(teacherIdSet)) return res;
|
||||
List<Teacher> teachers = teacherRepository.findAllByCodeAndIdIn(PK.COMMON_BASE, teacherIdSet);
|
||||
Map<String, String> idNameMap = teachers.stream().collect(Collectors.toMap(Teacher::getId, Teacher::getName));
|
||||
res = res.stream().peek(s -> s.setName(idNameMap.get(s.getId()))).sorted((a, b) -> b.getScore() - a.getScore()).collect(Collectors.toList());
|
||||
return res;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<DutyNodeRankVo> appraiseNodeRank(TimeRangeDto timeRangeDto) {
|
||||
Long startTime = timeRangeDto.getStartTime();
|
||||
Long endTime = timeRangeDto.getEndTime();
|
||||
String academicYearId = timeRangeDto.getAcademicYearId();
|
||||
String schoolId = SecurityUtil.getLoginUser().getSchoolId();
|
||||
|
||||
List<DutyNodeRankVo> res = dutyRecordRepository.dutyNodeRank(String.format(PK.WEEK_DUTY_RECORD, schoolId), academicYearId, startTime, endTime);
|
||||
if (ObjectUtils.isEmpty(res)) return null;
|
||||
List<String> names = res.stream().map(DutyNodeRankVo::getName).collect(Collectors.toList());
|
||||
if (ObjectUtils.isEmpty(names)) return null;
|
||||
// 去重后的 nodes
|
||||
List<WeekDuty.DutyTreeNode> nodesByName = dutyRecordRepository.findDutyRecordInNames(String.format(PK.WEEK_DUTY_RECORD, schoolId), academicYearId, names);
|
||||
// 正常情况下 name 一一对应 // TODO: 2024/2/29
|
||||
Map<String, WeekDuty.DutyTreeNode> nameNodeMap = nodesByName.stream()
|
||||
.collect(Collectors.toMap(WeekDuty.DutyTreeNode::getName, item -> item, (existing, replacement) -> {
|
||||
if (replacement.getPath() != null) {
|
||||
return replacement;
|
||||
}
|
||||
return existing;
|
||||
}));
|
||||
|
||||
res = res.stream()
|
||||
.sorted(Comparator.comparing(DutyNodeRankVo::getCount).reversed())
|
||||
// 流中对元素操作但不改变流
|
||||
.peek(s -> {
|
||||
WeekDuty.DutyTreeNode node = nameNodeMap.get(s.getName());
|
||||
if (node != null) {
|
||||
s.setPath(node.getPath());
|
||||
s.setScore(node.getScore());
|
||||
}
|
||||
})
|
||||
.collect(Collectors.toList());
|
||||
return res;
|
||||
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> getLessonRecord(LessonRecordDto lessonRecordDto, HttpServletRequest request) {
|
||||
|
||||
Map<String, Object> mapper = new HashMap<>();
|
||||
String url = env.getProperty("ies.server-url");
|
||||
if(lessonRecordDto.isClassMeeting()) {
|
||||
List<School.Subject> subjects = schoolRepository.findSubjectById(lessonRecordDto.getSchool(), lessonRecordDto.getPeriodId());
|
||||
List<String> subjectIds = subjects.stream().filter(subject -> "班会".equals(subject.getName()))
|
||||
.map(School.Subject::getId).collect(Collectors.toList());
|
||||
lessonRecordDto.setSubjectId(subjectIds);
|
||||
}
|
||||
mapper = GroupUtil.getGroupId(lessonRecordDto,new GroupUtil(env), request,url);
|
||||
return mapper;
|
||||
}
|
||||
}
|
@ -0,0 +1,104 @@
|
||||
package cn.teammodel.controller.admin.service.impl;
|
||||
|
||||
import cn.teammodel.controller.admin.service.ArtService;
|
||||
import cn.teammodel.model.dto.admin.art.ArtFindDto;
|
||||
import cn.teammodel.model.dto.admin.common.GroupDto;
|
||||
import cn.teammodel.model.dto.admin.common.RGroupList;
|
||||
import cn.teammodel.model.dto.admin.common.RMember;
|
||||
import cn.teammodel.model.vo.admin.ArtElementsVo;
|
||||
import cn.teammodel.repository.ArtRepository;
|
||||
import cn.teammodel.repository.ClassRepository;
|
||||
import cn.teammodel.repository.StudentRepository;
|
||||
import cn.teammodel.utils.GroupUtil;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.TypeReference;
|
||||
@Service
|
||||
public class ArtServiceImpl implements ArtService {
|
||||
@Resource
|
||||
private ArtRepository artRepository;
|
||||
@Resource
|
||||
private ClassRepository classRepository;
|
||||
@Resource
|
||||
private StudentRepository studentRepository;
|
||||
@Autowired
|
||||
private Environment env;
|
||||
@Override
|
||||
public List<ArtElementsVo> getArtList(ArtFindDto artFindDto, HttpServletRequest request) {
|
||||
List<ArtElementsVo> artElementsVos = artRepository.findPeriodById(artFindDto.getPeriodId(),"Art-"+ artFindDto.getCode());
|
||||
List<String> classIds = artElementsVos.stream()
|
||||
.map(ArtElementsVo::getClasses) // 正确的方法引用
|
||||
.flatMap(List::stream) // 将内部列表扁平化
|
||||
.collect(Collectors.toList());
|
||||
GroupDto groupDto = new GroupDto();
|
||||
groupDto.setIds(classIds);
|
||||
groupDto.setSchoolId(artFindDto.getCode());
|
||||
String url = env.getProperty("ies.server-url-group");
|
||||
//List<ClassInfo> classes = classRepository.findAllByCodeAndIdIn("Class-"+artFindDto.getCode(),classIds);
|
||||
Map<String, Object> groupId = GroupUtil.getGroupId(groupDto,new GroupUtil(env), request,url);
|
||||
List<RGroupList> rGroupList = new ArrayList<>();
|
||||
List<RMember> rMembers = new ArrayList<>();
|
||||
for (Map.Entry<String, Object> entry : groupId.entrySet()) {
|
||||
String key = entry.getKey();
|
||||
Object value = entry.getValue();
|
||||
if (key.equals("groups")) {
|
||||
String jsonGroups = JSON.toJSONString(value);
|
||||
rGroupList = JSON.parseObject(jsonGroups, new TypeReference<List<RGroupList>>() {});
|
||||
}
|
||||
if (key.equals("members")) {
|
||||
String jsonGroups = JSON.toJSONString(value);
|
||||
rMembers = JSON.parseObject(jsonGroups, new TypeReference<List<RMember>>() {});
|
||||
}
|
||||
}
|
||||
try {
|
||||
for (ArtElementsVo artElementsVo : artElementsVos) {
|
||||
List<String> classes1 = artElementsVo.getClasses();
|
||||
//int stuInClassCount = 0;
|
||||
for(String classId : classes1) {
|
||||
//stuInClassCount += studentRepository.countByClassIdAndCode(classId, String.format(PK.STUDENT, artFindDto.getCode()));
|
||||
ArtElementsVo.ClassInfos classInfos = new ArtElementsVo.ClassInfos();
|
||||
rGroupList.stream().filter(rGroupList1 -> rGroupList1.getId().equals(classId)).findFirst().ifPresent(rGroupList1 -> {
|
||||
classInfos.setId(rGroupList1.getId());
|
||||
classInfos.setName(rGroupList1.getName());
|
||||
});
|
||||
artElementsVo.addClassesInfos(classInfos);
|
||||
}
|
||||
artElementsVo.setCount(rMembers.size());
|
||||
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return artElementsVos;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<RGroupList> getGroupList(GroupDto groupDto, HttpServletRequest request) {
|
||||
List<RGroupList> rGroupList = new ArrayList<>();
|
||||
String url = env.getProperty("ies.server-url-group");
|
||||
try {
|
||||
Map<String, Object> groupId = GroupUtil.getGroupId(groupDto,new GroupUtil(env), request,url);
|
||||
//List<RMember> rMembers = new ArrayList<>();
|
||||
for (Map.Entry<String, Object> entry : groupId.entrySet()) {
|
||||
String key = entry.getKey();
|
||||
Object value = entry.getValue();
|
||||
if (key.equals("groups")) {
|
||||
String jsonGroups = JSON.toJSONString(value);
|
||||
rGroupList = JSON.parseObject(jsonGroups, new TypeReference<List<RGroupList>>() {});
|
||||
}
|
||||
}
|
||||
|
||||
}catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
|
||||
}
|
||||
return rGroupList;
|
||||
}
|
||||
}
|
@ -0,0 +1,83 @@
|
||||
package cn.teammodel.controller.admin.service.impl;
|
||||
|
||||
import cn.teammodel.controller.admin.service.CommonService;
|
||||
import cn.teammodel.model.dto.admin.common.GCDto;
|
||||
import cn.teammodel.model.entity.school.ClassInfo;
|
||||
import cn.teammodel.model.entity.school.School;
|
||||
import cn.teammodel.model.vo.admin.GradeAndClassVo;
|
||||
import cn.teammodel.repository.ClassRepository;
|
||||
import cn.teammodel.repository.SchoolRepository;
|
||||
import cn.teammodel.utils.MonthToNumberConverter;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.time.LocalDate;
|
||||
import java.time.Month;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static java.time.LocalDate.now;
|
||||
|
||||
@Service
|
||||
public class CommonServiceImpl implements CommonService {
|
||||
@Resource
|
||||
private SchoolRepository schoolRepository;
|
||||
@Resource
|
||||
private ClassRepository classRepository;
|
||||
|
||||
@Override
|
||||
public List<GradeAndClassVo> getGradeAndClass(GCDto gcDto) {
|
||||
List<GradeAndClassVo> gradeAndClassVos = new ArrayList<>();
|
||||
try {
|
||||
//获取当前学校该学段下详细信息
|
||||
List<School.Period> period = schoolRepository.findPeriodById(gcDto.getSchoolId(), gcDto.getPeriodId());
|
||||
List<ClassInfo> classes = classRepository.findClassBySchoolIdAndPeriodId(gcDto.getSchoolId(), gcDto.getPeriodId());
|
||||
int year = now().getYear();
|
||||
Month month = now().getMonth();
|
||||
int mon = MonthToNumberConverter.convertMonthToNumber(month.name());
|
||||
int day = now().getDayOfMonth();
|
||||
//处理年级ID
|
||||
for (ClassInfo classInfo : classes) {
|
||||
if(period.get(0).getId().equalsIgnoreCase(classInfo.getPeriodId())) {
|
||||
for (School.Semester semester : period.get(0).getSemesters()) {
|
||||
int time = 0;
|
||||
if(semester.getStart() == 1) {
|
||||
if (mon == semester.getMonth())
|
||||
{
|
||||
time = day >= semester.getDay() ? 0 : 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
time = mon > semester.getMonth() ? 0 : 1;
|
||||
}
|
||||
int eYear = year - time;
|
||||
classInfo.setGrade(eYear- classInfo.getYear());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
List<String> grades = period.get(0).getGrades();
|
||||
int index = 0;
|
||||
for (String grade : grades) {
|
||||
GradeAndClassVo gradeAndClassVo = new GradeAndClassVo();
|
||||
gradeAndClassVo.setGradeId(index);
|
||||
gradeAndClassVo.setGradeName(grade);
|
||||
classes.stream().filter(classInfo -> classInfo.getGrade() == gradeAndClassVo.getGradeId()).forEach(classInfo -> {
|
||||
GradeAndClassVo.CI ci = new GradeAndClassVo.CI();
|
||||
ci.setClassId(classInfo.getId());
|
||||
ci.setClassName(classInfo.getName());
|
||||
gradeAndClassVo.AddClass(ci);
|
||||
});
|
||||
index ++;
|
||||
gradeAndClassVos.add(gradeAndClassVo);
|
||||
}
|
||||
|
||||
return gradeAndClassVos;
|
||||
}catch (Exception e) {
|
||||
|
||||
throw new RuntimeException("获取当前学校当前学段年级班级信息失败");
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,170 @@
|
||||
package cn.teammodel.controller.frontend;
|
||||
|
||||
import cn.teammodel.common.IdRequest;
|
||||
import cn.teammodel.common.R;
|
||||
import cn.teammodel.model.dto.ai.*;
|
||||
import cn.teammodel.model.entity.TmdUserDetail;
|
||||
import cn.teammodel.model.entity.ai.ChatApp;
|
||||
import cn.teammodel.model.entity.ai.ChatSession;
|
||||
import cn.teammodel.security.utils.SecurityUtil;
|
||||
import cn.teammodel.service.ChatAppService;
|
||||
import cn.teammodel.service.ChatMessageService;
|
||||
import cn.teammodel.service.ChatSessionService;
|
||||
import io.jsonwebtoken.Claims;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import javax.validation.Valid;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/ai")
|
||||
@Api(tags = "AI 能力")
|
||||
public class AiController {
|
||||
@Resource
|
||||
private ChatSessionService chatSessionService;
|
||||
@Resource
|
||||
private ChatMessageService chatMessageService;
|
||||
@Resource
|
||||
private ChatAppService chatAppService;
|
||||
|
||||
@PostMapping("api/chat/completion")
|
||||
@ApiOperation("与 spark 的流式对话")
|
||||
public SseEmitter chatCompletionToApi(@RequestBody @Valid ChatCompletionReqDto chatCompletionReqDto) {
|
||||
String userId = ((TmdUserDetail) SecurityUtil.getAuthentication().getPrincipal()).getClaims().getSubject();
|
||||
return chatMessageService.chatCompletion(chatCompletionReqDto, userId);
|
||||
}
|
||||
@PostMapping("chat/completion")
|
||||
@ApiOperation("与 spark 的流式对话")
|
||||
public SseEmitter chatCompletion(@RequestBody @Valid ChatCompletionReqDto chatCompletionReqDto) {
|
||||
return chatMessageService.chatCompletion(chatCompletionReqDto, null);
|
||||
}
|
||||
|
||||
// @PostMapping("chat/test/completion")
|
||||
@ApiOperation("与 spark 的流式对话")
|
||||
public SseEmitter testCompletion(@RequestBody @Valid ChatCompletionReqDto chatCompletionReqDto) throws IOException, InterruptedException {
|
||||
SseEmitter sseEmitter = new SseEmitter();
|
||||
CompletableFuture.runAsync(() -> {
|
||||
try {
|
||||
sseEmitter.send("曾经以为我们");
|
||||
Thread.sleep(1000);
|
||||
sseEmitter.send("开始了就会走到最后,可是当分手之后才明白我想的");
|
||||
Thread.sleep(1000);
|
||||
sseEmitter.send("你不一定能做到,当我开始去遗忘我们");
|
||||
Thread.sleep(1500);
|
||||
sseEmitter.send("的经历时,却不知道遗忘已变成另一种开始,");
|
||||
Thread.sleep(800);
|
||||
sseEmitter.send("回忆是淡了,可是痛却还在,还是最真实。放手的时候微笑着说无所谓");
|
||||
Thread.sleep(800);
|
||||
sseEmitter.send(",离开了你我还可以过的很好");
|
||||
Thread.sleep(1000);
|
||||
sseEmitter.send(",而你却不知道微笑只是用来掩盖疼痛的伤疤。");
|
||||
Thread.sleep(1200);
|
||||
sseEmitter.send("离开你之后的自己陷入一种无助的状态,空洞的");
|
||||
Thread.sleep(1300);
|
||||
sseEmitter.send("双眼回忆不起曾经的你,那时候以为与世隔绝或许才是维护自己的最好方式。");
|
||||
Thread.sleep(1100);
|
||||
sseEmitter.send("[DONE]");
|
||||
sseEmitter.complete();
|
||||
|
||||
} catch (IOException | InterruptedException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
});
|
||||
return sseEmitter;
|
||||
}
|
||||
|
||||
@GetMapping("api/session/my")
|
||||
@ApiOperation("查询我的聊天会话")
|
||||
public R<List<ChatSession>> listMySession() {
|
||||
String userId = ((TmdUserDetail) SecurityUtil.getAuthentication().getPrincipal()).getClaims().getSubject();
|
||||
List<ChatSession> sessions = chatSessionService.listMySession(userId);
|
||||
return R.success(sessions);
|
||||
}
|
||||
|
||||
@GetMapping("api/chat/history/{sessionId}")
|
||||
@ApiOperation("查询我的聊天记录")
|
||||
public R<List<ChatSession.Message>> getHistory(@PathVariable String sessionId) {
|
||||
String userId = ((TmdUserDetail) SecurityUtil.getAuthentication().getPrincipal()).getClaims().getSubject();
|
||||
List<ChatSession.Message> history = chatSessionService.listHistory(sessionId, userId);
|
||||
return R.success(history);
|
||||
}
|
||||
@PostMapping("api/session/create")
|
||||
@ApiOperation("创建聊天会话")
|
||||
public R<String> createSession() {
|
||||
String userId = ((TmdUserDetail) SecurityUtil.getAuthentication().getPrincipal()).getClaims().getSubject();
|
||||
String name = (String) ((TmdUserDetail) SecurityUtil.getAuthentication().getPrincipal()).getClaims().get("name");
|
||||
name = StringUtils.isBlank(name) ? "老师" : name;
|
||||
String sessionId = chatSessionService.createSession(userId, name);
|
||||
return R.success(sessionId);
|
||||
}
|
||||
|
||||
@PostMapping("api/session/remove")
|
||||
@ApiOperation("删除聊天会话")
|
||||
public R<String> removeSession(@RequestBody @Valid IdRequest idRequest) {
|
||||
String userId = ((TmdUserDetail) SecurityUtil.getAuthentication().getPrincipal()).getClaims().getSubject();
|
||||
chatSessionService.deleteSession(idRequest.getId(), userId);
|
||||
return R.success("删除会话成功");
|
||||
}
|
||||
@PostMapping("api/session/update")
|
||||
@ApiOperation("更新聊天会话")
|
||||
public R<ChatSession> updateSession(@RequestBody @Valid UpdateSessionDto updateSessionDto) {
|
||||
String userId = ((TmdUserDetail) SecurityUtil.getAuthentication().getPrincipal()).getClaims().getSubject();
|
||||
ChatSession session = chatSessionService.updateSession(updateSessionDto, userId);
|
||||
return R.success(session);
|
||||
}
|
||||
|
||||
@PostMapping("app/list")
|
||||
@ApiOperation("查询聊天应用列表")
|
||||
public R<List<ChatApp>> listApp(@RequestBody @Valid SearchAppDto searchAppDto) {
|
||||
List<ChatApp> chatApps = chatAppService.listApp(searchAppDto);
|
||||
return R.success(chatApps);
|
||||
}
|
||||
|
||||
@PostMapping("app/create")
|
||||
@ApiOperation("创建聊天应用")
|
||||
public R<ChatApp> createApp(@RequestBody @Valid CreateChatAppDto createChatAppDto) {
|
||||
ChatApp chatApp = chatAppService.createApp(createChatAppDto);
|
||||
return R.success(chatApp);
|
||||
}
|
||||
|
||||
@PostMapping("app/update")
|
||||
@ApiOperation("更新聊天应用")
|
||||
public R<ChatApp> updateApp(@RequestBody @Valid UpdateChatAppDto updateChatAppDto) {
|
||||
ChatApp chatApp = chatAppService.updateApp(updateChatAppDto);
|
||||
return R.success(chatApp);
|
||||
}
|
||||
|
||||
@PostMapping("app/remove")
|
||||
@ApiOperation("删除聊天应用")
|
||||
public R<String> updateApp(@RequestBody @Valid IdRequest idRequest) {
|
||||
chatAppService.deleteApp(idRequest);
|
||||
return R.success("删除应用成功");
|
||||
}
|
||||
|
||||
@PostMapping("chat/comments")
|
||||
@ApiOperation("设置评语")
|
||||
public SseEmitter chatComments(@RequestBody @Valid ChatCompletionReqDto chatCompletionReqDto) {
|
||||
/*
|
||||
Authentication user0 = SecurityUtil.getAuthentication();
|
||||
Object user01 = SecurityUtil.getAuthentication().getPrincipal();
|
||||
TmdUserDetail user02 = (TmdUserDetail) SecurityUtil.getAuthentication().getPrincipal();
|
||||
Claims user03 = ((TmdUserDetail) SecurityUtil.getAuthentication().getPrincipal()).getClaims();
|
||||
String user04 = ((TmdUserDetail) SecurityUtil.getAuthentication().getPrincipal()).getClaims().getSubject();
|
||||
*/
|
||||
|
||||
//String userId = ((TmdUserDetail) SecurityUtil.getAuthentication().getPrincipal()).getClaims().getSubject();
|
||||
// 获取getClaims时为空
|
||||
String userId = ((TmdUserDetail) SecurityUtil.getAuthentication().getPrincipal()).getUser().getId();
|
||||
String userName = ((TmdUserDetail) SecurityUtil.getAuthentication().getPrincipal()).getUser().getName();
|
||||
return chatMessageService.chatComments(chatCompletionReqDto, userId, userName);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,94 @@
|
||||
package cn.teammodel.controller.frontend;
|
||||
|
||||
import cn.teammodel.common.IdRequest;
|
||||
import cn.teammodel.common.R;
|
||||
import cn.teammodel.model.dto.Appraise.*;
|
||||
import cn.teammodel.model.entity.appraise.Appraise;
|
||||
import cn.teammodel.model.vo.appraise.AppraiseRecordVo;
|
||||
import cn.teammodel.model.vo.appraise.StudentReportVo;
|
||||
import cn.teammodel.service.EvaluationService;
|
||||
import com.itextpdf.text.DocumentException;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
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;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.validation.Valid;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author winter
|
||||
* @create 2023-11-22 15:10
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/appraise")
|
||||
@Api(tags = "学生评价")
|
||||
public class AppraiseController {
|
||||
@Resource
|
||||
private EvaluationService evaluationService;
|
||||
|
||||
@PostMapping("getTrees")
|
||||
@ApiOperation(value = "获取评价树", notes = "获取评价树")
|
||||
public R<Appraise> getEvaluateTree(@RequestBody @Valid GetEvaluateTreeDto getEvaluateTreeDto) {
|
||||
Appraise appraise = evaluationService.getTree(getEvaluateTreeDto);
|
||||
return R.success(appraise);
|
||||
}
|
||||
|
||||
@PostMapping("insertNode")
|
||||
@ApiOperation(value = "新增评价树的节点")
|
||||
public R<Appraise> insertNode(@RequestBody @Valid InsertNodeDto insertNodeDto) {
|
||||
Appraise appraise = evaluationService.insertNode(insertNodeDto);
|
||||
return R.success(appraise);
|
||||
}
|
||||
@PostMapping("updateNode")
|
||||
@ApiOperation(value = "更新评价树的节点", notes = "传递更新后的节点,而不是局部更新的值")
|
||||
public R<Appraise> updateTree(@RequestBody @Valid UpdateNodeDto updateNodeDto) {
|
||||
// fixme: 更新一二级节点应该同时更新三级的 path
|
||||
Appraise appraise = evaluationService.updateNode(updateNodeDto);
|
||||
return R.success(appraise);
|
||||
}
|
||||
@PostMapping("deleteNode")
|
||||
@ApiOperation(value = "删除评价树的节点")
|
||||
public R<Appraise> deleteNode(@RequestBody @Valid DeleteNodeDto deleteNodeDto) {
|
||||
Appraise appraise = evaluationService.deleteNode(deleteNodeDto);
|
||||
return R.success(appraise);
|
||||
}
|
||||
|
||||
@PostMapping("vote")
|
||||
@ApiOperation(value = "给某个学生评价(投票)")
|
||||
public R<String> vote(@RequestBody @Valid AppraiseVoteDto appraiseVoteDto) {
|
||||
evaluationService.vote(appraiseVoteDto);
|
||||
return R.success("评价成功");
|
||||
}
|
||||
@PostMapping("recallVote")
|
||||
@ApiOperation(value = "撤回给某个学生评价(投票)")
|
||||
public R<String> recallVote(@RequestBody @Valid RecallVoteDto recallVoteDto) {
|
||||
evaluationService.recallVote(recallVoteDto);
|
||||
return R.success("撤回评价成功");
|
||||
}
|
||||
|
||||
@PostMapping("findVoteRecord")
|
||||
@ApiOperation(value = "多条件查询当前登录老师的学生评价(投票)")
|
||||
public R<List<AppraiseRecordVo>> findMyVoteRecord(@Valid @RequestBody FindVoteRecordDto findVoteRecordDto) {
|
||||
List<AppraiseRecordVo> res = evaluationService.findVoteRecord(findVoteRecordDto);
|
||||
return R.success(res);
|
||||
}
|
||||
|
||||
@PostMapping("studentReport")
|
||||
@ApiOperation(value = "查看学生当前的学期的实时评价报告")
|
||||
public R<StudentReportVo> studentReport(@Valid @RequestBody IdRequest idRequest) {
|
||||
StudentReportVo res = evaluationService.studentReport(idRequest);
|
||||
return R.success(res);
|
||||
}
|
||||
|
||||
@PostMapping("studentReportPDF")
|
||||
@ApiOperation(value = "导出学生当前的学期的实时评价报告 PDF")
|
||||
public void exportStuReportPdf(@Valid @RequestBody IdRequest idRequest, HttpServletResponse response) throws DocumentException, IOException {
|
||||
evaluationService.exportStuReportPdf(idRequest, response);
|
||||
}
|
||||
}
|
@ -0,0 +1,93 @@
|
||||
package cn.teammodel.controller.frontend;
|
||||
|
||||
import cn.teammodel.common.IdRequest;
|
||||
import cn.teammodel.common.R;
|
||||
import cn.teammodel.model.dto.weekDuty.*;
|
||||
import cn.teammodel.model.entity.weekDuty.WeekDuty;
|
||||
import cn.teammodel.model.vo.weekDuty.DutyRecordVo;
|
||||
import cn.teammodel.service.DutyService;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import javax.validation.Valid;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author winter
|
||||
* @create 2024-01-03 10:06
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/duty")
|
||||
@Api(tags = "值周巡检")
|
||||
public class DutyController {
|
||||
@Resource
|
||||
private DutyService dutyService;
|
||||
|
||||
@GetMapping("/getTree")
|
||||
@ApiOperation("获取值周评价标准树(不存在则拷贝模板)")
|
||||
public R<WeekDuty> getTree() {
|
||||
WeekDuty weekDuty = dutyService.getTree();
|
||||
return R.success(weekDuty);
|
||||
}
|
||||
|
||||
@PostMapping("/insertNode")
|
||||
@ApiOperation("插入值周评价标准树节点")
|
||||
public R<WeekDuty> insertNode(@RequestBody @Valid InsertDutyNodeDto insertDutyNodeDto) {
|
||||
WeekDuty weekDuty = dutyService.insertNode(insertDutyNodeDto);
|
||||
return R.success(weekDuty);
|
||||
}
|
||||
|
||||
@PostMapping("/deleteNode")
|
||||
@ApiOperation("删除值周评价标准树节点")
|
||||
public R<WeekDuty> deleteNode(@RequestBody @Valid DeleteDutyNodeDto deleteDutyNodeDto) {
|
||||
WeekDuty weekDuty = dutyService.deleteNode(deleteDutyNodeDto);
|
||||
return R.success(weekDuty);
|
||||
}
|
||||
|
||||
@PostMapping("/updateNode")
|
||||
@ApiOperation("更新值周评价标准树节点")
|
||||
public R<WeekDuty> updateNode(@RequestBody @Valid UpdateDutyNodeDto updateDutyNodeDto) {
|
||||
WeekDuty weekDuty = dutyService.updateNode(updateDutyNodeDto);
|
||||
return R.success(weekDuty);
|
||||
}
|
||||
|
||||
@PostMapping("/vote")
|
||||
@ApiOperation("值周评价投票")
|
||||
public R<String> vote(@RequestBody @Valid DutyVoteDto dutyVoteDto) {
|
||||
dutyService.vote(dutyVoteDto);
|
||||
return R.success("评价成功");
|
||||
}
|
||||
|
||||
@PostMapping("/recallVote")
|
||||
@ApiOperation("撤回评价投票")
|
||||
public R<String> recallVote(@RequestBody @Valid RecallDutyVoteDto recallDutyVoteDto) {
|
||||
dutyService.recallVote(recallDutyVoteDto);
|
||||
return R.success("撤回评价成功");
|
||||
}
|
||||
|
||||
@PostMapping("/insertSpot")
|
||||
@ApiOperation("插入值周评价地点")
|
||||
public R<List<WeekDuty.DutySpot>> insertSpot(@RequestBody @Valid InsertSpotDto insertSpotDto) {
|
||||
List<WeekDuty.DutySpot> spots = dutyService.insertSpot(insertSpotDto);
|
||||
return R.success(spots);
|
||||
}
|
||||
|
||||
@PostMapping("/deleteSpot")
|
||||
@ApiOperation("删除值周评价地点")
|
||||
public R<List<WeekDuty.DutySpot>> deleteSpot(@RequestBody @Valid IdRequest idRequest) {
|
||||
List<WeekDuty.DutySpot> spots = dutyService.deleteSpot(idRequest.getId());
|
||||
return R.success(spots);
|
||||
}
|
||||
|
||||
@PostMapping("/findRecords")
|
||||
@ApiOperation(value = "多条件查询评价明细", notes = "只带 periodId 默认查询当前登录老师在本周的评价明细")
|
||||
public R<List<DutyRecordVo>> findRecords(@RequestBody @Valid FindDutyRecordDto findDutyRecordDto) {
|
||||
List<DutyRecordVo> items = dutyService.findRecords(findDutyRecordDto);
|
||||
return R.success(items);
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,76 @@
|
||||
package cn.teammodel.controller.frontend;
|
||||
|
||||
import cn.teammodel.common.R;
|
||||
import cn.teammodel.repository.AppraiseRepository;
|
||||
import cn.teammodel.service.EvaluationService;
|
||||
import com.itextpdf.text.DocumentException;
|
||||
import io.swagger.annotations.Api;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/")
|
||||
@Api(tags = "鉴权测试")
|
||||
public class HelloController {
|
||||
|
||||
@Resource
|
||||
private EvaluationService evaluationService;
|
||||
@Resource
|
||||
private AppraiseRepository appraiseRepository;
|
||||
|
||||
@GetMapping("hello")
|
||||
@PreAuthorize("@ss.hasRole('admin')")
|
||||
public R<String> hello() {
|
||||
System.out.println(SecurityContextHolder.getContext().getAuthentication());
|
||||
|
||||
return new R(200, "success","hello world");
|
||||
}
|
||||
@GetMapping("public/free")
|
||||
@PreAuthorize("permitAll()")
|
||||
public R<String> free() {
|
||||
return new R(200, "success","hello world");
|
||||
}
|
||||
|
||||
@GetMapping("public/pdf")
|
||||
public void freepdf(HttpServletResponse response) throws DocumentException, IOException { // 设置response参数
|
||||
// response.reset();
|
||||
// response.setContentType("application/pdf");
|
||||
// response.setHeader("Content-disposition",
|
||||
// "attachment;filename=report_student_" + System.currentTimeMillis() + ".pdf");
|
||||
// ClassPathResource resource = new ClassPathResource("templates/pdf_templates/student_report.pdf");
|
||||
// InputStream in = resource.getInputStream();
|
||||
// ServletOutputStream os = response.getOutputStream();
|
||||
// // 处理 stampter
|
||||
// PdfReader pdfReader = new PdfReader(in);
|
||||
// PdfStamper stamper = new PdfStamper(pdfReader, os);
|
||||
//
|
||||
// Map<String, String> data = PdfUtil.data();
|
||||
// DefaultPieDataset dataset = new DefaultPieDataset( );
|
||||
// dataset.setValue( "IPhone 5s" , new Double( 20 ) );
|
||||
// dataset.setValue( "SamSung Grand" , new Double( 20 ) );
|
||||
// dataset.setValue( "MotoG" , new Double( 40 ) );
|
||||
// dataset.setValue( "Nokia Lumia" , new Double( 10 ) );
|
||||
//
|
||||
// JFreeChart pieChart = ChartUtil.pieChart("手机销量统计", dataset);
|
||||
// ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||
// ChartUtils.writeChartAsJPEG(bos, pieChart, 850, 440);
|
||||
// // 填充表单
|
||||
// PdfUtil.fillPdfForm(stamper, data);
|
||||
// PdfUtil.fillImage(stamper, "praiseDistribution", bos.toByteArray());
|
||||
// PdfUtil.fillImage(stamper, "criticalDistribution", bos.toByteArray());
|
||||
// // 关闭流
|
||||
// stamper.setFormFlattening(true);
|
||||
// stamper.close();
|
||||
// os.close();
|
||||
//
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,63 @@
|
||||
package cn.teammodel.controller.frontend;
|
||||
|
||||
import cn.teammodel.common.IdRequest;
|
||||
import cn.teammodel.common.R;
|
||||
import cn.teammodel.model.dto.news.CreateNewsDto;
|
||||
import cn.teammodel.model.dto.news.UpdateNewsDto;
|
||||
import cn.teammodel.model.entity.news.News;
|
||||
import cn.teammodel.service.NewsService;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import javax.validation.Valid;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author winter
|
||||
* @create 2024-02-26 17:04
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/news")
|
||||
@Api(tags = "新闻设置")
|
||||
public class NewsController {
|
||||
@Resource
|
||||
private NewsService newsService;
|
||||
|
||||
@GetMapping("list/{periodId}")
|
||||
@ApiOperation("查询新闻")
|
||||
public R<List<News>> listNews(@PathVariable String periodId) {
|
||||
List<News> Newss = newsService.listNews(periodId);
|
||||
return R.success(Newss);
|
||||
}
|
||||
|
||||
@GetMapping("get/{newsId}")
|
||||
@ApiOperation("根据id查询新闻")
|
||||
public R<News> getNewsById(@PathVariable String newsId) {
|
||||
News news = newsService.getNewsById(newsId);
|
||||
return R.success(news);
|
||||
}
|
||||
|
||||
|
||||
@PostMapping("create")
|
||||
@ApiOperation("创建新闻")
|
||||
public R<News> createNews(@RequestBody @Valid CreateNewsDto createNewsDto) {
|
||||
News News = newsService.createNews(createNewsDto);
|
||||
return R.success(News);
|
||||
}
|
||||
|
||||
@PostMapping("update")
|
||||
@ApiOperation("更新新闻")
|
||||
public R<News> updateNews(@RequestBody @Valid UpdateNewsDto updateNewsDto) {
|
||||
News News = newsService.updateNews(updateNewsDto);
|
||||
return R.success(News);
|
||||
}
|
||||
|
||||
@PostMapping("remove")
|
||||
@ApiOperation("删除新闻")
|
||||
public R<String> deleteNews(@RequestBody @Valid IdRequest idRequest) {
|
||||
newsService.deleteNews(idRequest);
|
||||
return R.success("删除新闻成功");
|
||||
}
|
||||
}
|
@ -0,0 +1,44 @@
|
||||
package cn.teammodel.manager.notification;
|
||||
|
||||
import com.dingtalk.api.DefaultDingTalkClient;
|
||||
import com.dingtalk.api.DingTalkClient;
|
||||
import com.dingtalk.api.request.OapiRobotSendRequest;
|
||||
import com.taobao.api.ApiException;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* 钉钉告警服务
|
||||
* @author winter
|
||||
* @create 2023-11-14 10:11
|
||||
*/
|
||||
@Component
|
||||
@Slf4j
|
||||
public class DingAlertNotifier implements NotificationService{
|
||||
private final DingTalkClient client;
|
||||
@Autowired
|
||||
public DingAlertNotifier(@Value("${ding.server-url}") String dingServerUrl) {
|
||||
this.client = new DefaultDingTalkClient(dingServerUrl);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void send(String message) {
|
||||
OapiRobotSendRequest request = new OapiRobotSendRequest();
|
||||
// 文本消息
|
||||
request.setMsgtype("text");
|
||||
OapiRobotSendRequest.Text text = new OapiRobotSendRequest.Text();
|
||||
text.setContent(message);
|
||||
request.setText(text);
|
||||
// at 管理员提醒异常
|
||||
OapiRobotSendRequest.At atAll = new OapiRobotSendRequest.At();
|
||||
atAll.setIsAtAll(true);
|
||||
request.setAt(atAll);
|
||||
try {
|
||||
client.execute(request);
|
||||
} catch (ApiException e) {
|
||||
log.error("钉钉 robot 推送消息渠道异常: {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
package cn.teammodel.manager.notification;
|
||||
|
||||
/**
|
||||
* 消息通知接口
|
||||
* @author winter
|
||||
* @create 2023-11-14 10:08
|
||||
*/
|
||||
public interface NotificationService {
|
||||
|
||||
/**
|
||||
* 发送消息
|
||||
* @author: winter
|
||||
* @date: 2023/11/14 10:09
|
||||
*/
|
||||
void send(String message);
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
package cn.teammodel.manager.wx;
|
||||
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/**
|
||||
* @author winter
|
||||
* @create 2024-03-26 11:08
|
||||
*/
|
||||
@Service
|
||||
public class MiniProgramSevice {
|
||||
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
package cn.teammodel.model.dto.Appraise;
|
||||
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
/**
|
||||
* @author winter
|
||||
* @create 2023-11-28 16:16
|
||||
*/
|
||||
@Data
|
||||
public class AppraiseVoteDto {
|
||||
@NotNull
|
||||
@ApiModelProperty(value = "评价对象 Id")
|
||||
private String targetId;
|
||||
|
||||
@NotNull
|
||||
@ApiModelProperty(value = "评价对象类型:", allowableValues = "student ,class")
|
||||
private String targetType;
|
||||
|
||||
@ApiModelProperty(value = "评分是否传播到班级下的所有学生(评价 CLASS 时生效)")
|
||||
private boolean spread;
|
||||
|
||||
@ApiModelProperty(value = "是否推送给家长(评价 STUDENT 时生效)")
|
||||
private boolean pushParent;
|
||||
/**
|
||||
* 可以当作唯一 id
|
||||
*/
|
||||
@NotNull
|
||||
@ApiModelProperty(value = "评价项唯一 id", required = true)
|
||||
private String appraiseId;
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
package cn.teammodel.model.dto.Appraise;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
/**
|
||||
* @author winter
|
||||
* @create 2023-11-22 16:16
|
||||
*/
|
||||
@Data
|
||||
public class DeleteNodeDto {
|
||||
@NotNull(message = "学段 id 不能为空")
|
||||
String periodId;
|
||||
@NotNull(message = "评价项节点 id 不能为空")
|
||||
String id;
|
||||
}
|
@ -0,0 +1,32 @@
|
||||
package cn.teammodel.model.dto.Appraise;
|
||||
|
||||
import cn.teammodel.common.PageableRequest;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
/**
|
||||
* @author winter
|
||||
* @create 2023-11-28 16:16
|
||||
*/
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Data
|
||||
public class FindVoteRecordDto extends PageableRequest {
|
||||
|
||||
@NotNull
|
||||
@ApiModelProperty(value = "必要参数,用于获取当前学年,注意: 其他参数不传则默认获取登录老师在该学年下评价的所有记录", required = true)
|
||||
private String periodId;
|
||||
|
||||
@ApiModelProperty(value = "班级或学生 id")
|
||||
private String targetId;
|
||||
|
||||
@ApiModelProperty(value = "评价对象类型:", allowableValues = "student ,class")
|
||||
private String targetType;
|
||||
|
||||
@ApiModelProperty(value = "按班级 id 搜索")
|
||||
private String classId;
|
||||
@ApiModelProperty(value = "是否为表扬")
|
||||
private Boolean isPraise;
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
package cn.teammodel.model.dto.Appraise;
|
||||
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
/**
|
||||
* @author winter
|
||||
* @create 2023-11-22 15:21
|
||||
*/
|
||||
@Data
|
||||
public class GetEvaluateTreeDto {
|
||||
@ApiModelProperty(value = "学段 id", required = true)
|
||||
@NotNull(message = "学段 id 不能为空")
|
||||
String periodId;
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
package cn.teammodel.model.dto.Appraise;
|
||||
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
/**
|
||||
* @author winter
|
||||
* @create 2023-11-22 16:16
|
||||
*/
|
||||
@Data
|
||||
public class InsertNodeDto {
|
||||
@ApiModelProperty(value = "学段 id", required = true)
|
||||
@NotNull(message = "学段 id 不能为空")
|
||||
String periodId;
|
||||
@ApiModelProperty(value = "父亲节点,不传则为根节点")
|
||||
String pid;
|
||||
@ApiModelProperty(value = "父亲节点,不传则为根节点", required = true)
|
||||
@NotBlank(message = "name 不能为空")
|
||||
String name;
|
||||
String logo;
|
||||
Integer order = 0;
|
||||
Integer score = 0;
|
||||
Boolean isPraise = true;
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
package cn.teammodel.model.dto.Appraise;
|
||||
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
/**
|
||||
* @author winter
|
||||
* @create 2023-11-22 16:16
|
||||
*/
|
||||
@Data
|
||||
public class RecallVoteDto {
|
||||
@NotNull
|
||||
@ApiModelProperty(value = "学生评价记录的文档id", required = true)
|
||||
String recordId;
|
||||
@NotNull
|
||||
@ApiModelProperty(value = "学生评价记录的具体节点id", required = true)
|
||||
String nodeId;
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
package cn.teammodel.model.dto.Appraise;
|
||||
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
/**
|
||||
* @author winter
|
||||
* @create 2023-11-22 16:16
|
||||
*/
|
||||
@Data
|
||||
public class UpdateNodeDto {
|
||||
@ApiModelProperty(value = "学段 id", required = true)
|
||||
@NotNull(message = "学段 id 不能为空")
|
||||
String periodId;
|
||||
@ApiModelProperty(value = "评价项节点的 id")
|
||||
String id;
|
||||
String name;
|
||||
String logo;
|
||||
Integer order;
|
||||
boolean isPraise;
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
package cn.teammodel.model.dto.admin.appraise;
|
||||
|
||||
import lombok.Data;
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
/**
|
||||
* @author winter
|
||||
* @create 2023-12-06 19:03
|
||||
*/
|
||||
@Data
|
||||
public class TimeRangeDto {
|
||||
private Long startTime;
|
||||
private Long endTime;
|
||||
@NotNull
|
||||
// todo: 似乎不需要(有时间范围就不需要这个字段来划分时间,如果不传时间那就需要)
|
||||
private String academicYearId;
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
package cn.teammodel.model.dto.admin.appraise;
|
||||
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
/**
|
||||
* @author winter
|
||||
* @create 2023-12-13 17:34
|
||||
*/
|
||||
@Data
|
||||
public class UpdateAchievementRuleDto {
|
||||
@NotNull
|
||||
@ApiModelProperty("学段 id")
|
||||
private String periodId;
|
||||
@ApiModelProperty("更新的 rule 节点: 将会直接覆盖老节点")
|
||||
private UpdateRule updateRule;
|
||||
|
||||
@Data
|
||||
public static class UpdateRule {
|
||||
private String id;
|
||||
/**
|
||||
* 等级名称
|
||||
*/
|
||||
private String name = "";
|
||||
@ApiModelProperty("等级 logo")
|
||||
private String logo = "";
|
||||
@ApiModelProperty("每次所需表扬数")
|
||||
private Integer levelCount = 0;
|
||||
@ApiModelProperty("晋级所需下一等级所需当前等级次数")
|
||||
private Integer promotionLevel = 0;
|
||||
}
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
package cn.teammodel.model.dto.admin.art;
|
||||
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
@Data
|
||||
public class ArtFindDto
|
||||
{
|
||||
@ApiModelProperty("学校编码")
|
||||
private String code;
|
||||
@ApiModelProperty("学段ID")
|
||||
private String periodId;
|
||||
@ApiModelProperty("学段类型")
|
||||
private String periodType;
|
||||
@ApiModelProperty("开始时间")
|
||||
private Long startTime;
|
||||
@ApiModelProperty("结束时间")
|
||||
private Long endTime;
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
package cn.teammodel.model.dto.admin.common;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class GCDto {
|
||||
private String schoolId;
|
||||
private String periodId;
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
package cn.teammodel.model.dto.admin.common;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
public class GroupDto {
|
||||
public List<String> ids;
|
||||
public String schoolId;
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
package cn.teammodel.model.dto.admin.weekduty;
|
||||
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
/**
|
||||
* @author winter
|
||||
* @create 2024-01-15 10:30
|
||||
*/
|
||||
@Data
|
||||
public class AdminFindDutyRecordDto {
|
||||
@ApiModelProperty("老师 id")
|
||||
private String teacherId;
|
||||
@ApiModelProperty(value = "班级 id", notes = "若不携带默认不指定某个班级")
|
||||
private String classId;
|
||||
@ApiModelProperty(required = true)
|
||||
@NotNull(message = "academicYearid 不能为空")
|
||||
private String academicYearId;
|
||||
@ApiModelProperty(value = "是否加分", notes = "若不携带默认为全部")
|
||||
private Boolean positive;
|
||||
@ApiModelProperty(value = "起始时间", notes = "若不携带默认为当前周")
|
||||
private Long startTime;
|
||||
@ApiModelProperty(value = "结束时间", notes = "若不携带默认为当前周")
|
||||
private Long endTime;
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
package cn.teammodel.model.dto.ai;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
|
||||
@Data
|
||||
public class ChatCompletionReqDto {
|
||||
private String sessionId;
|
||||
/**
|
||||
* 预设的会话面具 id
|
||||
*/
|
||||
private String appId;
|
||||
@NotBlank(message = "请输入消息内容")
|
||||
private String text;
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
package cn.teammodel.model.dto.ai;
|
||||
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
|
||||
@Data
|
||||
public class CreateChatAppDto {
|
||||
@ApiModelProperty("应用图标")
|
||||
private String icon;
|
||||
|
||||
@ApiModelProperty("应用名称")
|
||||
@NotBlank(message = "请输入应用名称")
|
||||
private String name;
|
||||
private String lang;
|
||||
private String bizType;
|
||||
@ApiModelProperty("应用域")
|
||||
@NotBlank(message = "请输入应用域")
|
||||
private String scope;
|
||||
private String itemType;;
|
||||
private String period;
|
||||
private String subject;
|
||||
@ApiModelProperty("应用描述")
|
||||
private String description;
|
||||
|
||||
@NotBlank(message = "请输入应用提示词")
|
||||
@ApiModelProperty("应用提示词")
|
||||
private String prompt;
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
package cn.teammodel.model.dto.ai;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
|
||||
/**
|
||||
* @author winter
|
||||
* @create 2024-02-01 15:40
|
||||
*/
|
||||
@Data
|
||||
public class SearchAppDto {
|
||||
@NotBlank(message = "scope不能为空")
|
||||
private String scope;
|
||||
private String bizType;
|
||||
private String lang;
|
||||
private String itemType;;
|
||||
private String period;
|
||||
private String subject;
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
package cn.teammodel.model.dto.ai;
|
||||
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
|
||||
@Data
|
||||
public class UpdateChatAppDto {
|
||||
@ApiModelProperty("应用 id")
|
||||
@NotBlank(message = "请输入应用 id")
|
||||
private String id;
|
||||
|
||||
@ApiModelProperty("应用图标")
|
||||
private String icon;
|
||||
|
||||
@ApiModelProperty("应用名称")
|
||||
@NotBlank(message = "请输入应用名称")
|
||||
private String name;
|
||||
private String lang;
|
||||
private String bizType;
|
||||
@ApiModelProperty("应用域")
|
||||
@NotBlank(message = "请输入应用域")
|
||||
private String scope;
|
||||
private String itemType;;
|
||||
private String period;
|
||||
private String subject;
|
||||
@ApiModelProperty("应用描述")
|
||||
private String description;
|
||||
|
||||
@NotBlank(message = "请输入应用提示词")
|
||||
@ApiModelProperty("应用提示词")
|
||||
private String prompt;
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
package cn.teammodel.model.dto.ai;
|
||||
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
|
||||
/**
|
||||
* @author winter
|
||||
* @create 2023-12-19 15:42
|
||||
*/
|
||||
@Data
|
||||
public class UpdateSessionDto {
|
||||
@ApiModelProperty(value = "session id", required = true)
|
||||
@NotBlank
|
||||
private String id;
|
||||
private String title;
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
package cn.teammodel.model.dto.news;
|
||||
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
|
||||
@Data
|
||||
public class CreateNewsDto {
|
||||
@ApiModelProperty("新闻标题")
|
||||
@NotBlank(message = "新闻标题不能为空")
|
||||
private String title;
|
||||
|
||||
@ApiModelProperty("学段ID")
|
||||
@NotBlank(message = "学段ID不能为空")
|
||||
private String periodId;
|
||||
|
||||
@ApiModelProperty("新闻描述")
|
||||
private String desc;
|
||||
|
||||
@ApiModelProperty("新闻正文")
|
||||
@NotBlank(message = "新闻正文不能为空")
|
||||
private String content;
|
||||
|
||||
@ApiModelProperty("新闻封面")
|
||||
private String cover;
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
package cn.teammodel.model.dto.news;
|
||||
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
|
||||
@Data
|
||||
public class UpdateNewsDto {
|
||||
@ApiModelProperty("新闻 id")
|
||||
private String id;
|
||||
|
||||
@ApiModelProperty("新闻标题")
|
||||
@NotBlank(message = "新闻标题不能为空")
|
||||
private String title;
|
||||
|
||||
@ApiModelProperty("学段ID")
|
||||
@NotBlank(message = "学段ID不能为空")
|
||||
private String periodId;
|
||||
|
||||
@ApiModelProperty("新闻描述")
|
||||
private String desc;
|
||||
|
||||
@ApiModelProperty("新闻正文")
|
||||
@NotBlank(message = "新闻正文不能为空")
|
||||
private String content;
|
||||
|
||||
@ApiModelProperty("新闻封面")
|
||||
private String cover;
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
package cn.teammodel.model.dto.weekDuty;
|
||||
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
/**
|
||||
* @author winter
|
||||
* @create 2024-01-03 10:56
|
||||
*/
|
||||
@Data
|
||||
public class DeleteDutyNodeDto {
|
||||
@ApiModelProperty("要删除的节点 id")
|
||||
@NotNull
|
||||
private String nodeId;
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
package cn.teammodel.model.dto.weekDuty;
|
||||
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author winter
|
||||
* @create 2024-01-03 15:05
|
||||
*/
|
||||
@Data
|
||||
public class DutyVoteDto {
|
||||
@NotNull
|
||||
@ApiModelProperty(value = "班级id", required = true)
|
||||
private String classId;
|
||||
// 拍照巡检暂时不用地点 id
|
||||
@NotNull
|
||||
@ApiModelProperty(value = "评价地点 id")
|
||||
private String spotId;
|
||||
@NotNull
|
||||
@ApiModelProperty(value = "评价节点id", required = true)
|
||||
private String dutyNodeId;
|
||||
@ApiModelProperty(value = "评分是否传播到学生")
|
||||
private Boolean spread = false;
|
||||
private String note;
|
||||
private List<String> attachments;
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
package cn.teammodel.model.dto.weekDuty;
|
||||
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
/**
|
||||
* @author winter
|
||||
* @create 2024-01-08 15:31
|
||||
*/
|
||||
@Data
|
||||
public class FindDutyRecordDto {
|
||||
@ApiModelProperty(value = "班级 id", notes = "若不携带默认不指定某个班级")
|
||||
private String classId;
|
||||
@ApiModelProperty(required = true)
|
||||
@NotNull(message = "学段 id 不能为空")
|
||||
private String periodId;
|
||||
@ApiModelProperty(value = "是否加分", notes = "若不携带默认为全部")
|
||||
private Boolean positive;
|
||||
@ApiModelProperty(value = "起始时间", notes = "若不携带默认为当前周")
|
||||
private Long startTime;
|
||||
@ApiModelProperty(value = "结束时间", notes = "若不携带默认为当前周")
|
||||
private Long endTime;
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
package cn.teammodel.model.dto.weekDuty;
|
||||
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.PositiveOrZero;
|
||||
|
||||
/**
|
||||
* @author winter
|
||||
* @create 2024-01-03 10:45
|
||||
*/
|
||||
|
||||
@Data
|
||||
public class InsertDutyNodeDto {
|
||||
@ApiModelProperty(value = "父亲节点,不传则为根节点")
|
||||
String pid;
|
||||
@ApiModelProperty(value = "节点名称", required = true)
|
||||
@NotBlank(message = "name 不能为空")
|
||||
String name;
|
||||
String desc;
|
||||
Integer order = 0;
|
||||
@PositiveOrZero(message = "score 不能为负数")
|
||||
Integer score = 0;
|
||||
Boolean positive = true;
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
package cn.teammodel.model.dto.weekDuty;
|
||||
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
|
||||
/**
|
||||
* @author winter
|
||||
* @create 2024-01-03 10:45
|
||||
*/
|
||||
|
||||
@Data
|
||||
public class InsertSpotDto {
|
||||
@ApiModelProperty(value = "地点名称", required = true)
|
||||
@NotBlank(message = "name 不能为空")
|
||||
private String name;
|
||||
}
|
@ -0,0 +1,40 @@
|
||||
package cn.teammodel.model.dto.weekDuty;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
public class LessonRecordDto {
|
||||
private String tmdid;
|
||||
@NotNull
|
||||
private String scope;
|
||||
@NotNull
|
||||
private String school;
|
||||
private String name;
|
||||
@NotNull
|
||||
private String periodId;
|
||||
private Long stime;
|
||||
private Long etime;
|
||||
private List<String> category;
|
||||
private List<String> subjectId;
|
||||
private List<String> grade;
|
||||
@Value("${lessonRecordDto.doubleGreen:false}")
|
||||
private boolean doubleGreen ;
|
||||
@Value("${lessonRecordDto.singleGreen:false}")
|
||||
private boolean singleGreen ;
|
||||
@Value("${lessonRecordDto.isOk:true}")
|
||||
private boolean isOk ;
|
||||
@Value("${lessonRecordDto.quality:false}")
|
||||
private boolean quality;
|
||||
@Value("${lessonRecordDto.desc:startTime}")
|
||||
private String desc;
|
||||
private int pageCount;
|
||||
private String continuationToken;
|
||||
@Value("${lessonRecordDto.managePage:true}")
|
||||
private boolean managePage;
|
||||
private boolean classMeeting;
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
package cn.teammodel.model.dto.weekDuty;
|
||||
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
/**
|
||||
* @author winter
|
||||
* @create 2023-11-22 16:16
|
||||
*/
|
||||
@Data
|
||||
public class RecallDutyVoteDto {
|
||||
@NotNull
|
||||
@ApiModelProperty(value = "值周记录的文档id", required = true)
|
||||
String recordId;
|
||||
@NotNull
|
||||
@ApiModelProperty(value = "值周记录的具体节点id", required = true)
|
||||
String nodeId;
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
package cn.teammodel.model.dto.weekDuty;
|
||||
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
import javax.validation.constraints.PositiveOrZero;
|
||||
|
||||
/**
|
||||
* @author winter
|
||||
* @create 2024-01-03 10:59
|
||||
*/
|
||||
@Data
|
||||
@ApiModel("覆盖原节点")
|
||||
public class UpdateDutyNodeDto {
|
||||
@ApiModelProperty(value = "待修改的节点 id")
|
||||
@NotNull
|
||||
private String id;
|
||||
private String name;
|
||||
private String desc;
|
||||
private Boolean positive = true;
|
||||
private Integer order = 0;
|
||||
@PositiveOrZero(message = "分数不能为负数")
|
||||
private Integer score = 0;
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
package cn.teammodel.model.entity;
|
||||
|
||||
import com.azure.spring.data.cosmos.core.mapping.GeneratedValue;
|
||||
import com.azure.spring.data.cosmos.core.mapping.PartitionKey;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import lombok.Data;
|
||||
import org.springframework.data.annotation.Id;
|
||||
|
||||
/**
|
||||
* @author winter
|
||||
* @create 2023-11-27 11:05
|
||||
*/
|
||||
@Data
|
||||
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||
public class BaseItem {
|
||||
@Id
|
||||
@GeneratedValue
|
||||
private String id;
|
||||
/**
|
||||
* 分区键
|
||||
*/
|
||||
@PartitionKey
|
||||
private String code;
|
||||
}
|
@ -0,0 +1,54 @@
|
||||
package cn.teammodel.model.entity;
|
||||
|
||||
import io.jsonwebtoken.Claims;
|
||||
import lombok.Data;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
|
||||
/**
|
||||
* @author winter
|
||||
* @create 2023-11-09 15:28
|
||||
*/
|
||||
@Data
|
||||
public class TmdUserDetail implements UserDetails {
|
||||
private User user;
|
||||
|
||||
private Claims claims;
|
||||
@Override
|
||||
public Collection<? extends GrantedAuthority> getAuthorities() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPassword() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUsername() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAccountNonExpired() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAccountNonLocked() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCredentialsNonExpired() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabled() {
|
||||
return true;
|
||||
}
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
package cn.teammodel.model.entity;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.ToString;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author winter
|
||||
* @create 2023-11-09 15:43
|
||||
*/
|
||||
@Data
|
||||
@ToString
|
||||
public class User {
|
||||
private String id;
|
||||
private String name;
|
||||
/**
|
||||
* 学校 id
|
||||
*/
|
||||
private String schoolId;
|
||||
private String picture;
|
||||
private String standard;
|
||||
/**
|
||||
* 用户身份
|
||||
*/
|
||||
private String scope;
|
||||
private String website;
|
||||
/**
|
||||
* 区级 id
|
||||
*/
|
||||
private String area;
|
||||
private Set<String> roles;
|
||||
private Set<String> permissions;
|
||||
}
|
@ -0,0 +1,55 @@
|
||||
package cn.teammodel.model.entity.ai;
|
||||
|
||||
import cn.teammodel.model.entity.BaseItem;
|
||||
import com.azure.spring.data.cosmos.core.mapping.Container;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
/**
|
||||
* 聊天应用(面具)
|
||||
* code: ChatApp
|
||||
* @author winter
|
||||
* @create 2024-01-23 11:08
|
||||
*/
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Container(containerName = "School")
|
||||
@Data
|
||||
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||
public class ChatApp extends BaseItem {
|
||||
/**
|
||||
* 租户 id (telnet id)
|
||||
*/
|
||||
private String schoolId;
|
||||
/**
|
||||
* 图标
|
||||
*/
|
||||
private String icon;
|
||||
private String name;
|
||||
private String bizType;
|
||||
private String scope;
|
||||
/**
|
||||
* 语言
|
||||
*/
|
||||
private String lang;
|
||||
/**
|
||||
* 题目类型
|
||||
*/
|
||||
private String itemType;
|
||||
/**
|
||||
* 学段
|
||||
*/
|
||||
private String period;
|
||||
/**
|
||||
* 学科
|
||||
*/
|
||||
private String subject;
|
||||
private String description;
|
||||
/**
|
||||
* 面具提示词
|
||||
*/
|
||||
private String prompt;
|
||||
private String creator;
|
||||
private String creatorId;
|
||||
private Long createTime;
|
||||
}
|
@ -0,0 +1,58 @@
|
||||
package cn.teammodel.model.entity.ai;
|
||||
|
||||
import cn.hutool.core.lang.UUID;
|
||||
import cn.teammodel.model.entity.BaseItem;
|
||||
import com.azure.spring.data.cosmos.core.mapping.Container;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import lombok.*;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 聊天会话,绑定 teacherId(userId), 主键id: sessionId
|
||||
* @author winter
|
||||
* @create 2023-12-19 15:09
|
||||
*/
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Container(containerName = "Teacher")
|
||||
@Data
|
||||
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||
public class ChatSession extends BaseItem {
|
||||
/**
|
||||
* 会话名称
|
||||
*/
|
||||
private String title;
|
||||
/**
|
||||
* 用户 id
|
||||
*/
|
||||
private String userId;
|
||||
private Long createTime;
|
||||
/**
|
||||
* 产生对话即更新时间,按更新时间排序
|
||||
*/
|
||||
private Long updateTime;
|
||||
private List<Message> history;
|
||||
|
||||
@Data
|
||||
public static class Message {
|
||||
private String id;
|
||||
private String userText;
|
||||
private String gptText;
|
||||
/**
|
||||
* 消耗的 point
|
||||
*/
|
||||
private Integer cost;
|
||||
private Long createTime;
|
||||
|
||||
public static Message of(String userText, String gptText) {
|
||||
Message message = new Message();
|
||||
message.setId(UUID.randomUUID().toString());
|
||||
message.setCost(0);
|
||||
message.setUserText(userText);
|
||||
message.setGptText(gptText);
|
||||
message.setCreateTime(Instant.now().toEpochMilli());
|
||||
return message;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
package cn.teammodel.model.entity.appraise;
|
||||
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 成就的晋级规则
|
||||
* @author winter
|
||||
* @create 2023-12-13 15:23
|
||||
*/
|
||||
@Data
|
||||
public class AchievementRule {
|
||||
private String id;
|
||||
@ApiModelProperty("等级名称")
|
||||
private String name;
|
||||
@ApiModelProperty("等级 logo")
|
||||
private String logo;
|
||||
@ApiModelProperty("等级 logo")
|
||||
private Integer level;
|
||||
@ApiModelProperty("等级顺序")
|
||||
private Integer levelCount;
|
||||
@ApiModelProperty("晋级所需下一等级所需当前等级次数")
|
||||
private Integer promotionLevel;
|
||||
@ApiModelProperty("晋升到下一等级所需总表扬数")
|
||||
private Integer promotionCount;
|
||||
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
package cn.teammodel.model.entity.appraise;
|
||||
|
||||
import cn.teammodel.model.entity.BaseItem;
|
||||
import com.azure.spring.data.cosmos.core.mapping.Container;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 评价列表
|
||||
* @author winter
|
||||
* @create 2023-11-20 11:04
|
||||
*/
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Container(containerName = "School")
|
||||
@Data
|
||||
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||
public class Appraise extends BaseItem {
|
||||
/**
|
||||
* 学校 Id
|
||||
*/
|
||||
private String schoolId;
|
||||
/**
|
||||
* 学段 id (默认 default, 模板为 template)
|
||||
*/
|
||||
private String periodId;
|
||||
/**
|
||||
* 评价树的扁平化节点
|
||||
*/
|
||||
private List<AppraiseTreeNode> nodes;
|
||||
/**
|
||||
* 成就等级节点
|
||||
*/
|
||||
private List<AchievementRule> achievementRules;
|
||||
}
|
@ -0,0 +1,59 @@
|
||||
package cn.teammodel.model.entity.appraise;
|
||||
|
||||
import cn.teammodel.model.entity.BaseItem;
|
||||
import com.azure.spring.data.cosmos.core.mapping.Container;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 一个学生在一个学年(学校+学年 = 分区键)的所有评价记录 <br/>
|
||||
* 注意: 唯一定位: academicYearId(学年-{semesterId}) + studentId + code(AppraiseRecord-{schoolId})
|
||||
* @author winter
|
||||
* @create 2023-11-27 11:02
|
||||
*/
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Container(containerName = "Student")
|
||||
@Data
|
||||
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||
public class AppraiseRecord extends BaseItem {
|
||||
/**
|
||||
* 学段 id
|
||||
*/
|
||||
private String periodId;
|
||||
/**
|
||||
* 班级 id
|
||||
*/
|
||||
private String classId;
|
||||
private String className;
|
||||
/**
|
||||
* 学年(组合 id: 学年-semesterId -> 2023-{semesterId})
|
||||
*/
|
||||
private String academicYearId;
|
||||
/**
|
||||
* 学生/class id
|
||||
*/
|
||||
private String targetId;
|
||||
private String targetType;
|
||||
/**
|
||||
* 学生/class 名字
|
||||
*/
|
||||
private String name;
|
||||
/**
|
||||
* 学生/class 头像
|
||||
*/
|
||||
private String avatar;
|
||||
private String gender;
|
||||
/**
|
||||
* 表扬次数
|
||||
*/
|
||||
private Integer praiseCount;
|
||||
/**
|
||||
* 学生得分
|
||||
*/
|
||||
private Integer score;
|
||||
private List<AppraiseRecordItem> nodes;
|
||||
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
package cn.teammodel.model.entity.appraise;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 评价项
|
||||
* @author winter
|
||||
* @create 2023-11-27 16:47
|
||||
*/
|
||||
|
||||
@Data
|
||||
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||
public class AppraiseRecordItem {
|
||||
private String id;
|
||||
private AppraiseTreeNode appraiseNode;
|
||||
/**
|
||||
* 用包装类,这样为 null 时就不会持久化
|
||||
*/
|
||||
private Boolean spread;
|
||||
private Boolean pushParent;
|
||||
String creator;
|
||||
String creatorId;
|
||||
private Long createTime;
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
package cn.teammodel.model.entity.appraise;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||
public class AppraiseTreeNode {
|
||||
String id;
|
||||
String pid;
|
||||
String name;
|
||||
String[] path;
|
||||
String logo;
|
||||
String creator;
|
||||
String creatorId;
|
||||
Long createTime;
|
||||
/**
|
||||
* 排序
|
||||
*/
|
||||
Integer order;
|
||||
/**
|
||||
* 分值
|
||||
*/
|
||||
Integer score;
|
||||
/**
|
||||
* 是否表扬? 默认 true
|
||||
*/
|
||||
@JsonProperty("isPraise")
|
||||
boolean isPraise;
|
||||
|
||||
List<AppraiseTreeNode> children;
|
||||
}
|
@ -0,0 +1,91 @@
|
||||
package cn.teammodel.model.entity.common;
|
||||
|
||||
import cn.teammodel.model.entity.BaseItem;
|
||||
import com.azure.spring.data.cosmos.core.mapping.Container;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Container(containerName = "Common")
|
||||
@Data
|
||||
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||
public class Art extends BaseItem {
|
||||
private String name;
|
||||
private String school;
|
||||
public List<Tasks> settings;
|
||||
public String creatorId;
|
||||
public String createTime;
|
||||
public String updateTime;
|
||||
public int type;
|
||||
public List<String> classes;
|
||||
public List<String> stuLists;
|
||||
public List<String> tchLists;
|
||||
public List<String> sIds;
|
||||
public String progress;
|
||||
public String scope;
|
||||
public int status;
|
||||
public long size;
|
||||
public String owner;
|
||||
public String areaId;
|
||||
public String pId;
|
||||
public String presenter;
|
||||
public String topic;
|
||||
public long startTime;
|
||||
public long endTime;
|
||||
public long uploadSTime;
|
||||
public long uploadETime;
|
||||
public String uploadProgress;
|
||||
public String address;
|
||||
public String pk;
|
||||
public String desc;
|
||||
public String img;
|
||||
public int publish;
|
||||
public List<ArtSubject> subjects;
|
||||
public ArtPeriod period;
|
||||
public String periodType;
|
||||
public List<LostStudent> lost;
|
||||
public int pass;
|
||||
public List<Integer> miss;
|
||||
public List<Zymusicstd> zymusicstds;
|
||||
public static class Acs {
|
||||
public String infoId ;
|
||||
public String acId ;
|
||||
public String name ;
|
||||
public String subject ;
|
||||
public int isOrder ;
|
||||
public int type ;
|
||||
public String workDesc ;
|
||||
public long workEnd ;
|
||||
|
||||
}
|
||||
public static class Tasks {
|
||||
public String id ;
|
||||
public String quotaname ;
|
||||
public List<String> path ;
|
||||
public List<Acs> task ;
|
||||
}
|
||||
public static class ArtPeriod
|
||||
{
|
||||
public String id ;
|
||||
public String name ;
|
||||
}
|
||||
public static class ArtSubject {
|
||||
public String id ;
|
||||
public String name ;
|
||||
}
|
||||
public static class LostStudent {
|
||||
public String code ;
|
||||
public String subject ;
|
||||
public double stu ;
|
||||
}
|
||||
private static class Zymusicstd
|
||||
{
|
||||
public String code ;
|
||||
public String label ;
|
||||
public double percent ;
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,27 @@
|
||||
package cn.teammodel.model.entity.news;
|
||||
|
||||
import cn.teammodel.model.entity.BaseItem;
|
||||
import com.azure.spring.data.cosmos.core.mapping.Container;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
/**
|
||||
* code: News-{schoolId}
|
||||
* @author winter
|
||||
* @create 2024-02-26 17:05
|
||||
*/
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Container(containerName = "School")
|
||||
@Data
|
||||
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||
public class News extends BaseItem {
|
||||
private String title;
|
||||
private String periodId;
|
||||
private String desc;
|
||||
private String content;
|
||||
private String cover;
|
||||
private String creator;
|
||||
private String creatorId;
|
||||
private Long createTime;
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
package cn.teammodel.model.entity.school;
|
||||
|
||||
import cn.teammodel.model.entity.BaseItem;
|
||||
import com.azure.spring.data.cosmos.core.mapping.Container;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Container(containerName = "School")
|
||||
@Data
|
||||
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||
public class ClassInfo extends BaseItem {
|
||||
private String no;
|
||||
private String name;
|
||||
private Teacher teacher;
|
||||
private String periodId;
|
||||
private Integer year;
|
||||
private String room;
|
||||
private String school;
|
||||
private Integer graduate;
|
||||
private Integer grade;
|
||||
private String pk;
|
||||
private Integer ttl;
|
||||
|
||||
@Data
|
||||
public static class Teacher {
|
||||
private String id;
|
||||
private String name;
|
||||
}
|
||||
}
|
@ -0,0 +1,139 @@
|
||||
package cn.teammodel.model.entity.school;
|
||||
|
||||
import cn.teammodel.model.entity.BaseItem;
|
||||
import com.azure.spring.data.cosmos.core.mapping.Container;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Container(containerName = "School")
|
||||
@Data
|
||||
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||
public class School extends BaseItem {
|
||||
/**
|
||||
* 学校名字
|
||||
*/
|
||||
private String name;
|
||||
private String schoolCode;
|
||||
private String region;
|
||||
private String province;
|
||||
private String city;
|
||||
private String dist;
|
||||
private String areaId;
|
||||
private Integer size;
|
||||
private Integer tsize;
|
||||
private String address;
|
||||
private String picture;
|
||||
private TimeZone timeZone;
|
||||
private Integer type;
|
||||
private String standard;
|
||||
private Integer hpappraise;
|
||||
private Integer scale;
|
||||
private Edition edition;
|
||||
private long createTime;
|
||||
private boolean isinit;
|
||||
private boolean openLessonRecord;
|
||||
//private Module[] modules;
|
||||
private String pk;
|
||||
private List<Period> period;
|
||||
private List<Campus> campuses;
|
||||
|
||||
@Data
|
||||
public static class Period {
|
||||
private List<Major> majors;
|
||||
private List<String> grades;
|
||||
private List<Subject> subjects;
|
||||
private List<Semester> semesters;
|
||||
private List<Semester> timetable;
|
||||
private String name;
|
||||
private String id;
|
||||
private String gradeCount;
|
||||
private String semesterCount;
|
||||
private String subjectCount;
|
||||
private String campusId;
|
||||
private Analysis analysis;
|
||||
// 学段类型
|
||||
private String periodType;
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class Major {
|
||||
private String id;
|
||||
private String name;
|
||||
}
|
||||
@Data
|
||||
public static class Timetable {
|
||||
private String id;
|
||||
private String label;
|
||||
private String time;
|
||||
// todo: 待补充
|
||||
//private String weeklies;
|
||||
}
|
||||
@Data
|
||||
public static class Analysis {
|
||||
private List<Type> type;
|
||||
private Integer income;
|
||||
private Integer eugenics;
|
||||
private Integer touch;
|
||||
}
|
||||
@Data
|
||||
public static class Type {
|
||||
private String id;
|
||||
private String name;
|
||||
}
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public static class Subject {
|
||||
private String id;
|
||||
private String name;
|
||||
private Integer type;
|
||||
private String bindId;
|
||||
}
|
||||
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public static class Semester {
|
||||
private String id;
|
||||
private String name;
|
||||
/**
|
||||
* start = 1 则代表一年开始的学期
|
||||
*/
|
||||
private Integer start;
|
||||
/**
|
||||
* 开始的月份
|
||||
*/
|
||||
private Integer month;
|
||||
/**
|
||||
* 开始的日
|
||||
*/
|
||||
private Integer day;
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class Campus {
|
||||
private String name;
|
||||
private String id;
|
||||
|
||||
}
|
||||
@Data
|
||||
public static class TimeZone {
|
||||
private String label;
|
||||
private String value;
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class Edition {
|
||||
private Integer current;
|
||||
private Integer record;
|
||||
private String scaleVersion;
|
||||
}
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
package cn.teammodel.model.entity.school;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 学校的配置项
|
||||
* @author winter
|
||||
* @create 2023-12-13 11:44
|
||||
*/
|
||||
@Data
|
||||
public class SchoolConfig {
|
||||
/**
|
||||
* 学校积分名字
|
||||
*/
|
||||
private String scoreName = "醍摩豆";
|
||||
}
|
@ -0,0 +1,49 @@
|
||||
package cn.teammodel.model.entity.school;
|
||||
|
||||
import com.azure.spring.data.cosmos.core.mapping.Container;
|
||||
import com.azure.spring.data.cosmos.core.mapping.PartitionKey;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import lombok.Data;
|
||||
import org.springframework.data.annotation.Id;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
|
||||
@Data
|
||||
@Container(containerName = "Student")
|
||||
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||
public class Student {
|
||||
@Id
|
||||
private String id;
|
||||
@PartitionKey
|
||||
private String code;
|
||||
private String mail;
|
||||
private String mobile;
|
||||
private String country;
|
||||
private String name;
|
||||
private String picture;
|
||||
private String schoolId;
|
||||
private String pw;
|
||||
private String salt;
|
||||
private Integer year;
|
||||
private String no;
|
||||
private String irs;
|
||||
private String classId;
|
||||
private String groupId;
|
||||
private String groupName;
|
||||
private String periodId;
|
||||
private String gender;
|
||||
private Integer graduate;
|
||||
private List<LoginInfo> loginInfos;
|
||||
private Long createTime;
|
||||
//private List<String> guardians;
|
||||
private String pk;
|
||||
|
||||
@Data
|
||||
public static class LoginInfo {
|
||||
private Long time;
|
||||
private String ip;
|
||||
private Long expire;
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,53 @@
|
||||
package cn.teammodel.model.entity.school;
|
||||
|
||||
import cn.teammodel.model.entity.BaseItem;
|
||||
import com.azure.spring.data.cosmos.core.mapping.Container;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Container(containerName = "Teacher")
|
||||
@Data
|
||||
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||
public class Teacher extends BaseItem {
|
||||
private String name;
|
||||
private String picture;
|
||||
private int size;
|
||||
private String defaultSchool;
|
||||
private List<School> schools;
|
||||
private List<Area> areas;
|
||||
private List<LoginInfo> loginInfos;
|
||||
private long createTime;
|
||||
//private List<String> binds;
|
||||
private int lessonLimit;
|
||||
//private List<String> lessonShow;
|
||||
private String lang;
|
||||
private String pk;
|
||||
|
||||
@Data
|
||||
public static class School {
|
||||
private String schoolId;
|
||||
private String name;
|
||||
private String status;
|
||||
private long time;
|
||||
private String picture;
|
||||
private String areaId;
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class Area {
|
||||
private String areaId;
|
||||
private String name;
|
||||
private String status;
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class LoginInfo {
|
||||
private long time;
|
||||
private String ip;
|
||||
private long expire;
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue