作者 钟来

修改bug

正在显示 29 个修改的文件 包含 2413 行增加75 行删除
1 package com.zhonglai.luhui.openai.controller; 1 package com.zhonglai.luhui.openai.controller;
2 2
3 import cn.hutool.http.HttpUtil; 3 import cn.hutool.http.HttpUtil;
  4 +import com.alibaba.fastjson.JSON;
4 import com.alibaba.fastjson.JSONObject; 5 import com.alibaba.fastjson.JSONObject;
5 import com.ruoyi.common.core.controller.BaseController; 6 import com.ruoyi.common.core.controller.BaseController;
6 import com.ruoyi.common.core.domain.AjaxResult; 7 import com.ruoyi.common.core.domain.AjaxResult;
@@ -20,12 +21,14 @@ import com.theokanning.openai.Usage; @@ -20,12 +21,14 @@ import com.theokanning.openai.Usage;
20 import com.theokanning.openai.completion.CompletionChoice; 21 import com.theokanning.openai.completion.CompletionChoice;
21 import com.theokanning.openai.completion.CompletionResult; 22 import com.theokanning.openai.completion.CompletionResult;
22 import com.zhonglai.luhui.openai.dto.*; 23 import com.zhonglai.luhui.openai.dto.*;
  24 +import com.zhonglai.luhui.openai.service.VipServiceImpl;
23 import com.zhonglai.luhui.openai.utils.OpenAiUtils; 25 import com.zhonglai.luhui.openai.utils.OpenAiUtils;
24 import com.zhonglai.luhui.openai.utils.SysConfigKeyType; 26 import com.zhonglai.luhui.openai.utils.SysConfigKeyType;
25 import io.swagger.annotations.Api; 27 import io.swagger.annotations.Api;
26 import io.swagger.annotations.ApiOperation; 28 import io.swagger.annotations.ApiOperation;
27 import okhttp3.Request; 29 import okhttp3.Request;
28 import okhttp3.Response; 30 import okhttp3.Response;
  31 +import org.apache.catalina.Session;
29 import org.apache.commons.io.input.ReaderInputStream; 32 import org.apache.commons.io.input.ReaderInputStream;
30 import org.springframework.beans.factory.annotation.Autowired; 33 import org.springframework.beans.factory.annotation.Autowired;
31 import org.springframework.transaction.annotation.Transactional; 34 import org.springframework.transaction.annotation.Transactional;
@@ -38,10 +41,7 @@ import java.io.ByteArrayOutputStream; @@ -38,10 +41,7 @@ import java.io.ByteArrayOutputStream;
38 import java.io.IOException; 41 import java.io.IOException;
39 import java.io.InputStream; 42 import java.io.InputStream;
40 import java.math.BigDecimal; 43 import java.math.BigDecimal;
41 -import java.util.ArrayList;  
42 -import java.util.List;  
43 -import java.util.Map;  
44 -import java.util.Optional; 44 +import java.util.*;
45 import java.util.stream.Collectors; 45 import java.util.stream.Collectors;
46 46
47 @Api(tags = "chatGPT接口") 47 @Api(tags = "chatGPT接口")
@@ -51,9 +51,14 @@ public class ChatGPTController extends BaseController { @@ -51,9 +51,14 @@ public class ChatGPTController extends BaseController {
51 @Autowired 51 @Autowired
52 private PublicService publicService; 52 private PublicService publicService;
53 53
54 - @ApiOperation("与AI机器进行聊天") 54 + @Autowired
  55 + private VipServiceImpl vipService;
  56 +
  57 + private static String sessionkey = "CHAT_HISTORY_CONTEXT";//上下文关联存放地址
  58 +
  59 + @ApiOperation("与AI机器进行聊天(废弃)")
55 @RequestMapping(value = "/aiChatbot",method = RequestMethod.POST) 60 @RequestMapping(value = "/aiChatbot",method = RequestMethod.POST)
56 - public Message aiChatbot(HttpServletRequest httpServletRequest, @RequestBody String data) 61 + public AjaxResult aiChatbot(HttpServletRequest httpServletRequest, @RequestBody String data)
57 { 62 {
58 HttpSession session = httpServletRequest.getSession(); 63 HttpSession session = httpServletRequest.getSession();
59 if(session!=null) 64 if(session!=null)
@@ -66,10 +71,10 @@ public class ChatGPTController extends BaseController { @@ -66,10 +71,10 @@ public class ChatGPTController extends BaseController {
66 i+=completionChoice.getText().length(); 71 i+=completionChoice.getText().length();
67 } 72 }
68 logger.info("{}请求的流量:{},回复的流量:{}",session.getId(),data.length(),i); 73 logger.info("{}请求的流量:{},回复的流量:{}",session.getId(),data.length(),i);
69 - return new Message(MessageCode.DEFAULT_SUCCESS_CODE,list); 74 + return AjaxResult.success(list);
70 } 75 }
71 List<CompletionChoice> list = OpenAiUtils.getAiChatbot(data); 76 List<CompletionChoice> list = OpenAiUtils.getAiChatbot(data);
72 - return new Message(MessageCode.DEFAULT_SUCCESS_CODE,list); 77 + return AjaxResult.success(list);
73 } 78 }
74 79
75 private ChatRoomMessages mapToChatRoomMessages(Map<String,Object> src) 80 private ChatRoomMessages mapToChatRoomMessages(Map<String,Object> src)
@@ -79,58 +84,80 @@ public class ChatGPTController extends BaseController { @@ -79,58 +84,80 @@ public class ChatGPTController extends BaseController {
79 return chatRoomMessages; 84 return chatRoomMessages;
80 } 85 }
81 86
82 - @ApiOperation("chatgpt3.5") 87 + @ApiOperation(value = "历史记录",notes = "默认获取最新5条记录")
83 @Transactional 88 @Transactional
84 - @RequestMapping(value = "/chatgptV3_5",method = RequestMethod.POST)  
85 - public Message chatgptV3_5(HttpServletRequest httpServletRequest, @RequestBody String data) 89 + @RequestMapping(value = "/roomHistory",method = RequestMethod.GET)
  90 + public AjaxResult roomHistory()
86 { 91 {
  92 + //跟进用户信息生成入口参数
87 OpenAiLoginUser userInfo = (OpenAiLoginUser) getLoginUser(); 93 OpenAiLoginUser userInfo = (OpenAiLoginUser) getLoginUser();
88 Integer user_id= userInfo.getUserId().intValue(); 94 Integer user_id= userInfo.getUserId().intValue();
89 - OpenAiUserInfo openAiUserInfo = (OpenAiUserInfo) userInfo.getUser();  
90 - if(openAiUserInfo.getFlow_packet_remain()<=0)  
91 - {  
92 - return new Message(MessageCode.DEFAULT_FAIL_CODE,"余量不足"); 95 + String room_id = String.valueOf(user_id);
  96 + List<Map<String,Object>> list = publicService.getObjectListBySQL("SELECT * FROM `lk_openai`.`gpt_message` WHERE room_id="+room_id+" AND user_id="+user_id+" order by create_time desc 5");
  97 + Collections.reverse(list);
  98 + return AjaxResult.success(list);
93 } 99 }
94 100
95 - List<Map<String,Object>> unitpriceList = publicService.getObjectListBySQL("select `key`,`value` from `lk_openai`.`sys_config` where `key_type`='"+SysConfigKeyType.gpt_3_5_unitprice+"'");  
96 - BigDecimal openaiUnitprice = new BigDecimal(0);  
97 - BigDecimal realityUnitprice = new BigDecimal(0);  
98 - if(null != unitpriceList && unitpriceList.size() !=0)  
99 - {  
100 - for(Map<String,Object> map:unitpriceList)  
101 - {  
102 - if("openai".equals(map.get("key"))) 101 + @ApiOperation(value = "chatgpt3.5",notes = "上下文关联,要实现上下问关联就需要记录整个聊天的提问记录,目前只支持相同session的上下文关联,就是说,如果聊天页面关闭以后再打开,是没有上下文关联的")
  102 + @Transactional
  103 + @RequestMapping(value = "/chatgptV3_5",method = RequestMethod.POST)
  104 + public AjaxResult chatgptV3_5(HttpServletRequest httpServletRequest,@RequestBody String data)
103 { 105 {
104 - openaiUnitprice = new BigDecimal(map.get("value").toString());  
105 - }else if("reality".equals(map.get("key"))) 106 + //跟进用户信息生成入口参数
  107 + OpenAiLoginUser userInfo = (OpenAiLoginUser) getLoginUser();
  108 + Integer user_id= userInfo.getUserId().intValue();
  109 + String room_id = String.valueOf(user_id);
  110 +
  111 + OpenAiUserInfo openAiUserInfo = (OpenAiUserInfo) userInfo.getUser();
  112 +
  113 + List<CompletionChoiceMessage3_5> rlist = new ArrayList<>();
  114 +
  115 + //验证余额是否充足
  116 + if(vipService.isCharging(openAiUserInfo.getVip_level()) && openAiUserInfo.getFlow_packet_remain()<=0)
106 { 117 {
107 - realityUnitprice = new BigDecimal(map.get("value").toString());  
108 - }  
109 - } 118 + CompletionChoiceMessage3_5 completionChoiceMessage3_5 = new CompletionChoiceMessage3_5();
  119 + completionChoiceMessage3_5.setRole("assistant");
  120 + StringBuffer stringBuffer = new StringBuffer();
  121 + stringBuffer.append("您的余额不足请联系管理员或者点击链接充值:\\n\\n");
  122 + stringBuffer.append("https://充值链接.com");
  123 + completionChoiceMessage3_5.setContent(stringBuffer.toString());
  124 + rlist.add(completionChoiceMessage3_5);
  125 +
  126 + return AjaxResult.success(rlist);
110 } 127 }
  128 +
  129 + BigDecimal[] bs = vipService.getUnitprice();
  130 + BigDecimal openaiUnitprice = bs[0];
  131 + BigDecimal realityUnitprice = bs[1];
111 if(openaiUnitprice.intValue()==0 || realityUnitprice.intValue()==0) 132 if(openaiUnitprice.intValue()==0 || realityUnitprice.intValue()==0)
112 { 133 {
113 - return new Message(MessageCode.DEFAULT_FAIL_CODE,"系统未配置流量单价,请联系管理员"); 134 + rlist.add(new CompletionChoiceMessage3_5("assistant","系统未配置流量单价,请联系管理员"));
  135 + return AjaxResult.success(rlist);
114 } 136 }
115 137
116 - String room_id = String.valueOf(user_id);  
117 logger.info("{}生成聊天会话:",room_id); 138 logger.info("{}生成聊天会话:",room_id);
118 139
119 - List<Map<String,Object>> list = publicService.getObjectListBySQL("SELECT send_role role,send_content content FROM `lk_openai`.`gpt_message` WHERE room_id='"+room_id+"' AND user_id="+user_id+" order by create_time asc limit 5");  
120 -  
121 - List<ChatRoomMessages> messageList = Optional.ofNullable(list).orElse(new ArrayList<>()).stream().map(item->mapToChatRoomMessages(item)).collect(Collectors.toList());  
122 - 140 + //上下文关联
  141 + HttpSession session = httpServletRequest.getSession();
  142 + List<ChatRoomMessages> messageList = Optional.ofNullable((List<Map<String, Object>>) session.getAttribute(sessionkey)).orElse(new ArrayList<>()).stream().map(item->mapToChatRoomMessages(item)).collect(Collectors.toList());
123 ChatRoomMessages chatRoomMessages = new ChatRoomMessages(); 143 ChatRoomMessages chatRoomMessages = new ChatRoomMessages();
124 chatRoomMessages.setRole("user"); 144 chatRoomMessages.setRole("user");
125 chatRoomMessages.setContent(data); 145 chatRoomMessages.setContent(data);
126 messageList.add(chatRoomMessages); 146 messageList.add(chatRoomMessages);
  147 + session.setAttribute(sessionkey,messageList);
127 148
128 //获取返回参数 149 //获取返回参数
129 - CompletionResult3_5 completionResult3_5 = sendGPTAi(messageList); 150 + CompletionResult3_5 completionResult3_5 = null;
  151 + if(vipService.isfree(openAiUserInfo.getVip_level()))
  152 + {
  153 + completionResult3_5 = sendGPTAi(messageList);
  154 + }else{
  155 + completionResult3_5 = sendFreeGPTAi(messageList);
  156 + }
  157 +
130 Usage usage = completionResult3_5.getUsage(); 158 Usage usage = completionResult3_5.getUsage();
131 159
132 List<CompletionChoice3_5> list3_5 = completionResult3_5.getChoices(); 160 List<CompletionChoice3_5> list3_5 = completionResult3_5.getChoices();
133 - List<CompletionChoiceMessage3_5> rlist = new ArrayList<>();  
134 161
135 if(null != list3_5 && list3_5.size() !=0 ) 162 if(null != list3_5 && list3_5.size() !=0 )
136 { 163 {
@@ -150,6 +177,7 @@ public class ChatGPTController extends BaseController { @@ -150,6 +177,7 @@ public class ChatGPTController extends BaseController {
150 //统计代币 177 //统计代币
151 gptMessage.setCompletion_tokens(usage.getCompletionTokens()); 178 gptMessage.setCompletion_tokens(usage.getCompletionTokens());
152 gptMessage.setPrompt_tokens(usage.getPromptTokens()); 179 gptMessage.setPrompt_tokens(usage.getPromptTokens());
  180 + gptMessage.setTotal_tokens(usage.getTotalTokens());
153 }else{ 181 }else{
154 gptMessage.setSend_size(0); 182 gptMessage.setSend_size(0);
155 } 183 }
@@ -183,18 +211,31 @@ public class ChatGPTController extends BaseController { @@ -183,18 +211,31 @@ public class ChatGPTController extends BaseController {
183 UserFlowPacketRemainLog userFlowPacketRemainLog = new UserFlowPacketRemainLog(); 211 UserFlowPacketRemainLog userFlowPacketRemainLog = new UserFlowPacketRemainLog();
184 userFlowPacketRemainLog.setCreate_time(DateUtils.getNowTimeMilly()); 212 userFlowPacketRemainLog.setCreate_time(DateUtils.getNowTimeMilly());
185 userFlowPacketRemainLog.setUser_id(openAiUserInfo.getId()); 213 userFlowPacketRemainLog.setUser_id(openAiUserInfo.getId());
186 - userFlowPacketRemainLog.setRoom_id(room_id);  
187 - userFlowPacketRemainLog.setPrompt_tokens(usage.getPromptTokens());  
188 - userFlowPacketRemainLog.setCompletion_tokens(usage.getCompletionTokens()); 214 + userFlowPacketRemainLog.setType(2); //消费
  215 +
  216 + StringBuffer stringBuffer = new StringBuffer();
  217 + stringBuffer.append("房间号:");
  218 + stringBuffer.append(room_id);
  219 + stringBuffer.append("发送代币:");
  220 + stringBuffer.append(usage.getPromptTokens());
  221 + stringBuffer.append("返回代币:");
  222 + stringBuffer.append(usage.getCompletionTokens());
  223 + userFlowPacketRemainLog.setDescribe(stringBuffer.toString());
189 userFlowPacketRemainLog.setTotal_tokens(usage.getTotalTokens()); 224 userFlowPacketRemainLog.setTotal_tokens(usage.getTotalTokens());
  225 +
190 userFlowPacketRemainLog.setOpenai_money(openaiUnitprice.multiply(new BigDecimal(usage.getTotalTokens())).divide(new BigDecimal(1000),6)); 226 userFlowPacketRemainLog.setOpenai_money(openaiUnitprice.multiply(new BigDecimal(usage.getTotalTokens())).divide(new BigDecimal(1000),6));
191 userFlowPacketRemainLog.setReality_money(realityUnitprice.multiply(new BigDecimal(usage.getTotalTokens())).divide(new BigDecimal(1000),6)); 227 userFlowPacketRemainLog.setReality_money(realityUnitprice.multiply(new BigDecimal(usage.getTotalTokens())).divide(new BigDecimal(1000),6));
192 publicService.insertToTable(userFlowPacketRemainLog,"`lk_openai`.`user_flow_packet_remain_log`"); 228 publicService.insertToTable(userFlowPacketRemainLog,"`lk_openai`.`user_flow_packet_remain_log`");
193 } 229 }
194 - return new Message(MessageCode.DEFAULT_FAIL_CODE,rlist); 230 + return AjaxResult.success(rlist);
195 } 231 }
196 232
197 233
  234 + /**
  235 + * 付费接口
  236 + * @param messageList
  237 + * @return
  238 + */
198 private CompletionResult3_5 sendGPTAi(List<ChatRoomMessages> messageList) 239 private CompletionResult3_5 sendGPTAi(List<ChatRoomMessages> messageList)
199 { 240 {
200 JSONObject jsonObject = new JSONObject(); 241 JSONObject jsonObject = new JSONObject();
@@ -205,6 +246,21 @@ public class ChatGPTController extends BaseController { @@ -205,6 +246,21 @@ public class ChatGPTController extends BaseController {
205 return completionResult; 246 return completionResult;
206 } 247 }
207 248
  249 + /**
  250 + * 免费接口
  251 + * @param messageList
  252 + * @return
  253 + */
  254 + private CompletionResult3_5 sendFreeGPTAi(List<ChatRoomMessages> messageList)
  255 + {
  256 + JSONObject jsonObject = new JSONObject();
  257 + jsonObject.put("model","gpt-3.5-turbo-0301");
  258 + jsonObject.put("messages",messageList);
  259 + String str = HttpUtil.post("https://free.chatgpt.njlaikun.com/v1/chat/completions",jsonObject.toString());
  260 + CompletionResult3_5 completionResult = JSONObject.parseObject(str, CompletionResult3_5.class);
  261 + return completionResult;
  262 + }
  263 +
208 public static byte[] readInputStream(InputStream inputStream) throws IOException { 264 public static byte[] readInputStream(InputStream inputStream) throws IOException {
209 byte[] buffer = new byte[1024]; 265 byte[] buffer = new byte[1024];
210 int len = 0; 266 int len = 0;
@@ -3,15 +3,20 @@ package com.zhonglai.luhui.openai.controller; @@ -3,15 +3,20 @@ package com.zhonglai.luhui.openai.controller;
3 import cn.hutool.http.HttpUtil; 3 import cn.hutool.http.HttpUtil;
4 import com.alibaba.fastjson.JSON; 4 import com.alibaba.fastjson.JSON;
5 import com.ruoyi.common.constant.Constants; 5 import com.ruoyi.common.constant.Constants;
  6 +import com.ruoyi.common.constant.HttpStatus;
6 import com.ruoyi.common.core.controller.BaseController; 7 import com.ruoyi.common.core.controller.BaseController;
  8 +import com.ruoyi.common.core.domain.AjaxResult;
7 import com.ruoyi.common.core.domain.Message; 9 import com.ruoyi.common.core.domain.Message;
8 import com.ruoyi.common.core.domain.MessageCode; 10 import com.ruoyi.common.core.domain.MessageCode;
  11 +import com.ruoyi.common.utils.DESUtil;
  12 +import com.ruoyi.common.utils.DateUtils;
9 import com.ruoyi.system.login.service.LoginService; 13 import com.ruoyi.system.login.service.LoginService;
10 import io.swagger.annotations.Api; 14 import io.swagger.annotations.Api;
11 import io.swagger.annotations.ApiImplicitParam; 15 import io.swagger.annotations.ApiImplicitParam;
12 import io.swagger.annotations.ApiImplicitParams; 16 import io.swagger.annotations.ApiImplicitParams;
13 import io.swagger.annotations.ApiOperation; 17 import io.swagger.annotations.ApiOperation;
14 import org.springframework.beans.factory.annotation.Autowired; 18 import org.springframework.beans.factory.annotation.Autowired;
  19 +import org.springframework.web.bind.annotation.GetMapping;
15 import org.springframework.web.bind.annotation.PostMapping; 20 import org.springframework.web.bind.annotation.PostMapping;
16 import org.springframework.web.bind.annotation.RequestMapping; 21 import org.springframework.web.bind.annotation.RequestMapping;
17 import org.springframework.web.bind.annotation.RestController; 22 import org.springframework.web.bind.annotation.RestController;
@@ -24,28 +29,29 @@ import java.util.Map; @@ -24,28 +29,29 @@ import java.util.Map;
24 @RequestMapping("/openAiUserLogin") 29 @RequestMapping("/openAiUserLogin")
25 public class OpenAiUserLoginController extends BaseController { 30 public class OpenAiUserLoginController extends BaseController {
26 private static String user_pass = "123456"; 31 private static String user_pass = "123456";
  32 +
  33 + public static String ENCODE_KEY = "com/zhonglai";
27 @Autowired 34 @Autowired
28 private LoginService loginService; 35 private LoginService loginService;
29 - @ApiOperation("手机验证码登陆") 36 + @ApiOperation(value = "手机验证码登陆",notes = "返回token和longtoken,token是接口操作令牌失效时间端,长期本地缓存推荐存储longtoken,longtoken可以通过/openAiUserLogin/getTokenFromLongtoken接口换取token")
30 @ApiImplicitParams({ 37 @ApiImplicitParams({
31 @ApiImplicitParam(value = "手机号",name = "phone"), 38 @ApiImplicitParam(value = "手机号",name = "phone"),
32 @ApiImplicitParam(value = "验证码",name = "code"), 39 @ApiImplicitParam(value = "验证码",name = "code"),
33 }) 40 })
34 @PostMapping("/userpassLogin") 41 @PostMapping("/userpassLogin")
35 - public Message userpassLogin(String phone, String code,String key) 42 + public AjaxResult userpassLogin(String phone, String code, String key)
36 { 43 {
37 //验证验证码 44 //验证验证码
38 String str = HttpUtil.get("http://ly.userlogin.yu2le.com/userLogin/verificationCode?key="+key+"&code="+code); 45 String str = HttpUtil.get("http://ly.userlogin.yu2le.com/userLogin/verificationCode?key="+key+"&code="+code);
39 Message message = JSON.parseObject(str,Message.class); 46 Message message = JSON.parseObject(str,Message.class);
40 if(message.getCode()!=MessageCode.DEFAULT_SUCCESS_CODE.code) 47 if(message.getCode()!=MessageCode.DEFAULT_SUCCESS_CODE.code)
41 { 48 {
42 - return message; 49 + return AjaxResult.error(message.getMessage());
43 } 50 }
44 //生成令牌 51 //生成令牌
45 String token = loginService.openaiLoginByPass(phone,user_pass); 52 String token = loginService.openaiLoginByPass(phone,user_pass);
46 - Map<String,Object> map = new HashMap<>();  
47 - map.put(Constants.TOKEN, token);  
48 - return new Message(MessageCode.DEFAULT_SUCCESS_CODE,map); 53 + String longtoken = DESUtil.encode(phone+"_"+ DateUtils.getNowTimeMilly(),ENCODE_KEY);
  54 + return AjaxResult.success().put(Constants.TOKEN, token).put("longtoken", longtoken);
49 } 55 }
50 56
51 57
@@ -54,11 +60,34 @@ public class OpenAiUserLoginController extends BaseController { @@ -54,11 +60,34 @@ public class OpenAiUserLoginController extends BaseController {
54 @ApiImplicitParam(value = "手机号",name = "phone"), 60 @ApiImplicitParam(value = "手机号",name = "phone"),
55 }) 61 })
56 @PostMapping("/sendPhoneCode") 62 @PostMapping("/sendPhoneCode")
57 - public Message sendPhoneCode(String phone) 63 + public AjaxResult sendPhoneCode(String phone)
58 { 64 {
59 //生成令牌 65 //生成令牌
60 String str = HttpUtil.get("http://ly.userlogin.yu2le.com/userLogin/getPhoneCode/1/"+phone); 66 String str = HttpUtil.get("http://ly.userlogin.yu2le.com/userLogin/getPhoneCode/1/"+phone);
61 - return JSON.parseObject(str,Message.class); 67 + Message message = JSON.parseObject(str,Message.class);
  68 + if(message.getCode()!=MessageCode.DEFAULT_SUCCESS_CODE.code)
  69 + {
  70 + return AjaxResult.error(message.getMessage());
  71 + }
  72 + return AjaxResult.success(message.getMessage()).put("data",message.getData());
62 } 73 }
63 74
  75 + @ApiOperation(value = "长期token获取短期token",notes = "此接口也会返回一个长token,推荐用此接口更新本地存储的长token,可以延长长token的使用时间")
  76 + @ApiImplicitParams({
  77 + @ApiImplicitParam(value = "长token(为接口 /openAiUserLogin/userpassLogin 返回的 longtoken)",name = "longtoken"),
  78 + })
  79 + @GetMapping("/getTokenFromLongtoken")
  80 + public AjaxResult getTokenFromLongtoken(String longtoken)
  81 + {
  82 + String str = DESUtil.decode(longtoken,ENCODE_KEY);
  83 + String[] ss = str.split("_");
  84 + Integer time = 5184000; //60天
  85 + if(DateUtils.getNowTimeMilly()-Integer.parseInt(ss[1])>time)
  86 + {
  87 + return AjaxResult.error(HttpStatus.FORBIDDEN,"长token过期,请重新用手机号登陆");
  88 + }
  89 + String token = loginService.openaiLoginByPass(ss[0],user_pass);
  90 + String agenlongtoken = DESUtil.encode(ss[0]+"_"+ DateUtils.getNowTimeMilly(),ENCODE_KEY);
  91 + return AjaxResult.success().put(Constants.TOKEN, token).put("longtoken", agenlongtoken);
  92 + }
64 } 93 }
  1 +package com.zhonglai.luhui.openai.controller;
  2 +
  3 +import com.ruoyi.common.core.controller.BaseController;
  4 +import com.ruoyi.common.core.domain.AjaxResult;
  5 +import com.ruoyi.common.core.page.TableDataInfo;
  6 +import com.ruoyi.system.service.PublicService;
  7 +import io.swagger.annotations.Api;
  8 +import io.swagger.annotations.ApiOperation;
  9 +import org.springframework.beans.factory.annotation.Autowired;
  10 +import org.springframework.web.bind.annotation.GetMapping;
  11 +import org.springframework.web.bind.annotation.PostMapping;
  12 +import org.springframework.web.bind.annotation.RequestMapping;
  13 +import org.springframework.web.bind.annotation.RestController;
  14 +
  15 +import java.util.List;
  16 +import java.util.Map;
  17 +
  18 +@Api(tags = "用户管理")
  19 +@RestController
  20 +@RequestMapping("/userInfo")
  21 +public class UserInfoController extends BaseController {
  22 + @Autowired
  23 + private PublicService publicService;
  24 + @ApiOperation("获取用户信息")
  25 + @GetMapping("/getUserInfo")
  26 + public AjaxResult getUserInfo()
  27 + {
  28 + return AjaxResult.success(getLoginUser().getUser());
  29 + }
  30 +
  31 + @ApiOperation("修改头像")
  32 + @PostMapping("/upimgurl")
  33 + public AjaxResult upimgurl(String img_url)
  34 + {
  35 + publicService.updateBySql("UPDATE `lk_openai`.`user_info` SET img_url='"+img_url+"' WHERE id="+getUserId().intValue());
  36 + return AjaxResult.success();
  37 + }
  38 +
  39 + @ApiOperation("修改昵称")
  40 + @PostMapping("/upnickname")
  41 + public AjaxResult upnickname(String nickname)
  42 + {
  43 + publicService.updateBySql("UPDATE `lk_openai`.`user_info` SET nickname='"+nickname+"' WHERE id="+getUserId().intValue());
  44 + return AjaxResult.success();
  45 + }
  46 +
  47 + @ApiOperation("代币使用记录")
  48 + @GetMapping("/userFlowPacketRemainLog")
  49 + public TableDataInfo useFlowPacketRemainLog()
  50 + {
  51 + startPage();
  52 + List<Map<String,Object>> list = publicService.getObjectListBySQL("SELECT * FROM `lk_openai`.`user_flow_packet_remain_log` WHERE `user_id=`"+getUserId().intValue()+" and `type`=2 order by create_time desc");
  53 + return getDataTable(list);
  54 + }
  55 +
  56 + @ApiOperation("代币充值记录")
  57 + @GetMapping("/rechargeFlowPacketRemainLog")
  58 + public TableDataInfo rechargeFlowPacketRemainLog()
  59 + {
  60 + startPage();
  61 + List<Map<String,Object>> list = publicService.getObjectListBySQL("SELECT * FROM `lk_openai`.`user_flow_packet_remain_log` WHERE `user_id`="+getUserId().intValue()+" and `type`=1 order by create_time desc");
  62 + return getDataTable(list);
  63 + }
  64 +}
@@ -4,6 +4,14 @@ public class CompletionChoiceMessage3_5 { @@ -4,6 +4,14 @@ public class CompletionChoiceMessage3_5 {
4 private String role; 4 private String role;
5 private String content; 5 private String content;
6 6
  7 + public CompletionChoiceMessage3_5() {
  8 + }
  9 +
  10 + public CompletionChoiceMessage3_5(String role, String content) {
  11 + this.role = role;
  12 + this.content = content;
  13 + }
  14 +
7 public String getRole() { 15 public String getRole() {
8 return role; 16 return role;
9 } 17 }
@@ -14,6 +14,16 @@ public class GptMessage { @@ -14,6 +14,16 @@ public class GptMessage {
14 private Long prompt_tokens; //输入代币 14 private Long prompt_tokens; //输入代币
15 private Long completion_tokens; //输出代币 15 private Long completion_tokens; //输出代币
16 16
  17 + private Long total_tokens; //bigint(20) DEFAULT NULL COMMENT '总代币',
  18 +
  19 + public Long getTotal_tokens() {
  20 + return total_tokens;
  21 + }
  22 +
  23 + public void setTotal_tokens(Long total_tokens) {
  24 + this.total_tokens = total_tokens;
  25 + }
  26 +
17 public Long getPrompt_tokens() { 27 public Long getPrompt_tokens() {
18 return prompt_tokens; 28 return prompt_tokens;
19 } 29 }
@@ -5,13 +5,28 @@ import java.math.BigDecimal; @@ -5,13 +5,28 @@ import java.math.BigDecimal;
5 public class UserFlowPacketRemainLog { 5 public class UserFlowPacketRemainLog {
6 private Integer id; //int(11) DEFAULT NULL COMMENT '主键', 6 private Integer id; //int(11) DEFAULT NULL COMMENT '主键',
7 private Integer user_id; //int(11) DEFAULT NULL COMMENT '用户', 7 private Integer user_id; //int(11) DEFAULT NULL COMMENT '用户',
8 - private String room_id; //varchar(50) DEFAULT NULL COMMENT '房间号',  
9 private Integer create_time; //int(11) DEFAULT NULL COMMENT '创建时间', 8 private Integer create_time; //int(11) DEFAULT NULL COMMENT '创建时间',
10 - private Long prompt_tokens; //bigint(20) DEFAULT NULL COMMENT '发起代币',  
11 - private Long completion_tokens; //bigint(20) DEFAULT NULL COMMENT '完成代币',  
12 private Long total_tokens; //bigint(20) DEFAULT NULL COMMENT '总代币', 9 private Long total_tokens; //bigint(20) DEFAULT NULL COMMENT '总代币',
13 private BigDecimal openai_money; //bigint(20) DEFAULT NULL COMMENT 'openai费用(美元)', 10 private BigDecimal openai_money; //bigint(20) DEFAULT NULL COMMENT 'openai费用(美元)',
14 private BigDecimal reality_money; //bigint(20) DEFAULT NULL COMMENT '实际费用(人民币)' 11 private BigDecimal reality_money; //bigint(20) DEFAULT NULL COMMENT '实际费用(人民币)'
  12 + private String describe; //描述
  13 + private Integer type; //int(11) DEFAULT NULL COMMENT '类型(1充值,2消费)',
  14 +
  15 + public Integer getType() {
  16 + return type;
  17 + }
  18 +
  19 + public void setType(Integer type) {
  20 + this.type = type;
  21 + }
  22 +
  23 + public String getDescribe() {
  24 + return describe;
  25 + }
  26 +
  27 + public void setDescribe(String describe) {
  28 + this.describe = describe;
  29 + }
15 30
16 public Integer getId() { 31 public Integer getId() {
17 return id; 32 return id;
@@ -29,13 +44,6 @@ public class UserFlowPacketRemainLog { @@ -29,13 +44,6 @@ public class UserFlowPacketRemainLog {
29 this.user_id = user_id; 44 this.user_id = user_id;
30 } 45 }
31 46
32 - public String getRoom_id() {  
33 - return room_id;  
34 - }  
35 -  
36 - public void setRoom_id(String room_id) {  
37 - this.room_id = room_id;  
38 - }  
39 47
40 public Integer getCreate_time() { 48 public Integer getCreate_time() {
41 return create_time; 49 return create_time;
@@ -45,22 +53,6 @@ public class UserFlowPacketRemainLog { @@ -45,22 +53,6 @@ public class UserFlowPacketRemainLog {
45 this.create_time = create_time; 53 this.create_time = create_time;
46 } 54 }
47 55
48 - public Long getPrompt_tokens() {  
49 - return prompt_tokens;  
50 - }  
51 -  
52 - public void setPrompt_tokens(Long prompt_tokens) {  
53 - this.prompt_tokens = prompt_tokens;  
54 - }  
55 -  
56 - public Long getCompletion_tokens() {  
57 - return completion_tokens;  
58 - }  
59 -  
60 - public void setCompletion_tokens(Long completion_tokens) {  
61 - this.completion_tokens = completion_tokens;  
62 - }  
63 -  
64 public Long getTotal_tokens() { 56 public Long getTotal_tokens() {
65 return total_tokens; 57 return total_tokens;
66 } 58 }
  1 +package com.zhonglai.luhui.openai.service;
  2 +
  3 +import com.ruoyi.common.core.domain.Message;
  4 +import com.ruoyi.common.core.domain.MessageCode;
  5 +import com.ruoyi.system.service.PublicService;
  6 +import com.zhonglai.luhui.openai.dto.CompletionChoiceMessage3_5;
  7 +import com.zhonglai.luhui.openai.utils.SysConfigKeyType;
  8 +import org.springframework.beans.factory.annotation.Autowired;
  9 +import org.springframework.stereotype.Service;
  10 +import org.springframework.web.bind.annotation.PostMapping;
  11 +
  12 +import javax.annotation.PostConstruct;
  13 +import java.math.BigDecimal;
  14 +import java.util.ArrayList;
  15 +import java.util.List;
  16 +import java.util.Map;
  17 +
  18 +/**
  19 + * vip业务
  20 + */
  21 +@Service
  22 +public class VipServiceImpl {
  23 +
  24 + @Autowired
  25 + private PublicService publicService;
  26 +
  27 + private static List<Boolean> freeList = new ArrayList<>(); //是否免费
  28 + private static List<Boolean> chargingList = new ArrayList<>(); //是否计费
  29 + private static List<Boolean> timingList = new ArrayList<>(); //是否计计时
  30 +
  31 + @PostConstruct
  32 + public void init()
  33 + {
  34 + /**
  35 + * vip0(试用用户默认有免费30000代币试用,调用的接口是免费接口响应慢可能会失败)
  36 + * vip1(充值用户拥有代币充值记录的用户,调用的接口是免费接口响应慢可能会失败)
  37 + * vip2(升级接口通道,按月支付。响应速度和成功率都有保证)
  38 + * vip3(包月用户,使用vip2的接口)
  39 + * vip4(包季,使用vip2的接口)
  40 + * vip5(包年,使用vip2的接口)
  41 + */
  42 +
  43 + freeList.add(0,true);
  44 + freeList.add(1,true);
  45 + freeList.add(2,false);
  46 + freeList.add(3,false);
  47 + freeList.add(4,false);
  48 + freeList.add(5,false);
  49 +
  50 + chargingList.add(0,true);
  51 + chargingList.add(1,true);
  52 + chargingList.add(2,false);
  53 + chargingList.add(3,false);
  54 + chargingList.add(4,false);
  55 + chargingList.add(5,false);
  56 +
  57 + timingList.add(0,false);
  58 + timingList.add(1,false);
  59 + timingList.add(2,true);
  60 + timingList.add(3,true);
  61 + timingList.add(4,true);
  62 + timingList.add(5,true);
  63 + }
  64 + public boolean isfree(Integer vipLevel)
  65 + {
  66 + return freeList.get(vipLevel);
  67 + }
  68 + public boolean isCharging(Integer vipLevel)
  69 + {
  70 + return chargingList.get(vipLevel);
  71 + }
  72 + public boolean isTiming(Integer vipLevel)
  73 + {
  74 + return timingList.get(vipLevel);
  75 + }
  76 +
  77 + public BigDecimal[] getUnitprice()
  78 + {
  79 + List<Map<String,Object>> unitpriceList = publicService.getObjectListBySQL("select `key`,`value` from `lk_openai`.`sys_config` where `key_type`='"+ SysConfigKeyType.gpt_3_5_unitprice+"'");
  80 + BigDecimal openaiUnitprice = new BigDecimal(0);
  81 + BigDecimal realityUnitprice = new BigDecimal(0);
  82 + if(null != unitpriceList && unitpriceList.size() !=0)
  83 + {
  84 + for(Map<String,Object> map:unitpriceList)
  85 + {
  86 + if("openai".equals(map.get("key")))
  87 + {
  88 + openaiUnitprice = new BigDecimal(map.get("value").toString());
  89 + }else if("reality".equals(map.get("key")))
  90 + {
  91 + realityUnitprice = new BigDecimal(map.get("value").toString());
  92 + }
  93 + }
  94 + }
  95 + return new BigDecimal[]{openaiUnitprice,realityUnitprice};
  96 + }
  97 +
  98 +}
1 -# 项目相关配置 jhlt: # 名称 name: zhonglai # 版本 version: 3.8.2 # 版权年份 copyrightYear: 2022 # 获取ip地址开关 addressEnabled: false # 开发环境配置 server: # 服务器的HTTP端口,默认为8080 port: 8082 servlet: # 应用的访问路径 context-path: / tomcat: # tomcat的URI编码 uri-encoding: UTF-8 # 连接数满后的排队数,默认为100 accept-count: 1000 threads: # tomcat最大线程数,默认为200 max: 800 # Tomcat启动初始化的线程数,默认值10 min-spare: 100 # 日志配置 logging: level: com.ruoyi: debug org.springframework: warn # Spring配置 spring: # 资源信息 messages: # 国际化资源文件路径 basename: i18n/messages profiles: active: druid # 文件上传 servlet: multipart: # 单个文件大小 max-file-size: 10MB # 设置总上传的文件大小 max-request-size: 20MB # 服务模块 devtools: restart: # 热部署开关 enabled: true # redis 配置 redis: # 地址 host: 47.112.163.61 # 端口,默认为6379 port: 9527 # 数据库索引 database: 1 # 密码 password: Luhui586 # 连接超时时间 timeout: 10s lettuce: pool: # 连接池中的最小空闲连接 min-idle: 0 # 连接池中的最大空闲连接 max-idle: 8 # 连接池的最大数据库连接数 max-active: 8 # #连接池最大阻塞等待时间(使用负值表示没有限制) max-wait: -1ms # token配置 token: # 令牌自定义标识 header: Authorization # 令牌密钥 secret: abcdefghijklmnopqrstuvwxyz # 令牌有效期(默认30分钟) expireTime: 1440 rediskey: lh-openai # MyBatis配置 mybatis: # 搜索指定包别名 typeAliasesPackage: com.ruoyi.**.domain # 配置mapper的扫描,找到所有的mapper.xml映射文件 mapperLocations: classpath*:mapper/**/*Mapper.xml # 加载全局的配置文件 configLocation: classpath:mybatis/mybatis-config.xml # PageHelper分页插件 pagehelper: helperDialect: mysql supportMethodsArguments: true params: count=countSql # Swagger配置 swagger: # 是否开启swagger enabled: true # 请求前缀 pathMapping: /dev-api # 防止XSS攻击 xss: # 过滤开关 enabled: true # 排除链接(多个用逗号分隔) excludes: /system/notice # 匹配链接 urlPatterns: /system/*,/monitor/*,/tool/* mqtt: client: device_life: 180 # NameServer地址 rocketmq: name-server: 47.115.144.179:9876 # 默认的消息组 producer: group: deviceCommand send-message-timeout: 30000 send-topic: lh-chat-gpt send-tags: 1 sys: ## // 对于登录login 注册register 验证码captchaImage 允许匿名访问 antMatchers: /login,/register,/captchaImage,/getCacheObject,/v2/api-docs,/chatgpt/aiChatbot,/chatgpt/chatgptV3_5, chatgpt: token: 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 timeout: 5000  
  1 +# 项目相关配置 jhlt: # 名称 name: zhonglai # 版本 version: 3.8.2 # 版权年份 copyrightYear: 2022 # 获取ip地址开关 addressEnabled: false # 开发环境配置 server: # 服务器的HTTP端口,默认为8080 port: 8082 servlet: # 应用的访问路径 context-path: / tomcat: # tomcat的URI编码 uri-encoding: UTF-8 # 连接数满后的排队数,默认为100 accept-count: 1000 threads: # tomcat最大线程数,默认为200 max: 800 # Tomcat启动初始化的线程数,默认值10 min-spare: 100 # 日志配置 logging: level: com.ruoyi: debug org.springframework: warn # Spring配置 spring: # 资源信息 messages: # 国际化资源文件路径 basename: i18n/messages profiles: active: druid # 文件上传 servlet: multipart: # 单个文件大小 max-file-size: 10MB # 设置总上传的文件大小 max-request-size: 20MB # 服务模块 devtools: restart: # 热部署开关 enabled: true # redis 配置 redis: # 地址 host: 47.112.163.61 # 端口,默认为6379 port: 9527 # 数据库索引 database: 1 # 密码 password: Luhui586 # 连接超时时间 timeout: 10s lettuce: pool: # 连接池中的最小空闲连接 min-idle: 0 # 连接池中的最大空闲连接 max-idle: 8 # 连接池的最大数据库连接数 max-active: 8 # #连接池最大阻塞等待时间(使用负值表示没有限制) max-wait: -1ms # token配置 token: # 令牌自定义标识 header: Authorization # 令牌密钥 secret: abcdefghijklmnopqrstuvwxyz # 令牌有效期(默认30分钟) expireTime: 1440 rediskey: lh-openai # MyBatis配置 mybatis: # 搜索指定包别名 typeAliasesPackage: com.ruoyi.**.domain # 配置mapper的扫描,找到所有的mapper.xml映射文件 mapperLocations: classpath*:mapper/**/*Mapper.xml # 加载全局的配置文件 configLocation: classpath:mybatis/mybatis-config.xml # PageHelper分页插件 pagehelper: helperDialect: mysql supportMethodsArguments: true params: count=countSql # Swagger配置 swagger: # 是否开启swagger enabled: true # 请求前缀 pathMapping: /dev-api # 防止XSS攻击 xss: # 过滤开关 enabled: true # 排除链接(多个用逗号分隔) excludes: /system/notice # 匹配链接 urlPatterns: /system/*,/monitor/*,/tool/* mqtt: client: device_life: 180 # NameServer地址 rocketmq: name-server: 47.115.144.179:9876 # 默认的消息组 producer: group: deviceCommand send-message-timeout: 30000 send-topic: lh-chat-gpt send-tags: 1 sys: ## // 对于登录login 注册register 验证码captchaImage 允许匿名访问 antMatchers: /login,/register,/captchaImage,/getCacheObject,/v2/api-docs chatgpt: token: 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 timeout: 5000
  1 +<?xml version="1.0" encoding="UTF-8"?>
  2 +<project xmlns="http://maven.apache.org/POM/4.0.0"
  3 + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4 + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  5 + <parent>
  6 + <groupId>com.zhonglai.luhui</groupId>
  7 + <artifactId>Luhui</artifactId>
  8 + <version>1.0-SNAPSHOT</version>
  9 + </parent>
  10 + <modelVersion>4.0.0</modelVersion>
  11 +
  12 + <artifactId>lh-quartz</artifactId>
  13 +
  14 + <description>
  15 + quartz定时任务
  16 + </description>
  17 +
  18 + <dependencies>
  19 +
  20 + <!-- 定时任务 -->
  21 + <dependency>
  22 + <groupId>org.quartz-scheduler</groupId>
  23 + <artifactId>quartz</artifactId>
  24 + <exclusions>
  25 + <exclusion>
  26 + <groupId>com.mchange</groupId>
  27 + <artifactId>c3p0</artifactId>
  28 + </exclusion>
  29 + </exclusions>
  30 + </dependency>
  31 +
  32 + <!-- 通用工具-->
  33 + <dependency>
  34 + <groupId>com.zhonglai.luhui</groupId>
  35 + <artifactId>ruoyi-common</artifactId>
  36 + </dependency>
  37 +
  38 + </dependencies>
  39 +
  40 +</project>
  1 +package com.ruoyi.quartz.config;
  2 +
  3 +import org.springframework.context.annotation.Bean;
  4 +import org.springframework.context.annotation.Configuration;
  5 +import org.springframework.scheduling.quartz.SchedulerFactoryBean;
  6 +import javax.sql.DataSource;
  7 +import java.util.Properties;
  8 +
  9 +/**
  10 + * 定时任务配置(单机部署建议删除此类和qrtz数据库表,默认走内存会最高效)
  11 + *
  12 + * @author ruoyi
  13 + */
  14 +@Configuration
  15 +public class ScheduleConfig
  16 +{
  17 + @Bean
  18 + public SchedulerFactoryBean schedulerFactoryBean(DataSource dataSource)
  19 + {
  20 + SchedulerFactoryBean factory = new SchedulerFactoryBean();
  21 + factory.setDataSource(dataSource);
  22 +
  23 + // quartz参数
  24 + Properties prop = new Properties();
  25 + prop.put("org.quartz.scheduler.instanceName", "RuoyiScheduler");
  26 + prop.put("org.quartz.scheduler.instanceId", "AUTO");
  27 + // 线程池配置
  28 + prop.put("org.quartz.threadPool.class", "org.quartz.simpl.SimpleThreadPool");
  29 + prop.put("org.quartz.threadPool.threadCount", "20");
  30 + prop.put("org.quartz.threadPool.threadPriority", "5");
  31 + // JobStore配置
  32 +// prop.put("org.quartz.jobStore.class", "org.quartz.impl.jdbcjobstore.JobStoreTX");
  33 + // 集群配置
  34 + prop.put("org.quartz.jobStore.isClustered", "true");
  35 + prop.put("org.quartz.jobStore.clusterCheckinInterval", "15000");
  36 + prop.put("org.quartz.jobStore.maxMisfiresToHandleAtATime", "1");
  37 + prop.put("org.quartz.jobStore.txIsolationLevelSerializable", "true");
  38 +
  39 + // sqlserver 启用
  40 + // prop.put("org.quartz.jobStore.selectWithLockSQL", "SELECT * FROM {0}LOCKS UPDLOCK WHERE LOCK_NAME = ?");
  41 + prop.put("org.quartz.jobStore.misfireThreshold", "12000");
  42 + prop.put("org.quartz.jobStore.tablePrefix", "QRTZ_");
  43 + factory.setQuartzProperties(prop);
  44 +
  45 + factory.setSchedulerName("RuoyiScheduler");
  46 + // 延时启动
  47 + factory.setStartupDelay(1);
  48 + factory.setApplicationContextSchedulerContextKey("applicationContextKey");
  49 + // 可选,QuartzScheduler
  50 + // 启动时更新己存在的Job,这样就不用每次修改targetObject后删除qrtz_job_details表对应记录了
  51 + factory.setOverwriteExistingJobs(true);
  52 + // 设置自动启动,默认为true
  53 + factory.setAutoStartup(true);
  54 +
  55 + return factory;
  56 + }
  57 +}
  1 +package com.ruoyi.quartz.controller;
  2 +
  3 +import java.util.List;
  4 +import javax.servlet.http.HttpServletResponse;
  5 +
  6 +import io.swagger.annotations.Api;
  7 +import io.swagger.annotations.ApiOperation;
  8 +import org.quartz.SchedulerException;
  9 +import org.springframework.beans.factory.annotation.Autowired;
  10 +import org.springframework.security.access.prepost.PreAuthorize;
  11 +import org.springframework.web.bind.annotation.DeleteMapping;
  12 +import org.springframework.web.bind.annotation.GetMapping;
  13 +import org.springframework.web.bind.annotation.PathVariable;
  14 +import org.springframework.web.bind.annotation.PostMapping;
  15 +import org.springframework.web.bind.annotation.PutMapping;
  16 +import org.springframework.web.bind.annotation.RequestBody;
  17 +import org.springframework.web.bind.annotation.RequestMapping;
  18 +import org.springframework.web.bind.annotation.RestController;
  19 +import com.ruoyi.common.annotation.Log;
  20 +import com.ruoyi.common.constant.Constants;
  21 +import com.ruoyi.common.core.controller.BaseController;
  22 +import com.ruoyi.common.core.domain.AjaxResult;
  23 +import com.ruoyi.common.core.page.TableDataInfo;
  24 +import com.ruoyi.common.enums.BusinessType;
  25 +import com.ruoyi.common.exception.job.TaskException;
  26 +import com.ruoyi.common.utils.StringUtils;
  27 +import com.ruoyi.common.utils.poi.ExcelUtil;
  28 +import com.ruoyi.quartz.domain.SysJob;
  29 +import com.ruoyi.quartz.service.ISysJobService;
  30 +import com.ruoyi.quartz.util.CronUtils;
  31 +
  32 +/**
  33 + * 调度任务信息操作处理
  34 + *
  35 + * @author ruoyi
  36 + */
  37 +@Api(tags = "调度任务信息操作处理")
  38 +@RestController
  39 +@RequestMapping("/monitor/job")
  40 +public class SysJobController extends BaseController
  41 +{
  42 + @Autowired
  43 + private ISysJobService jobService;
  44 +
  45 + /**
  46 + * 查询定时任务列表
  47 + */
  48 + @ApiOperation("查询定时任务列表")
  49 + @PreAuthorize("@ss.hasPermi('monitor:job:list')")
  50 + @GetMapping("/list")
  51 + public TableDataInfo list(SysJob sysJob)
  52 + {
  53 + startPage();
  54 + List<SysJob> list = jobService.selectJobList(sysJob);
  55 + return getDataTable(list);
  56 + }
  57 +
  58 + /**
  59 + * 导出定时任务列表
  60 + */
  61 + @ApiOperation("导出定时任务列表")
  62 + @PreAuthorize("@ss.hasPermi('monitor:job:export')")
  63 + @Log(title = "定时任务", businessType = BusinessType.EXPORT)
  64 + @PostMapping("/export")
  65 + public void export(HttpServletResponse response, SysJob sysJob)
  66 + {
  67 + List<SysJob> list = jobService.selectJobList(sysJob);
  68 + ExcelUtil<SysJob> util = new ExcelUtil<SysJob>(SysJob.class);
  69 + util.exportExcel(response, list, "定时任务");
  70 + }
  71 +
  72 + /**
  73 + * 获取定时任务详细信息
  74 + */
  75 + @ApiOperation("获取定时任务详细信息")
  76 + @PreAuthorize("@ss.hasPermi('monitor:job:query')")
  77 + @GetMapping(value = "/{jobId}")
  78 + public AjaxResult getInfo(@PathVariable("jobId") Long jobId)
  79 + {
  80 + return AjaxResult.success(jobService.selectJobById(jobId));
  81 + }
  82 +
  83 + /**
  84 + * 新增定时任务
  85 + */
  86 + @ApiOperation("新增定时任务")
  87 + @PreAuthorize("@ss.hasPermi('monitor:job:add')")
  88 + @Log(title = "定时任务", businessType = BusinessType.INSERT)
  89 + @PostMapping
  90 + public AjaxResult add(@RequestBody SysJob job) throws SchedulerException, TaskException
  91 + {
  92 + if (!CronUtils.isValid(job.getCronExpression()))
  93 + {
  94 + return error("新增任务'" + job.getJobName() + "'失败,Cron表达式不正确");
  95 + }
  96 + else if (StringUtils.containsIgnoreCase(job.getInvokeTarget(), Constants.LOOKUP_RMI))
  97 + {
  98 + return error("新增任务'" + job.getJobName() + "'失败,目标字符串不允许'rmi://'调用");
  99 + }
  100 + else if (StringUtils.containsIgnoreCase(job.getInvokeTarget(), Constants.LOOKUP_LDAP))
  101 + {
  102 + return error("新增任务'" + job.getJobName() + "'失败,目标字符串不允许'ldap://'调用");
  103 + }
  104 + else if (StringUtils.containsAnyIgnoreCase(job.getInvokeTarget(), new String[] { Constants.HTTP, Constants.HTTPS }))
  105 + {
  106 + return error("新增任务'" + job.getJobName() + "'失败,目标字符串不允许'http(s)//'调用");
  107 + }
  108 + else if (StringUtils.containsAnyIgnoreCase(job.getInvokeTarget(), Constants.JOB_ERROR_STR))
  109 + {
  110 + return error("新增任务'" + job.getJobName() + "'失败,目标字符串存在违规");
  111 + }
  112 + job.setCreateBy(getUsername());
  113 + return toAjax(jobService.insertJob(job));
  114 + }
  115 +
  116 + /**
  117 + * 修改定时任务
  118 + */
  119 + @ApiOperation("修改定时任务")
  120 + @PreAuthorize("@ss.hasPermi('monitor:job:edit')")
  121 + @Log(title = "定时任务", businessType = BusinessType.UPDATE)
  122 + @PutMapping
  123 + public AjaxResult edit(@RequestBody SysJob job) throws SchedulerException, TaskException
  124 + {
  125 + if (!CronUtils.isValid(job.getCronExpression()))
  126 + {
  127 + return error("修改任务'" + job.getJobName() + "'失败,Cron表达式不正确");
  128 + }
  129 + else if (StringUtils.containsIgnoreCase(job.getInvokeTarget(), Constants.LOOKUP_RMI))
  130 + {
  131 + return error("修改任务'" + job.getJobName() + "'失败,目标字符串不允许'rmi://'调用");
  132 + }
  133 + else if (StringUtils.containsIgnoreCase(job.getInvokeTarget(), Constants.LOOKUP_LDAP))
  134 + {
  135 + return error("修改任务'" + job.getJobName() + "'失败,目标字符串不允许'ldap://'调用");
  136 + }
  137 + else if (StringUtils.containsAnyIgnoreCase(job.getInvokeTarget(), new String[] { Constants.HTTP, Constants.HTTPS }))
  138 + {
  139 + return error("修改任务'" + job.getJobName() + "'失败,目标字符串不允许'http(s)//'调用");
  140 + }
  141 + else if (StringUtils.containsAnyIgnoreCase(job.getInvokeTarget(), Constants.JOB_ERROR_STR))
  142 + {
  143 + return error("修改任务'" + job.getJobName() + "'失败,目标字符串存在违规");
  144 + }
  145 + job.setUpdateBy(getUsername());
  146 + return toAjax(jobService.updateJob(job));
  147 + }
  148 +
  149 + /**
  150 + * 定时任务状态修改
  151 + */
  152 + @ApiOperation("定时任务状态修改")
  153 + @PreAuthorize("@ss.hasPermi('monitor:job:changeStatus')")
  154 + @Log(title = "定时任务", businessType = BusinessType.UPDATE)
  155 + @PutMapping("/changeStatus")
  156 + public AjaxResult changeStatus(@RequestBody SysJob job) throws SchedulerException
  157 + {
  158 + SysJob newJob = jobService.selectJobById(job.getJobId());
  159 + newJob.setStatus(job.getStatus());
  160 + return toAjax(jobService.changeStatus(newJob));
  161 + }
  162 +
  163 + /**
  164 + * 定时任务立即执行一次
  165 + */
  166 + @ApiOperation("定时任务立即执行一次")
  167 + @PreAuthorize("@ss.hasPermi('monitor:job:changeStatus')")
  168 + @Log(title = "定时任务", businessType = BusinessType.UPDATE)
  169 + @PutMapping("/run")
  170 + public AjaxResult run(@RequestBody SysJob job) throws SchedulerException
  171 + {
  172 + jobService.run(job);
  173 + return AjaxResult.success();
  174 + }
  175 +
  176 + /**
  177 + * 删除定时任务
  178 + */
  179 + @ApiOperation("删除定时任务")
  180 + @PreAuthorize("@ss.hasPermi('monitor:job:remove')")
  181 + @Log(title = "定时任务", businessType = BusinessType.DELETE)
  182 + @DeleteMapping("/{jobIds}")
  183 + public AjaxResult remove(@PathVariable Long[] jobIds) throws SchedulerException, TaskException
  184 + {
  185 + jobService.deleteJobByIds(jobIds);
  186 + return AjaxResult.success();
  187 + }
  188 +}
  1 +package com.ruoyi.quartz.controller;
  2 +
  3 +import java.util.List;
  4 +import javax.servlet.http.HttpServletResponse;
  5 +
  6 +import io.swagger.annotations.Api;
  7 +import io.swagger.annotations.ApiOperation;
  8 +import org.springframework.beans.factory.annotation.Autowired;
  9 +import org.springframework.security.access.prepost.PreAuthorize;
  10 +import org.springframework.web.bind.annotation.DeleteMapping;
  11 +import org.springframework.web.bind.annotation.GetMapping;
  12 +import org.springframework.web.bind.annotation.PathVariable;
  13 +import org.springframework.web.bind.annotation.PostMapping;
  14 +import org.springframework.web.bind.annotation.RequestMapping;
  15 +import org.springframework.web.bind.annotation.RestController;
  16 +import com.ruoyi.common.annotation.Log;
  17 +import com.ruoyi.common.core.controller.BaseController;
  18 +import com.ruoyi.common.core.domain.AjaxResult;
  19 +import com.ruoyi.common.core.page.TableDataInfo;
  20 +import com.ruoyi.common.enums.BusinessType;
  21 +import com.ruoyi.common.utils.poi.ExcelUtil;
  22 +import com.ruoyi.quartz.domain.SysJobLog;
  23 +import com.ruoyi.quartz.service.ISysJobLogService;
  24 +
  25 +/**
  26 + * 调度日志操作处理
  27 + *
  28 + * @author ruoyi
  29 + */
  30 +@Api(tags = "调度日志操作处理")
  31 +@RestController
  32 +@RequestMapping("/monitor/jobLog")
  33 +public class SysJobLogController extends BaseController
  34 +{
  35 + @Autowired
  36 + private ISysJobLogService jobLogService;
  37 +
  38 + /**
  39 + * 查询定时任务调度日志列表
  40 + */
  41 + @ApiOperation("查询定时任务调度日志列表")
  42 + @PreAuthorize("@ss.hasPermi('monitor:job:list')")
  43 + @GetMapping("/list")
  44 + public TableDataInfo list(SysJobLog sysJobLog)
  45 + {
  46 + startPage();
  47 + List<SysJobLog> list = jobLogService.selectJobLogList(sysJobLog);
  48 + return getDataTable(list);
  49 + }
  50 +
  51 + /**
  52 + * 导出定时任务调度日志列表
  53 + */
  54 + @ApiOperation("导出定时任务调度日志列表")
  55 + @PreAuthorize("@ss.hasPermi('monitor:job:export')")
  56 + @Log(title = "任务调度日志", businessType = BusinessType.EXPORT)
  57 + @PostMapping("/export")
  58 + public void export(HttpServletResponse response, SysJobLog sysJobLog)
  59 + {
  60 + List<SysJobLog> list = jobLogService.selectJobLogList(sysJobLog);
  61 + ExcelUtil<SysJobLog> util = new ExcelUtil<SysJobLog>(SysJobLog.class);
  62 + util.exportExcel(response, list, "调度日志");
  63 + }
  64 +
  65 + /**
  66 + * 根据调度编号获取详细信息
  67 + */
  68 + @ApiOperation("根据调度编号获取详细信息")
  69 + @PreAuthorize("@ss.hasPermi('monitor:job:query')")
  70 + @GetMapping(value = "/{configId}")
  71 + public AjaxResult getInfo(@PathVariable Long jobLogId)
  72 + {
  73 + return AjaxResult.success(jobLogService.selectJobLogById(jobLogId));
  74 + }
  75 +
  76 +
  77 + /**
  78 + * 删除定时任务调度日志
  79 + */
  80 + @ApiOperation("删除定时任务调度日志")
  81 + @PreAuthorize("@ss.hasPermi('monitor:job:remove')")
  82 + @Log(title = "定时任务调度日志", businessType = BusinessType.DELETE)
  83 + @DeleteMapping("/{jobLogIds}")
  84 + public AjaxResult remove(@PathVariable Long[] jobLogIds)
  85 + {
  86 + return toAjax(jobLogService.deleteJobLogByIds(jobLogIds));
  87 + }
  88 +
  89 + /**
  90 + * 清空定时任务调度日志
  91 + */
  92 + @ApiOperation("清空定时任务调度日志")
  93 + @PreAuthorize("@ss.hasPermi('monitor:job:remove')")
  94 + @Log(title = "调度日志", businessType = BusinessType.CLEAN)
  95 + @DeleteMapping("/clean")
  96 + public AjaxResult clean()
  97 + {
  98 + jobLogService.cleanJobLog();
  99 + return AjaxResult.success();
  100 + }
  101 +}
  1 +package com.ruoyi.quartz.domain;
  2 +
  3 +import java.util.Date;
  4 +import javax.validation.constraints.NotBlank;
  5 +import javax.validation.constraints.Size;
  6 +
  7 +import com.ruoyi.system.domain.tool.BaseEntity;
  8 +import org.apache.commons.lang3.builder.ToStringBuilder;
  9 +import org.apache.commons.lang3.builder.ToStringStyle;
  10 +import com.fasterxml.jackson.annotation.JsonFormat;
  11 +import com.ruoyi.common.annotation.Excel;
  12 +import com.ruoyi.common.annotation.Excel.ColumnType;
  13 +import com.ruoyi.common.constant.ScheduleConstants;
  14 +import com.ruoyi.common.utils.StringUtils;
  15 +import com.ruoyi.quartz.util.CronUtils;
  16 +
  17 +/**
  18 + * 定时任务调度表 sys_job
  19 + *
  20 + * @author ruoyi
  21 + */
  22 +public class SysJob extends BaseEntity
  23 +{
  24 + private static final long serialVersionUID = 1L;
  25 +
  26 + /** 任务ID */
  27 + @Excel(name = "任务序号", cellType = ColumnType.NUMERIC)
  28 + private Long jobId;
  29 +
  30 + /** 任务名称 */
  31 + @Excel(name = "任务名称")
  32 + private String jobName;
  33 +
  34 + /** 任务组名 */
  35 + @Excel(name = "任务组名")
  36 + private String jobGroup;
  37 +
  38 + /** 调用目标字符串 */
  39 + @Excel(name = "调用目标字符串")
  40 + private String invokeTarget;
  41 +
  42 + /** cron执行表达式 */
  43 + @Excel(name = "执行表达式 ")
  44 + private String cronExpression;
  45 +
  46 + /** cron计划策略 */
  47 + @Excel(name = "计划策略 ", readConverterExp = "0=默认,1=立即触发执行,2=触发一次执行,3=不触发立即执行")
  48 + private String misfirePolicy = ScheduleConstants.MISFIRE_DEFAULT;
  49 +
  50 + /** 是否并发执行(0允许 1禁止) */
  51 + @Excel(name = "并发执行", readConverterExp = "0=允许,1=禁止")
  52 + private String concurrent;
  53 +
  54 + /** 任务状态(0正常 1暂停) */
  55 + @Excel(name = "任务状态", readConverterExp = "0=正常,1=暂停")
  56 + private String status;
  57 +
  58 + public Long getJobId()
  59 + {
  60 + return jobId;
  61 + }
  62 +
  63 + public void setJobId(Long jobId)
  64 + {
  65 + this.jobId = jobId;
  66 + }
  67 +
  68 + @NotBlank(message = "任务名称不能为空")
  69 + @Size(min = 0, max = 64, message = "任务名称不能超过64个字符")
  70 + public String getJobName()
  71 + {
  72 + return jobName;
  73 + }
  74 +
  75 + public void setJobName(String jobName)
  76 + {
  77 + this.jobName = jobName;
  78 + }
  79 +
  80 + public String getJobGroup()
  81 + {
  82 + return jobGroup;
  83 + }
  84 +
  85 + public void setJobGroup(String jobGroup)
  86 + {
  87 + this.jobGroup = jobGroup;
  88 + }
  89 +
  90 + @NotBlank(message = "调用目标字符串不能为空")
  91 + @Size(min = 0, max = 500, message = "调用目标字符串长度不能超过500个字符")
  92 + public String getInvokeTarget()
  93 + {
  94 + return invokeTarget;
  95 + }
  96 +
  97 + public void setInvokeTarget(String invokeTarget)
  98 + {
  99 + this.invokeTarget = invokeTarget;
  100 + }
  101 +
  102 + @NotBlank(message = "Cron执行表达式不能为空")
  103 + @Size(min = 0, max = 255, message = "Cron执行表达式不能超过255个字符")
  104 + public String getCronExpression()
  105 + {
  106 + return cronExpression;
  107 + }
  108 +
  109 + public void setCronExpression(String cronExpression)
  110 + {
  111 + this.cronExpression = cronExpression;
  112 + }
  113 +
  114 + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
  115 + public Date getNextValidTime()
  116 + {
  117 + if (StringUtils.isNotEmpty(cronExpression))
  118 + {
  119 + return CronUtils.getNextExecution(cronExpression);
  120 + }
  121 + return null;
  122 + }
  123 +
  124 + public String getMisfirePolicy()
  125 + {
  126 + return misfirePolicy;
  127 + }
  128 +
  129 + public void setMisfirePolicy(String misfirePolicy)
  130 + {
  131 + this.misfirePolicy = misfirePolicy;
  132 + }
  133 +
  134 + public String getConcurrent()
  135 + {
  136 + return concurrent;
  137 + }
  138 +
  139 + public void setConcurrent(String concurrent)
  140 + {
  141 + this.concurrent = concurrent;
  142 + }
  143 +
  144 + public String getStatus()
  145 + {
  146 + return status;
  147 + }
  148 +
  149 + public void setStatus(String status)
  150 + {
  151 + this.status = status;
  152 + }
  153 +
  154 + @Override
  155 + public String toString() {
  156 + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
  157 + .append("jobId", getJobId())
  158 + .append("jobName", getJobName())
  159 + .append("jobGroup", getJobGroup())
  160 + .append("cronExpression", getCronExpression())
  161 + .append("nextValidTime", getNextValidTime())
  162 + .append("misfirePolicy", getMisfirePolicy())
  163 + .append("concurrent", getConcurrent())
  164 + .append("status", getStatus())
  165 + .append("createBy", getCreateBy())
  166 + .append("createTime", getCreateTime())
  167 + .append("updateBy", getUpdateBy())
  168 + .append("updateTime", getUpdateTime())
  169 + .append("remark", getRemark())
  170 + .toString();
  171 + }
  172 +}
  1 +package com.ruoyi.quartz.domain;
  2 +
  3 +import java.util.Date;
  4 +
  5 +import com.ruoyi.system.domain.tool.BaseEntity;
  6 +import org.apache.commons.lang3.builder.ToStringBuilder;
  7 +import org.apache.commons.lang3.builder.ToStringStyle;
  8 +import com.ruoyi.common.annotation.Excel;
  9 +
  10 +/**
  11 + * 定时任务调度日志表 sys_job_log
  12 + *
  13 + * @author ruoyi
  14 + */
  15 +public class SysJobLog extends BaseEntity
  16 +{
  17 + private static final long serialVersionUID = 1L;
  18 +
  19 + /** ID */
  20 + @Excel(name = "日志序号")
  21 + private Long jobLogId;
  22 +
  23 + /** 任务名称 */
  24 + @Excel(name = "任务名称")
  25 + private String jobName;
  26 +
  27 + /** 任务组名 */
  28 + @Excel(name = "任务组名")
  29 + private String jobGroup;
  30 +
  31 + /** 调用目标字符串 */
  32 + @Excel(name = "调用目标字符串")
  33 + private String invokeTarget;
  34 +
  35 + /** 日志信息 */
  36 + @Excel(name = "日志信息")
  37 + private String jobMessage;
  38 +
  39 + /** 执行状态(0正常 1失败) */
  40 + @Excel(name = "执行状态", readConverterExp = "0=正常,1=失败")
  41 + private String status;
  42 +
  43 + /** 异常信息 */
  44 + @Excel(name = "异常信息")
  45 + private String exceptionInfo;
  46 +
  47 + /** 开始时间 */
  48 + private Date startTime;
  49 +
  50 + /** 停止时间 */
  51 + private Date stopTime;
  52 +
  53 + public Long getJobLogId()
  54 + {
  55 + return jobLogId;
  56 + }
  57 +
  58 + public void setJobLogId(Long jobLogId)
  59 + {
  60 + this.jobLogId = jobLogId;
  61 + }
  62 +
  63 + public String getJobName()
  64 + {
  65 + return jobName;
  66 + }
  67 +
  68 + public void setJobName(String jobName)
  69 + {
  70 + this.jobName = jobName;
  71 + }
  72 +
  73 + public String getJobGroup()
  74 + {
  75 + return jobGroup;
  76 + }
  77 +
  78 + public void setJobGroup(String jobGroup)
  79 + {
  80 + this.jobGroup = jobGroup;
  81 + }
  82 +
  83 + public String getInvokeTarget()
  84 + {
  85 + return invokeTarget;
  86 + }
  87 +
  88 + public void setInvokeTarget(String invokeTarget)
  89 + {
  90 + this.invokeTarget = invokeTarget;
  91 + }
  92 +
  93 + public String getJobMessage()
  94 + {
  95 + return jobMessage;
  96 + }
  97 +
  98 + public void setJobMessage(String jobMessage)
  99 + {
  100 + this.jobMessage = jobMessage;
  101 + }
  102 +
  103 + public String getStatus()
  104 + {
  105 + return status;
  106 + }
  107 +
  108 + public void setStatus(String status)
  109 + {
  110 + this.status = status;
  111 + }
  112 +
  113 + public String getExceptionInfo()
  114 + {
  115 + return exceptionInfo;
  116 + }
  117 +
  118 + public void setExceptionInfo(String exceptionInfo)
  119 + {
  120 + this.exceptionInfo = exceptionInfo;
  121 + }
  122 +
  123 + public Date getStartTime()
  124 + {
  125 + return startTime;
  126 + }
  127 +
  128 + public void setStartTime(Date startTime)
  129 + {
  130 + this.startTime = startTime;
  131 + }
  132 +
  133 + public Date getStopTime()
  134 + {
  135 + return stopTime;
  136 + }
  137 +
  138 + public void setStopTime(Date stopTime)
  139 + {
  140 + this.stopTime = stopTime;
  141 + }
  142 +
  143 + @Override
  144 + public String toString() {
  145 + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
  146 + .append("jobLogId", getJobLogId())
  147 + .append("jobName", getJobName())
  148 + .append("jobGroup", getJobGroup())
  149 + .append("jobMessage", getJobMessage())
  150 + .append("status", getStatus())
  151 + .append("exceptionInfo", getExceptionInfo())
  152 + .append("startTime", getStartTime())
  153 + .append("stopTime", getStopTime())
  154 + .toString();
  155 + }
  156 +}
  1 +package com.ruoyi.quartz.mapper;
  2 +
  3 +import java.util.List;
  4 +import com.ruoyi.quartz.domain.SysJobLog;
  5 +
  6 +/**
  7 + * 调度任务日志信息 数据层
  8 + *
  9 + * @author ruoyi
  10 + */
  11 +public interface SysJobLogMapper
  12 +{
  13 + /**
  14 + * 获取quartz调度器日志的计划任务
  15 + *
  16 + * @param jobLog 调度日志信息
  17 + * @return 调度任务日志集合
  18 + */
  19 + public List<SysJobLog> selectJobLogList(SysJobLog jobLog);
  20 +
  21 + /**
  22 + * 查询所有调度任务日志
  23 + *
  24 + * @return 调度任务日志列表
  25 + */
  26 + public List<SysJobLog> selectJobLogAll();
  27 +
  28 + /**
  29 + * 通过调度任务日志ID查询调度信息
  30 + *
  31 + * @param jobLogId 调度任务日志ID
  32 + * @return 调度任务日志对象信息
  33 + */
  34 + public SysJobLog selectJobLogById(Long jobLogId);
  35 +
  36 + /**
  37 + * 新增任务日志
  38 + *
  39 + * @param jobLog 调度日志信息
  40 + * @return 结果
  41 + */
  42 + public int insertJobLog(SysJobLog jobLog);
  43 +
  44 + /**
  45 + * 批量删除调度日志信息
  46 + *
  47 + * @param logIds 需要删除的数据ID
  48 + * @return 结果
  49 + */
  50 + public int deleteJobLogByIds(Long[] logIds);
  51 +
  52 + /**
  53 + * 删除任务日志
  54 + *
  55 + * @param jobId 调度日志ID
  56 + * @return 结果
  57 + */
  58 + public int deleteJobLogById(Long jobId);
  59 +
  60 + /**
  61 + * 清空任务日志
  62 + */
  63 + public void cleanJobLog();
  64 +}
  1 +package com.ruoyi.quartz.mapper;
  2 +
  3 +import java.util.List;
  4 +import com.ruoyi.quartz.domain.SysJob;
  5 +
  6 +/**
  7 + * 调度任务信息 数据层
  8 + *
  9 + * @author ruoyi
  10 + */
  11 +public interface SysJobMapper
  12 +{
  13 + /**
  14 + * 查询调度任务日志集合
  15 + *
  16 + * @param job 调度信息
  17 + * @return 操作日志集合
  18 + */
  19 + public List<SysJob> selectJobList(SysJob job);
  20 +
  21 + /**
  22 + * 查询所有调度任务
  23 + *
  24 + * @return 调度任务列表
  25 + */
  26 + public List<SysJob> selectJobAll();
  27 +
  28 + /**
  29 + * 通过调度ID查询调度任务信息
  30 + *
  31 + * @param jobId 调度ID
  32 + * @return 角色对象信息
  33 + */
  34 + public SysJob selectJobById(Long jobId);
  35 +
  36 + /**
  37 + * 通过调度ID删除调度任务信息
  38 + *
  39 + * @param jobId 调度ID
  40 + * @return 结果
  41 + */
  42 + public int deleteJobById(Long jobId);
  43 +
  44 + /**
  45 + * 批量删除调度任务信息
  46 + *
  47 + * @param ids 需要删除的数据ID
  48 + * @return 结果
  49 + */
  50 + public int deleteJobByIds(Long[] ids);
  51 +
  52 + /**
  53 + * 修改调度任务信息
  54 + *
  55 + * @param job 调度任务信息
  56 + * @return 结果
  57 + */
  58 + public int updateJob(SysJob job);
  59 +
  60 + /**
  61 + * 新增调度任务信息
  62 + *
  63 + * @param job 调度任务信息
  64 + * @return 结果
  65 + */
  66 + public int insertJob(SysJob job);
  67 +}
  1 +package com.ruoyi.quartz.service;
  2 +
  3 +import java.util.List;
  4 +import com.ruoyi.quartz.domain.SysJobLog;
  5 +
  6 +/**
  7 + * 定时任务调度日志信息信息 服务层
  8 + *
  9 + * @author ruoyi
  10 + */
  11 +public interface ISysJobLogService
  12 +{
  13 + /**
  14 + * 获取quartz调度器日志的计划任务
  15 + *
  16 + * @param jobLog 调度日志信息
  17 + * @return 调度任务日志集合
  18 + */
  19 + public List<SysJobLog> selectJobLogList(SysJobLog jobLog);
  20 +
  21 + /**
  22 + * 通过调度任务日志ID查询调度信息
  23 + *
  24 + * @param jobLogId 调度任务日志ID
  25 + * @return 调度任务日志对象信息
  26 + */
  27 + public SysJobLog selectJobLogById(Long jobLogId);
  28 +
  29 + /**
  30 + * 新增任务日志
  31 + *
  32 + * @param jobLog 调度日志信息
  33 + */
  34 + public void addJobLog(SysJobLog jobLog);
  35 +
  36 + /**
  37 + * 批量删除调度日志信息
  38 + *
  39 + * @param logIds 需要删除的日志ID
  40 + * @return 结果
  41 + */
  42 + public int deleteJobLogByIds(Long[] logIds);
  43 +
  44 + /**
  45 + * 删除任务日志
  46 + *
  47 + * @param jobId 调度日志ID
  48 + * @return 结果
  49 + */
  50 + public int deleteJobLogById(Long jobId);
  51 +
  52 + /**
  53 + * 清空任务日志
  54 + */
  55 + public void cleanJobLog();
  56 +}
  1 +package com.ruoyi.quartz.service;
  2 +
  3 +import java.util.List;
  4 +import org.quartz.SchedulerException;
  5 +import com.ruoyi.common.exception.job.TaskException;
  6 +import com.ruoyi.quartz.domain.SysJob;
  7 +
  8 +/**
  9 + * 定时任务调度信息信息 服务层
  10 + *
  11 + * @author ruoyi
  12 + */
  13 +public interface ISysJobService
  14 +{
  15 + /**
  16 + * 获取quartz调度器的计划任务
  17 + *
  18 + * @param job 调度信息
  19 + * @return 调度任务集合
  20 + */
  21 + public List<SysJob> selectJobList(SysJob job);
  22 +
  23 + /**
  24 + * 通过调度任务ID查询调度信息
  25 + *
  26 + * @param jobId 调度任务ID
  27 + * @return 调度任务对象信息
  28 + */
  29 + public SysJob selectJobById(Long jobId);
  30 +
  31 + /**
  32 + * 暂停任务
  33 + *
  34 + * @param job 调度信息
  35 + * @return 结果
  36 + */
  37 + public int pauseJob(SysJob job) throws SchedulerException;
  38 +
  39 + /**
  40 + * 恢复任务
  41 + *
  42 + * @param job 调度信息
  43 + * @return 结果
  44 + */
  45 + public int resumeJob(SysJob job) throws SchedulerException;
  46 +
  47 + /**
  48 + * 删除任务后,所对应的trigger也将被删除
  49 + *
  50 + * @param job 调度信息
  51 + * @return 结果
  52 + */
  53 + public int deleteJob(SysJob job) throws SchedulerException;
  54 +
  55 + /**
  56 + * 批量删除调度信息
  57 + *
  58 + * @param jobIds 需要删除的任务ID
  59 + * @return 结果
  60 + */
  61 + public void deleteJobByIds(Long[] jobIds) throws SchedulerException;
  62 +
  63 + /**
  64 + * 任务调度状态修改
  65 + *
  66 + * @param job 调度信息
  67 + * @return 结果
  68 + */
  69 + public int changeStatus(SysJob job) throws SchedulerException;
  70 +
  71 + /**
  72 + * 立即运行任务
  73 + *
  74 + * @param job 调度信息
  75 + * @return 结果
  76 + */
  77 + public void run(SysJob job) throws SchedulerException;
  78 +
  79 + /**
  80 + * 新增任务
  81 + *
  82 + * @param job 调度信息
  83 + * @return 结果
  84 + */
  85 + public int insertJob(SysJob job) throws SchedulerException, TaskException;
  86 +
  87 + /**
  88 + * 更新任务
  89 + *
  90 + * @param job 调度信息
  91 + * @return 结果
  92 + */
  93 + public int updateJob(SysJob job) throws SchedulerException, TaskException;
  94 +
  95 + /**
  96 + * 校验cron表达式是否有效
  97 + *
  98 + * @param cronExpression 表达式
  99 + * @return 结果
  100 + */
  101 + public boolean checkCronExpressionIsValid(String cronExpression);
  102 +}
  1 +package com.ruoyi.quartz.service.impl;
  2 +
  3 +import java.util.List;
  4 +import org.springframework.beans.factory.annotation.Autowired;
  5 +import org.springframework.stereotype.Service;
  6 +import com.ruoyi.quartz.domain.SysJobLog;
  7 +import com.ruoyi.quartz.mapper.SysJobLogMapper;
  8 +import com.ruoyi.quartz.service.ISysJobLogService;
  9 +
  10 +/**
  11 + * 定时任务调度日志信息 服务层
  12 + *
  13 + * @author ruoyi
  14 + */
  15 +@Service
  16 +public class SysJobLogServiceImpl implements ISysJobLogService
  17 +{
  18 + @Autowired
  19 + private SysJobLogMapper jobLogMapper;
  20 +
  21 + /**
  22 + * 获取quartz调度器日志的计划任务
  23 + *
  24 + * @param jobLog 调度日志信息
  25 + * @return 调度任务日志集合
  26 + */
  27 + @Override
  28 + public List<SysJobLog> selectJobLogList(SysJobLog jobLog)
  29 + {
  30 + return jobLogMapper.selectJobLogList(jobLog);
  31 + }
  32 +
  33 + /**
  34 + * 通过调度任务日志ID查询调度信息
  35 + *
  36 + * @param jobLogId 调度任务日志ID
  37 + * @return 调度任务日志对象信息
  38 + */
  39 + @Override
  40 + public SysJobLog selectJobLogById(Long jobLogId)
  41 + {
  42 + return jobLogMapper.selectJobLogById(jobLogId);
  43 + }
  44 +
  45 + /**
  46 + * 新增任务日志
  47 + *
  48 + * @param jobLog 调度日志信息
  49 + */
  50 + @Override
  51 + public void addJobLog(SysJobLog jobLog)
  52 + {
  53 + jobLogMapper.insertJobLog(jobLog);
  54 + }
  55 +
  56 + /**
  57 + * 批量删除调度日志信息
  58 + *
  59 + * @param logIds 需要删除的数据ID
  60 + * @return 结果
  61 + */
  62 + @Override
  63 + public int deleteJobLogByIds(Long[] logIds)
  64 + {
  65 + return jobLogMapper.deleteJobLogByIds(logIds);
  66 + }
  67 +
  68 + /**
  69 + * 删除任务日志
  70 + *
  71 + * @param jobId 调度日志ID
  72 + */
  73 + @Override
  74 + public int deleteJobLogById(Long jobId)
  75 + {
  76 + return jobLogMapper.deleteJobLogById(jobId);
  77 + }
  78 +
  79 + /**
  80 + * 清空任务日志
  81 + */
  82 + @Override
  83 + public void cleanJobLog()
  84 + {
  85 + jobLogMapper.cleanJobLog();
  86 + }
  87 +}
  1 +package com.ruoyi.quartz.service.impl;
  2 +
  3 +import java.util.List;
  4 +import javax.annotation.PostConstruct;
  5 +import org.quartz.JobDataMap;
  6 +import org.quartz.JobKey;
  7 +import org.quartz.Scheduler;
  8 +import org.quartz.SchedulerException;
  9 +import org.springframework.beans.factory.annotation.Autowired;
  10 +import org.springframework.stereotype.Service;
  11 +import org.springframework.transaction.annotation.Transactional;
  12 +import com.ruoyi.common.constant.ScheduleConstants;
  13 +import com.ruoyi.common.exception.job.TaskException;
  14 +import com.ruoyi.quartz.domain.SysJob;
  15 +import com.ruoyi.quartz.mapper.SysJobMapper;
  16 +import com.ruoyi.quartz.service.ISysJobService;
  17 +import com.ruoyi.quartz.util.CronUtils;
  18 +import com.ruoyi.quartz.util.ScheduleUtils;
  19 +
  20 +/**
  21 + * 定时任务调度信息 服务层
  22 + *
  23 + * @author ruoyi
  24 + */
  25 +@Service
  26 +public class SysJobServiceImpl implements ISysJobService
  27 +{
  28 + @Autowired
  29 + private Scheduler scheduler;
  30 +
  31 + @Autowired
  32 + private SysJobMapper jobMapper;
  33 +
  34 + /**
  35 + * 项目启动时,初始化定时器 主要是防止手动修改数据库导致未同步到定时任务处理(注:不能手动修改数据库ID和任务组名,否则会导致脏数据)
  36 + */
  37 + @PostConstruct
  38 + public void init() throws SchedulerException, TaskException
  39 + {
  40 + scheduler.clear();
  41 + List<SysJob> jobList = jobMapper.selectJobAll();
  42 + for (SysJob job : jobList)
  43 + {
  44 + ScheduleUtils.createScheduleJob(scheduler, job);
  45 + }
  46 + }
  47 +
  48 + /**
  49 + * 获取quartz调度器的计划任务列表
  50 + *
  51 + * @param job 调度信息
  52 + * @return
  53 + */
  54 + @Override
  55 + public List<SysJob> selectJobList(SysJob job)
  56 + {
  57 + return jobMapper.selectJobList(job);
  58 + }
  59 +
  60 + /**
  61 + * 通过调度任务ID查询调度信息
  62 + *
  63 + * @param jobId 调度任务ID
  64 + * @return 调度任务对象信息
  65 + */
  66 + @Override
  67 + public SysJob selectJobById(Long jobId)
  68 + {
  69 + return jobMapper.selectJobById(jobId);
  70 + }
  71 +
  72 + /**
  73 + * 暂停任务
  74 + *
  75 + * @param job 调度信息
  76 + */
  77 + @Override
  78 + @Transactional(rollbackFor = Exception.class)
  79 + public int pauseJob(SysJob job) throws SchedulerException
  80 + {
  81 + Long jobId = job.getJobId();
  82 + String jobGroup = job.getJobGroup();
  83 + job.setStatus(ScheduleConstants.Status.PAUSE.getValue());
  84 + int rows = jobMapper.updateJob(job);
  85 + if (rows > 0)
  86 + {
  87 + scheduler.pauseJob(ScheduleUtils.getJobKey(jobId, jobGroup));
  88 + }
  89 + return rows;
  90 + }
  91 +
  92 + /**
  93 + * 恢复任务
  94 + *
  95 + * @param job 调度信息
  96 + */
  97 + @Override
  98 + @Transactional(rollbackFor = Exception.class)
  99 + public int resumeJob(SysJob job) throws SchedulerException
  100 + {
  101 + Long jobId = job.getJobId();
  102 + String jobGroup = job.getJobGroup();
  103 + job.setStatus(ScheduleConstants.Status.NORMAL.getValue());
  104 + int rows = jobMapper.updateJob(job);
  105 + if (rows > 0)
  106 + {
  107 + scheduler.resumeJob(ScheduleUtils.getJobKey(jobId, jobGroup));
  108 + }
  109 + return rows;
  110 + }
  111 +
  112 + /**
  113 + * 删除任务后,所对应的trigger也将被删除
  114 + *
  115 + * @param job 调度信息
  116 + */
  117 + @Override
  118 + @Transactional(rollbackFor = Exception.class)
  119 + public int deleteJob(SysJob job) throws SchedulerException
  120 + {
  121 + Long jobId = job.getJobId();
  122 + String jobGroup = job.getJobGroup();
  123 + int rows = jobMapper.deleteJobById(jobId);
  124 + if (rows > 0)
  125 + {
  126 + scheduler.deleteJob(ScheduleUtils.getJobKey(jobId, jobGroup));
  127 + }
  128 + return rows;
  129 + }
  130 +
  131 + /**
  132 + * 批量删除调度信息
  133 + *
  134 + * @param jobIds 需要删除的任务ID
  135 + * @return 结果
  136 + */
  137 + @Override
  138 + @Transactional(rollbackFor = Exception.class)
  139 + public void deleteJobByIds(Long[] jobIds) throws SchedulerException
  140 + {
  141 + for (Long jobId : jobIds)
  142 + {
  143 + SysJob job = jobMapper.selectJobById(jobId);
  144 + deleteJob(job);
  145 + }
  146 + }
  147 +
  148 + /**
  149 + * 任务调度状态修改
  150 + *
  151 + * @param job 调度信息
  152 + */
  153 + @Override
  154 + @Transactional(rollbackFor = Exception.class)
  155 + public int changeStatus(SysJob job) throws SchedulerException
  156 + {
  157 + int rows = 0;
  158 + String status = job.getStatus();
  159 + if (ScheduleConstants.Status.NORMAL.getValue().equals(status))
  160 + {
  161 + rows = resumeJob(job);
  162 + }
  163 + else if (ScheduleConstants.Status.PAUSE.getValue().equals(status))
  164 + {
  165 + rows = pauseJob(job);
  166 + }
  167 + return rows;
  168 + }
  169 +
  170 + /**
  171 + * 立即运行任务
  172 + *
  173 + * @param job 调度信息
  174 + */
  175 + @Override
  176 + @Transactional(rollbackFor = Exception.class)
  177 + public void run(SysJob job) throws SchedulerException
  178 + {
  179 + Long jobId = job.getJobId();
  180 + String jobGroup = job.getJobGroup();
  181 + SysJob properties = selectJobById(job.getJobId());
  182 + // 参数
  183 + JobDataMap dataMap = new JobDataMap();
  184 + dataMap.put(ScheduleConstants.TASK_PROPERTIES, properties);
  185 + scheduler.triggerJob(ScheduleUtils.getJobKey(jobId, jobGroup), dataMap);
  186 + }
  187 +
  188 + /**
  189 + * 新增任务
  190 + *
  191 + * @param job 调度信息 调度信息
  192 + */
  193 + @Override
  194 + @Transactional(rollbackFor = Exception.class)
  195 + public int insertJob(SysJob job) throws SchedulerException, TaskException
  196 + {
  197 + job.setStatus(ScheduleConstants.Status.PAUSE.getValue());
  198 + int rows = jobMapper.insertJob(job);
  199 + if (rows > 0)
  200 + {
  201 + ScheduleUtils.createScheduleJob(scheduler, job);
  202 + }
  203 + return rows;
  204 + }
  205 +
  206 + /**
  207 + * 更新任务的时间表达式
  208 + *
  209 + * @param job 调度信息
  210 + */
  211 + @Override
  212 + @Transactional(rollbackFor = Exception.class)
  213 + public int updateJob(SysJob job) throws SchedulerException, TaskException
  214 + {
  215 + SysJob properties = selectJobById(job.getJobId());
  216 + int rows = jobMapper.updateJob(job);
  217 + if (rows > 0)
  218 + {
  219 + updateSchedulerJob(job, properties.getJobGroup());
  220 + }
  221 + return rows;
  222 + }
  223 +
  224 + /**
  225 + * 更新任务
  226 + *
  227 + * @param job 任务对象
  228 + * @param jobGroup 任务组名
  229 + */
  230 + public void updateSchedulerJob(SysJob job, String jobGroup) throws SchedulerException, TaskException
  231 + {
  232 + Long jobId = job.getJobId();
  233 + // 判断是否存在
  234 + JobKey jobKey = ScheduleUtils.getJobKey(jobId, jobGroup);
  235 + if (scheduler.checkExists(jobKey))
  236 + {
  237 + // 防止创建时存在数据问题 先移除,然后在执行创建操作
  238 + scheduler.deleteJob(jobKey);
  239 + }
  240 + ScheduleUtils.createScheduleJob(scheduler, job);
  241 + }
  242 +
  243 + /**
  244 + * 校验cron表达式是否有效
  245 + *
  246 + * @param cronExpression 表达式
  247 + * @return 结果
  248 + */
  249 + @Override
  250 + public boolean checkCronExpressionIsValid(String cronExpression)
  251 + {
  252 + return CronUtils.isValid(cronExpression);
  253 + }
  254 +}
  1 +package com.ruoyi.quartz.task;
  2 +
  3 +import org.springframework.stereotype.Component;
  4 +import com.ruoyi.common.utils.StringUtils;
  5 +
  6 +/**
  7 + * 定时任务调度测试
  8 + *
  9 + * @author ruoyi
  10 + */
  11 +@Component("ryTask")
  12 +public class RyTask
  13 +{
  14 + public void ryMultipleParams(String s, Boolean b, Long l, Double d, Integer i)
  15 + {
  16 + System.out.println(StringUtils.format("执行多参方法: 字符串类型{},布尔类型{},长整型{},浮点型{},整形{}", s, b, l, d, i));
  17 + }
  18 +
  19 + public void ryParams(String params)
  20 + {
  21 + System.out.println("执行有参方法:" + params);
  22 + }
  23 +
  24 + public void ryNoParams()
  25 + {
  26 + System.out.println("执行无参方法");
  27 + }
  28 +}
  1 +package com.ruoyi.quartz.util;
  2 +
  3 +import java.util.Date;
  4 +import org.quartz.Job;
  5 +import org.quartz.JobExecutionContext;
  6 +import org.quartz.JobExecutionException;
  7 +import org.slf4j.Logger;
  8 +import org.slf4j.LoggerFactory;
  9 +import com.ruoyi.common.constant.Constants;
  10 +import com.ruoyi.common.constant.ScheduleConstants;
  11 +import com.ruoyi.common.utils.ExceptionUtil;
  12 +import com.ruoyi.common.utils.StringUtils;
  13 +import com.ruoyi.common.utils.bean.BeanUtils;
  14 +import com.ruoyi.common.utils.spring.SpringUtils;
  15 +import com.ruoyi.quartz.domain.SysJob;
  16 +import com.ruoyi.quartz.domain.SysJobLog;
  17 +import com.ruoyi.quartz.service.ISysJobLogService;
  18 +
  19 +/**
  20 + * 抽象quartz调用
  21 + *
  22 + * @author ruoyi
  23 + */
  24 +public abstract class AbstractQuartzJob implements Job
  25 +{
  26 + private static final Logger log = LoggerFactory.getLogger(AbstractQuartzJob.class);
  27 +
  28 + /**
  29 + * 线程本地变量
  30 + */
  31 + private static ThreadLocal<Date> threadLocal = new ThreadLocal<>();
  32 +
  33 + @Override
  34 + public void execute(JobExecutionContext context) throws JobExecutionException
  35 + {
  36 + SysJob sysJob = new SysJob();
  37 + BeanUtils.copyBeanProp(sysJob, context.getMergedJobDataMap().get(ScheduleConstants.TASK_PROPERTIES));
  38 + try
  39 + {
  40 + before(context, sysJob);
  41 + if (sysJob != null)
  42 + {
  43 + doExecute(context, sysJob);
  44 + }
  45 + after(context, sysJob, null);
  46 + }
  47 + catch (Exception e)
  48 + {
  49 + log.error("任务执行异常 - :", e);
  50 + after(context, sysJob, e);
  51 + }
  52 + }
  53 +
  54 + /**
  55 + * 执行前
  56 + *
  57 + * @param context 工作执行上下文对象
  58 + * @param sysJob 系统计划任务
  59 + */
  60 + protected void before(JobExecutionContext context, SysJob sysJob)
  61 + {
  62 + threadLocal.set(new Date());
  63 + }
  64 +
  65 + /**
  66 + * 执行后
  67 + *
  68 + * @param context 工作执行上下文对象
  69 + * @param sysJob 系统计划任务
  70 + */
  71 + protected void after(JobExecutionContext context, SysJob sysJob, Exception e)
  72 + {
  73 + Date startTime = threadLocal.get();
  74 + threadLocal.remove();
  75 +
  76 + final SysJobLog sysJobLog = new SysJobLog();
  77 + sysJobLog.setJobName(sysJob.getJobName());
  78 + sysJobLog.setJobGroup(sysJob.getJobGroup());
  79 + sysJobLog.setInvokeTarget(sysJob.getInvokeTarget());
  80 + sysJobLog.setStartTime(startTime);
  81 + sysJobLog.setStopTime(new Date());
  82 + long runMs = sysJobLog.getStopTime().getTime() - sysJobLog.getStartTime().getTime();
  83 + sysJobLog.setJobMessage(sysJobLog.getJobName() + " 总共耗时:" + runMs + "毫秒");
  84 + if (e != null)
  85 + {
  86 + sysJobLog.setStatus(Constants.FAIL);
  87 + String errorMsg = StringUtils.substring(ExceptionUtil.getExceptionMessage(e), 0, 2000);
  88 + sysJobLog.setExceptionInfo(errorMsg);
  89 + }
  90 + else
  91 + {
  92 + sysJobLog.setStatus(Constants.SUCCESS);
  93 + }
  94 +
  95 + // 写入数据库当中
  96 + SpringUtils.getBean(ISysJobLogService.class).addJobLog(sysJobLog);
  97 + }
  98 +
  99 + /**
  100 + * 执行方法,由子类重载
  101 + *
  102 + * @param context 工作执行上下文对象
  103 + * @param sysJob 系统计划任务
  104 + * @throws Exception 执行过程中的异常
  105 + */
  106 + protected abstract void doExecute(JobExecutionContext context, SysJob sysJob) throws Exception;
  107 +}
  1 +package com.ruoyi.quartz.util;
  2 +
  3 +import java.text.ParseException;
  4 +import java.util.Date;
  5 +import org.quartz.CronExpression;
  6 +
  7 +/**
  8 + * cron表达式工具类
  9 + *
  10 + * @author ruoyi
  11 + *
  12 + */
  13 +public class CronUtils
  14 +{
  15 + /**
  16 + * 返回一个布尔值代表一个给定的Cron表达式的有效性
  17 + *
  18 + * @param cronExpression Cron表达式
  19 + * @return boolean 表达式是否有效
  20 + */
  21 + public static boolean isValid(String cronExpression)
  22 + {
  23 + return CronExpression.isValidExpression(cronExpression);
  24 + }
  25 +
  26 + /**
  27 + * 返回一个字符串值,表示该消息无效Cron表达式给出有效性
  28 + *
  29 + * @param cronExpression Cron表达式
  30 + * @return String 无效时返回表达式错误描述,如果有效返回null
  31 + */
  32 + public static String getInvalidMessage(String cronExpression)
  33 + {
  34 + try
  35 + {
  36 + new CronExpression(cronExpression);
  37 + return null;
  38 + }
  39 + catch (ParseException pe)
  40 + {
  41 + return pe.getMessage();
  42 + }
  43 + }
  44 +
  45 + /**
  46 + * 返回下一个执行时间根据给定的Cron表达式
  47 + *
  48 + * @param cronExpression Cron表达式
  49 + * @return Date 下次Cron表达式执行时间
  50 + */
  51 + public static Date getNextExecution(String cronExpression)
  52 + {
  53 + try
  54 + {
  55 + CronExpression cron = new CronExpression(cronExpression);
  56 + return cron.getNextValidTimeAfter(new Date(System.currentTimeMillis()));
  57 + }
  58 + catch (ParseException e)
  59 + {
  60 + throw new IllegalArgumentException(e.getMessage());
  61 + }
  62 + }
  63 +}
  1 +package com.ruoyi.quartz.util;
  2 +
  3 +import java.lang.reflect.InvocationTargetException;
  4 +import java.lang.reflect.Method;
  5 +import java.util.LinkedList;
  6 +import java.util.List;
  7 +import com.ruoyi.common.utils.StringUtils;
  8 +import com.ruoyi.common.utils.spring.SpringUtils;
  9 +import com.ruoyi.quartz.domain.SysJob;
  10 +
  11 +/**
  12 + * 任务执行工具
  13 + *
  14 + * @author ruoyi
  15 + */
  16 +public class JobInvokeUtil
  17 +{
  18 + /**
  19 + * 执行方法
  20 + *
  21 + * @param sysJob 系统任务
  22 + */
  23 + public static void invokeMethod(SysJob sysJob) throws Exception
  24 + {
  25 + String invokeTarget = sysJob.getInvokeTarget();
  26 + String beanName = getBeanName(invokeTarget);
  27 + String methodName = getMethodName(invokeTarget);
  28 + List<Object[]> methodParams = getMethodParams(invokeTarget);
  29 +
  30 + if (!isValidClassName(beanName))
  31 + {
  32 + Object bean = SpringUtils.getBean(beanName);
  33 + invokeMethod(bean, methodName, methodParams);
  34 + }
  35 + else
  36 + {
  37 + Object bean = Class.forName(beanName).newInstance();
  38 + invokeMethod(bean, methodName, methodParams);
  39 + }
  40 + }
  41 +
  42 + /**
  43 + * 调用任务方法
  44 + *
  45 + * @param bean 目标对象
  46 + * @param methodName 方法名称
  47 + * @param methodParams 方法参数
  48 + */
  49 + private static void invokeMethod(Object bean, String methodName, List<Object[]> methodParams)
  50 + throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException,
  51 + InvocationTargetException
  52 + {
  53 + if (StringUtils.isNotNull(methodParams) && methodParams.size() > 0)
  54 + {
  55 + Method method = bean.getClass().getDeclaredMethod(methodName, getMethodParamsType(methodParams));
  56 + method.invoke(bean, getMethodParamsValue(methodParams));
  57 + }
  58 + else
  59 + {
  60 + Method method = bean.getClass().getDeclaredMethod(methodName);
  61 + method.invoke(bean);
  62 + }
  63 + }
  64 +
  65 + /**
  66 + * 校验是否为为class包名
  67 + *
  68 + * @param str 名称
  69 + * @return true是 false否
  70 + */
  71 + public static boolean isValidClassName(String invokeTarget)
  72 + {
  73 + return StringUtils.countMatches(invokeTarget, ".") > 1;
  74 + }
  75 +
  76 + /**
  77 + * 获取bean名称
  78 + *
  79 + * @param invokeTarget 目标字符串
  80 + * @return bean名称
  81 + */
  82 + public static String getBeanName(String invokeTarget)
  83 + {
  84 + String beanName = StringUtils.substringBefore(invokeTarget, "(");
  85 + return StringUtils.substringBeforeLast(beanName, ".");
  86 + }
  87 +
  88 + /**
  89 + * 获取bean方法
  90 + *
  91 + * @param invokeTarget 目标字符串
  92 + * @return method方法
  93 + */
  94 + public static String getMethodName(String invokeTarget)
  95 + {
  96 + String methodName = StringUtils.substringBefore(invokeTarget, "(");
  97 + return StringUtils.substringAfterLast(methodName, ".");
  98 + }
  99 +
  100 + /**
  101 + * 获取method方法参数相关列表
  102 + *
  103 + * @param invokeTarget 目标字符串
  104 + * @return method方法相关参数列表
  105 + */
  106 + public static List<Object[]> getMethodParams(String invokeTarget)
  107 + {
  108 + String methodStr = StringUtils.substringBetween(invokeTarget, "(", ")");
  109 + if (StringUtils.isEmpty(methodStr))
  110 + {
  111 + return null;
  112 + }
  113 + String[] methodParams = methodStr.split(",(?=(?:[^\']*\"[^\']*\')*[^\']*$)");
  114 + List<Object[]> classs = new LinkedList<>();
  115 + for (int i = 0; i < methodParams.length; i++)
  116 + {
  117 + String str = StringUtils.trimToEmpty(methodParams[i]);
  118 + // String字符串类型,包含'
  119 + if (StringUtils.contains(str, "'"))
  120 + {
  121 + classs.add(new Object[] { StringUtils.replace(str, "'", ""), String.class });
  122 + }
  123 + // boolean布尔类型,等于true或者false
  124 + else if (StringUtils.equals(str, "true") || StringUtils.equalsIgnoreCase(str, "false"))
  125 + {
  126 + classs.add(new Object[] { Boolean.valueOf(str), Boolean.class });
  127 + }
  128 + // long长整形,包含L
  129 + else if (StringUtils.containsIgnoreCase(str, "L"))
  130 + {
  131 + classs.add(new Object[] { Long.valueOf(StringUtils.replaceIgnoreCase(str, "L", "")), Long.class });
  132 + }
  133 + // double浮点类型,包含D
  134 + else if (StringUtils.containsIgnoreCase(str, "D"))
  135 + {
  136 + classs.add(new Object[] { Double.valueOf(StringUtils.replaceIgnoreCase(str, "D", "")), Double.class });
  137 + }
  138 + // 其他类型归类为整形
  139 + else
  140 + {
  141 + classs.add(new Object[] { Integer.valueOf(str), Integer.class });
  142 + }
  143 + }
  144 + return classs;
  145 + }
  146 +
  147 + /**
  148 + * 获取参数类型
  149 + *
  150 + * @param methodParams 参数相关列表
  151 + * @return 参数类型列表
  152 + */
  153 + public static Class<?>[] getMethodParamsType(List<Object[]> methodParams)
  154 + {
  155 + Class<?>[] classs = new Class<?>[methodParams.size()];
  156 + int index = 0;
  157 + for (Object[] os : methodParams)
  158 + {
  159 + classs[index] = (Class<?>) os[1];
  160 + index++;
  161 + }
  162 + return classs;
  163 + }
  164 +
  165 + /**
  166 + * 获取参数值
  167 + *
  168 + * @param methodParams 参数相关列表
  169 + * @return 参数值列表
  170 + */
  171 + public static Object[] getMethodParamsValue(List<Object[]> methodParams)
  172 + {
  173 + Object[] classs = new Object[methodParams.size()];
  174 + int index = 0;
  175 + for (Object[] os : methodParams)
  176 + {
  177 + classs[index] = (Object) os[0];
  178 + index++;
  179 + }
  180 + return classs;
  181 + }
  182 +}
  1 +package com.ruoyi.quartz.util;
  2 +
  3 +import org.quartz.DisallowConcurrentExecution;
  4 +import org.quartz.JobExecutionContext;
  5 +import com.ruoyi.quartz.domain.SysJob;
  6 +
  7 +/**
  8 + * 定时任务处理(禁止并发执行)
  9 + *
  10 + * @author ruoyi
  11 + *
  12 + */
  13 +@DisallowConcurrentExecution
  14 +public class QuartzDisallowConcurrentExecution extends AbstractQuartzJob
  15 +{
  16 + @Override
  17 + protected void doExecute(JobExecutionContext context, SysJob sysJob) throws Exception
  18 + {
  19 + JobInvokeUtil.invokeMethod(sysJob);
  20 + }
  21 +}
  1 +package com.ruoyi.quartz.util;
  2 +
  3 +import org.quartz.JobExecutionContext;
  4 +import com.ruoyi.quartz.domain.SysJob;
  5 +
  6 +/**
  7 + * 定时任务处理(允许并发执行)
  8 + *
  9 + * @author ruoyi
  10 + *
  11 + */
  12 +public class QuartzJobExecution extends AbstractQuartzJob
  13 +{
  14 + @Override
  15 + protected void doExecute(JobExecutionContext context, SysJob sysJob) throws Exception
  16 + {
  17 + JobInvokeUtil.invokeMethod(sysJob);
  18 + }
  19 +}
  1 +package com.ruoyi.quartz.util;
  2 +
  3 +import org.quartz.CronScheduleBuilder;
  4 +import org.quartz.CronTrigger;
  5 +import org.quartz.Job;
  6 +import org.quartz.JobBuilder;
  7 +import org.quartz.JobDetail;
  8 +import org.quartz.JobKey;
  9 +import org.quartz.Scheduler;
  10 +import org.quartz.SchedulerException;
  11 +import org.quartz.TriggerBuilder;
  12 +import org.quartz.TriggerKey;
  13 +import com.ruoyi.common.constant.ScheduleConstants;
  14 +import com.ruoyi.common.exception.job.TaskException;
  15 +import com.ruoyi.common.exception.job.TaskException.Code;
  16 +import com.ruoyi.quartz.domain.SysJob;
  17 +
  18 +/**
  19 + * 定时任务工具类
  20 + *
  21 + * @author ruoyi
  22 + *
  23 + */
  24 +public class ScheduleUtils
  25 +{
  26 + /**
  27 + * 得到quartz任务类
  28 + *
  29 + * @param sysJob 执行计划
  30 + * @return 具体执行任务类
  31 + */
  32 + private static Class<? extends Job> getQuartzJobClass(SysJob sysJob)
  33 + {
  34 + boolean isConcurrent = "0".equals(sysJob.getConcurrent());
  35 + return isConcurrent ? QuartzJobExecution.class : QuartzDisallowConcurrentExecution.class;
  36 + }
  37 +
  38 + /**
  39 + * 构建任务触发对象
  40 + */
  41 + public static TriggerKey getTriggerKey(Long jobId, String jobGroup)
  42 + {
  43 + return TriggerKey.triggerKey(ScheduleConstants.TASK_CLASS_NAME + jobId, jobGroup);
  44 + }
  45 +
  46 + /**
  47 + * 构建任务键对象
  48 + */
  49 + public static JobKey getJobKey(Long jobId, String jobGroup)
  50 + {
  51 + return JobKey.jobKey(ScheduleConstants.TASK_CLASS_NAME + jobId, jobGroup);
  52 + }
  53 +
  54 + /**
  55 + * 创建定时任务
  56 + */
  57 + public static void createScheduleJob(Scheduler scheduler, SysJob job) throws SchedulerException, TaskException
  58 + {
  59 + Class<? extends Job> jobClass = getQuartzJobClass(job);
  60 + // 构建job信息
  61 + Long jobId = job.getJobId();
  62 + String jobGroup = job.getJobGroup();
  63 + JobDetail jobDetail = JobBuilder.newJob(jobClass).withIdentity(getJobKey(jobId, jobGroup)).build();
  64 +
  65 + // 表达式调度构建器
  66 + CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(job.getCronExpression());
  67 + cronScheduleBuilder = handleCronScheduleMisfirePolicy(job, cronScheduleBuilder);
  68 +
  69 + // 按新的cronExpression表达式构建一个新的trigger
  70 + CronTrigger trigger = TriggerBuilder.newTrigger().withIdentity(getTriggerKey(jobId, jobGroup))
  71 + .withSchedule(cronScheduleBuilder).build();
  72 +
  73 + // 放入参数,运行时的方法可以获取
  74 + jobDetail.getJobDataMap().put(ScheduleConstants.TASK_PROPERTIES, job);
  75 +
  76 + // 判断是否存在
  77 + if (scheduler.checkExists(getJobKey(jobId, jobGroup)))
  78 + {
  79 + // 防止创建时存在数据问题 先移除,然后在执行创建操作
  80 + scheduler.deleteJob(getJobKey(jobId, jobGroup));
  81 + }
  82 +
  83 + scheduler.scheduleJob(jobDetail, trigger);
  84 +
  85 + // 暂停任务
  86 + if (job.getStatus().equals(ScheduleConstants.Status.PAUSE.getValue()))
  87 + {
  88 + scheduler.pauseJob(ScheduleUtils.getJobKey(jobId, jobGroup));
  89 + }
  90 + }
  91 +
  92 + /**
  93 + * 设置定时任务策略
  94 + */
  95 + public static CronScheduleBuilder handleCronScheduleMisfirePolicy(SysJob job, CronScheduleBuilder cb)
  96 + throws TaskException
  97 + {
  98 + switch (job.getMisfirePolicy())
  99 + {
  100 + case ScheduleConstants.MISFIRE_DEFAULT:
  101 + return cb;
  102 + case ScheduleConstants.MISFIRE_IGNORE_MISFIRES:
  103 + return cb.withMisfireHandlingInstructionIgnoreMisfires();
  104 + case ScheduleConstants.MISFIRE_FIRE_AND_PROCEED:
  105 + return cb.withMisfireHandlingInstructionFireAndProceed();
  106 + case ScheduleConstants.MISFIRE_DO_NOTHING:
  107 + return cb.withMisfireHandlingInstructionDoNothing();
  108 + default:
  109 + throw new TaskException("The task misfire policy '" + job.getMisfirePolicy()
  110 + + "' cannot be used in cron schedule tasks", Code.CONFIG_ERROR);
  111 + }
  112 + }
  113 +}
  1 +<?xml version="1.0" encoding="UTF-8" ?>
  2 +<!DOCTYPE mapper
  3 +PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
  4 +"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
  5 +<mapper namespace="com.ruoyi.quartz.mapper.SysJobLogMapper">
  6 +
  7 + <resultMap type="SysJobLog" id="SysJobLogResult">
  8 + <id property="jobLogId" column="job_log_id" />
  9 + <result property="jobName" column="job_name" />
  10 + <result property="jobGroup" column="job_group" />
  11 + <result property="invokeTarget" column="invoke_target" />
  12 + <result property="jobMessage" column="job_message" />
  13 + <result property="status" column="status" />
  14 + <result property="exceptionInfo" column="exception_info" />
  15 + <result property="createTime" column="create_time" />
  16 + </resultMap>
  17 +
  18 + <sql id="selectJobLogVo">
  19 + select job_log_id, job_name, job_group, invoke_target, job_message, status, exception_info, create_time
  20 + from sys_job_log
  21 + </sql>
  22 +
  23 + <select id="selectJobLogList" parameterType="SysJobLog" resultMap="SysJobLogResult">
  24 + <include refid="selectJobLogVo"/>
  25 + <where>
  26 + <if test="jobName != null and jobName != ''">
  27 + AND job_name like concat('%', #{jobName}, '%')
  28 + </if>
  29 + <if test="jobGroup != null and jobGroup != ''">
  30 + AND job_group = #{jobGroup}
  31 + </if>
  32 + <if test="status != null and status != ''">
  33 + AND status = #{status}
  34 + </if>
  35 + <if test="invokeTarget != null and invokeTarget != ''">
  36 + AND invoke_target like concat('%', #{invokeTarget}, '%')
  37 + </if>
  38 + <if test="params.beginTime != null and params.beginTime != ''"><!-- 开始时间检索 -->
  39 + and date_format(create_time,'%y%m%d') &gt;= date_format(#{params.beginTime},'%y%m%d')
  40 + </if>
  41 + <if test="params.endTime != null and params.endTime != ''"><!-- 结束时间检索 -->
  42 + and date_format(create_time,'%y%m%d') &lt;= date_format(#{params.endTime},'%y%m%d')
  43 + </if>
  44 + </where>
  45 + </select>
  46 +
  47 + <select id="selectJobLogAll" resultMap="SysJobLogResult">
  48 + <include refid="selectJobLogVo"/>
  49 + </select>
  50 +
  51 + <select id="selectJobLogById" parameterType="Long" resultMap="SysJobLogResult">
  52 + <include refid="selectJobLogVo"/>
  53 + where job_log_id = #{jobLogId}
  54 + </select>
  55 +
  56 + <delete id="deleteJobLogById" parameterType="Long">
  57 + delete from sys_job_log where job_log_id = #{jobLogId}
  58 + </delete>
  59 +
  60 + <delete id="deleteJobLogByIds" parameterType="Long">
  61 + delete from sys_job_log where job_log_id in
  62 + <foreach collection="array" item="jobLogId" open="(" separator="," close=")">
  63 + #{jobLogId}
  64 + </foreach>
  65 + </delete>
  66 +
  67 + <update id="cleanJobLog">
  68 + truncate table sys_job_log
  69 + </update>
  70 +
  71 + <insert id="insertJobLog" parameterType="SysJobLog">
  72 + insert into sys_job_log(
  73 + <if test="jobLogId != null and jobLogId != 0">job_log_id,</if>
  74 + <if test="jobName != null and jobName != ''">job_name,</if>
  75 + <if test="jobGroup != null and jobGroup != ''">job_group,</if>
  76 + <if test="invokeTarget != null and invokeTarget != ''">invoke_target,</if>
  77 + <if test="jobMessage != null and jobMessage != ''">job_message,</if>
  78 + <if test="status != null and status != ''">status,</if>
  79 + <if test="exceptionInfo != null and exceptionInfo != ''">exception_info,</if>
  80 + create_time
  81 + )values(
  82 + <if test="jobLogId != null and jobLogId != 0">#{jobLogId},</if>
  83 + <if test="jobName != null and jobName != ''">#{jobName},</if>
  84 + <if test="jobGroup != null and jobGroup != ''">#{jobGroup},</if>
  85 + <if test="invokeTarget != null and invokeTarget != ''">#{invokeTarget},</if>
  86 + <if test="jobMessage != null and jobMessage != ''">#{jobMessage},</if>
  87 + <if test="status != null and status != ''">#{status},</if>
  88 + <if test="exceptionInfo != null and exceptionInfo != ''">#{exceptionInfo},</if>
  89 + sysdate()
  90 + )
  91 + </insert>
  92 +
  93 +</mapper>
  1 +<?xml version="1.0" encoding="UTF-8" ?>
  2 +<!DOCTYPE mapper
  3 +PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
  4 +"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
  5 +<mapper namespace="com.ruoyi.quartz.mapper.SysJobMapper">
  6 +
  7 + <resultMap type="SysJob" id="SysJobResult">
  8 + <id property="jobId" column="job_id" />
  9 + <result property="jobName" column="job_name" />
  10 + <result property="jobGroup" column="job_group" />
  11 + <result property="invokeTarget" column="invoke_target" />
  12 + <result property="cronExpression" column="cron_expression" />
  13 + <result property="misfirePolicy" column="misfire_policy" />
  14 + <result property="concurrent" column="concurrent" />
  15 + <result property="status" column="status" />
  16 + <result property="createBy" column="create_by" />
  17 + <result property="createTime" column="create_time" />
  18 + <result property="updateBy" column="update_by" />
  19 + <result property="updateTime" column="update_time" />
  20 + <result property="remark" column="remark" />
  21 + </resultMap>
  22 +
  23 + <sql id="selectJobVo">
  24 + select job_id, job_name, job_group, invoke_target, cron_expression, misfire_policy, concurrent, status, create_by, create_time, remark
  25 + from sys_job
  26 + </sql>
  27 +
  28 + <select id="selectJobList" parameterType="SysJob" resultMap="SysJobResult">
  29 + <include refid="selectJobVo"/>
  30 + <where>
  31 + <if test="jobName != null and jobName != ''">
  32 + AND job_name like concat('%', #{jobName}, '%')
  33 + </if>
  34 + <if test="jobGroup != null and jobGroup != ''">
  35 + AND job_group = #{jobGroup}
  36 + </if>
  37 + <if test="status != null and status != ''">
  38 + AND status = #{status}
  39 + </if>
  40 + <if test="invokeTarget != null and invokeTarget != ''">
  41 + AND invoke_target like concat('%', #{invokeTarget}, '%')
  42 + </if>
  43 + </where>
  44 + </select>
  45 +
  46 + <select id="selectJobAll" resultMap="SysJobResult">
  47 + <include refid="selectJobVo"/>
  48 + </select>
  49 +
  50 + <select id="selectJobById" parameterType="Long" resultMap="SysJobResult">
  51 + <include refid="selectJobVo"/>
  52 + where job_id = #{jobId}
  53 + </select>
  54 +
  55 + <delete id="deleteJobById" parameterType="Long">
  56 + delete from sys_job where job_id = #{jobId}
  57 + </delete>
  58 +
  59 + <delete id="deleteJobByIds" parameterType="Long">
  60 + delete from sys_job where job_id in
  61 + <foreach collection="array" item="jobId" open="(" separator="," close=")">
  62 + #{jobId}
  63 + </foreach>
  64 + </delete>
  65 +
  66 + <update id="updateJob" parameterType="SysJob">
  67 + update sys_job
  68 + <set>
  69 + <if test="jobName != null and jobName != ''">job_name = #{jobName},</if>
  70 + <if test="jobGroup != null and jobGroup != ''">job_group = #{jobGroup},</if>
  71 + <if test="invokeTarget != null and invokeTarget != ''">invoke_target = #{invokeTarget},</if>
  72 + <if test="cronExpression != null and cronExpression != ''">cron_expression = #{cronExpression},</if>
  73 + <if test="misfirePolicy != null and misfirePolicy != ''">misfire_policy = #{misfirePolicy},</if>
  74 + <if test="concurrent != null and concurrent != ''">concurrent = #{concurrent},</if>
  75 + <if test="status !=null">status = #{status},</if>
  76 + <if test="remark != null and remark != ''">remark = #{remark},</if>
  77 + <if test="updateBy != null and updateBy != ''">update_by = #{updateBy},</if>
  78 + update_time = sysdate()
  79 + </set>
  80 + where job_id = #{jobId}
  81 + </update>
  82 +
  83 + <insert id="insertJob" parameterType="SysJob" useGeneratedKeys="true" keyProperty="jobId">
  84 + insert into sys_job(
  85 + <if test="jobId != null and jobId != 0">job_id,</if>
  86 + <if test="jobName != null and jobName != ''">job_name,</if>
  87 + <if test="jobGroup != null and jobGroup != ''">job_group,</if>
  88 + <if test="invokeTarget != null and invokeTarget != ''">invoke_target,</if>
  89 + <if test="cronExpression != null and cronExpression != ''">cron_expression,</if>
  90 + <if test="misfirePolicy != null and misfirePolicy != ''">misfire_policy,</if>
  91 + <if test="concurrent != null and concurrent != ''">concurrent,</if>
  92 + <if test="status != null and status != ''">status,</if>
  93 + <if test="remark != null and remark != ''">remark,</if>
  94 + <if test="createBy != null and createBy != ''">create_by,</if>
  95 + create_time
  96 + )values(
  97 + <if test="jobId != null and jobId != 0">#{jobId},</if>
  98 + <if test="jobName != null and jobName != ''">#{jobName},</if>
  99 + <if test="jobGroup != null and jobGroup != ''">#{jobGroup},</if>
  100 + <if test="invokeTarget != null and invokeTarget != ''">#{invokeTarget},</if>
  101 + <if test="cronExpression != null and cronExpression != ''">#{cronExpression},</if>
  102 + <if test="misfirePolicy != null and misfirePolicy != ''">#{misfirePolicy},</if>
  103 + <if test="concurrent != null and concurrent != ''">#{concurrent},</if>
  104 + <if test="status != null and status != ''">#{status},</if>
  105 + <if test="remark != null and remark != ''">#{remark},</if>
  106 + <if test="createBy != null and createBy != ''">#{createBy},</if>
  107 + sysdate()
  108 + )
  109 + </insert>
  110 +
  111 +</mapper>