diff --git a/doc/readme.md b/doc/readme.md index e4e9742..4f57d7f 100644 --- a/doc/readme.md +++ b/doc/readme.md @@ -1,4 +1,12 @@ -# 账号自动解锁 +# 增加微信支付回调 + +微信支付增加支付成功后系统回调通知 + +# 增加系统任务 + +quartz系统任务 + +# 账号自动解锁(已上线) 说明 @@ -10,8 +18,8 @@ 1. 一个账号可以有两个邀请码,一个邀请码用于其他账号关联,另一个邀请码是关联其他账号。 2. 邀请码的获取有两种方式: - 1. 由管理员直接生成 - 2. 自己发起申请,管理员审核通过后生成 + 1. 由管理员直接生成 + 2. 自己发起申请,管理员审核通过后生成 3. 生成邀请码时,必须设置金额和邀请码返利比例 变更的表 diff --git a/src/main/java/cn/com/tenlion/operator/controller/api/account/AccountController.java b/src/main/java/cn/com/tenlion/operator/controller/api/account/AccountController.java index 519144b..c05958f 100644 --- a/src/main/java/cn/com/tenlion/operator/controller/api/account/AccountController.java +++ b/src/main/java/cn/com/tenlion/operator/controller/api/account/AccountController.java @@ -5,8 +5,10 @@ import cn.com.tenlion.operator.enums.ReconciliationStatusEnum; import cn.com.tenlion.operator.enums.ThirdPartyEnum; import cn.com.tenlion.operator.pojo.dtos.accountbank.AccountBankDTO; import cn.com.tenlion.operator.pojo.dtos.accountrecharge.AccountRechargeDTO; +import cn.com.tenlion.operator.properties.SystemApiPathProperties; import cn.com.tenlion.operator.service.accountbank.IAccountBankService; import cn.com.tenlion.operator.service.accountrecharge.IAccountRechargeService; +import cn.com.tenlion.operator.service.sys.callback.SysCallbackService; import cn.com.tenlion.operator.util.UserUtil; import cn.com.tenlion.operator.util.pay.ALiPay; import cn.com.tenlion.operator.util.pay.PayResultDTO; @@ -48,15 +50,16 @@ public class AccountController extends DefaultBaseController { @Autowired private IAccountService accountService; - @Autowired private IUserBaseService iUserBaseService; - @Autowired private IAccountBankService iAccountBankService; - @Autowired private IAccountRechargeService iAccountRechargeService; + @Autowired + private SysCallbackService sysCallbackService; + @Autowired + private SystemApiPathProperties systemApiPathProperties; @ApiOperation(value = "支付宝支付成功后回调", notes = "支付宝支付成功后回调") @ApiResponses({@ApiResponse(code = 400, message = "请求失败", response = ErrorResult.class)}) @@ -93,9 +96,10 @@ public class AccountController extends DefaultBaseController { JSONObject selfData = JSON.parseObject(dto.getSelfData()); if (selfData.containsKey("type") && StringUtils.equals("PROJ_PKG", selfData.getString("type"))) { LOG.debug("通知付打包款成功"); - + sysCallbackService.save("项目打包付款成功", systemApiPathProperties.getCopyright() + "api/pay/pkg-pay-success/proj-id/" + selfData.getString("projId"), null); } } catch (Exception e) { + LOG.error(e.getMessage(), e); } } } diff --git a/src/main/java/cn/com/tenlion/operator/dao/sys/callback/ISysCallbackDao.java b/src/main/java/cn/com/tenlion/operator/dao/sys/callback/ISysCallbackDao.java new file mode 100644 index 0000000..2771b2d --- /dev/null +++ b/src/main/java/cn/com/tenlion/operator/dao/sys/callback/ISysCallbackDao.java @@ -0,0 +1,24 @@ +package cn.com.tenlion.operator.dao.sys.callback; + +import cn.com.tenlion.operator.pojo.pos.sys.callback.SysCallbackPO; +import org.springframework.stereotype.Repository; + +import java.util.List; +import java.util.Map; + +@Repository +public interface ISysCallbackDao { + + void save(Map params); + + void remove(Map params); + + void update(Map params); + + void updateStatus(Map params); + + SysCallbackPO getPO(Map params); + + List listPO(Map params); + +} diff --git a/src/main/java/cn/com/tenlion/operator/enums/SysCallbackStatusEnum.java b/src/main/java/cn/com/tenlion/operator/enums/SysCallbackStatusEnum.java new file mode 100644 index 0000000..584a384 --- /dev/null +++ b/src/main/java/cn/com/tenlion/operator/enums/SysCallbackStatusEnum.java @@ -0,0 +1,8 @@ +package cn.com.tenlion.operator.enums; + +public enum SysCallbackStatusEnum { + + SUCCESS, + UN_SUCCESS + +} diff --git a/src/main/java/cn/com/tenlion/operator/pojo/pos/sys/callback/SysCallbackPO.java b/src/main/java/cn/com/tenlion/operator/pojo/pos/sys/callback/SysCallbackPO.java new file mode 100644 index 0000000..3527e56 --- /dev/null +++ b/src/main/java/cn/com/tenlion/operator/pojo/pos/sys/callback/SysCallbackPO.java @@ -0,0 +1,106 @@ +package cn.com.tenlion.operator.pojo.pos.sys.callback; + +import cn.com.tenlion.operator.enums.SysCallbackStatusEnum; + +public class SysCallbackPO { + + private String callbackId; + private String name; + private String url; + private String jsonBody; + private SysCallbackStatusEnum status; + private Integer failCount; + private String gmtCreate; + private String creator; + private String gmtModified; + private String modifier; + private Integer isDelete; + + public String getCallbackId() { + return callbackId == null ? "" : callbackId.trim(); + } + + public void setCallbackId(String callbackId) { + this.callbackId = callbackId; + } + + public String getName() { + return name == null ? "" : name.trim(); + } + + public void setName(String name) { + this.name = name; + } + + public String getUrl() { + return url == null ? "" : url.trim(); + } + + public void setUrl(String url) { + this.url = url; + } + + public String getJsonBody() { + return jsonBody == null ? "" : jsonBody.trim(); + } + + public void setJsonBody(String jsonBody) { + this.jsonBody = jsonBody; + } + + public SysCallbackStatusEnum getStatus() { + return status; + } + + public void setStatus(SysCallbackStatusEnum status) { + this.status = status; + } + + public Integer getFailCount() { + return failCount == null ? 0 : failCount; + } + + public void setFailCount(Integer failCount) { + this.failCount = failCount; + } + + public String getGmtCreate() { + return gmtCreate == null ? "" : gmtCreate.trim(); + } + + public void setGmtCreate(String gmtCreate) { + this.gmtCreate = gmtCreate; + } + + public String getCreator() { + return creator == null ? "" : creator.trim(); + } + + public void setCreator(String creator) { + this.creator = creator; + } + + public String getGmtModified() { + return gmtModified == null ? "" : gmtModified.trim(); + } + + public void setGmtModified(String gmtModified) { + this.gmtModified = gmtModified; + } + + public String getModifier() { + return modifier == null ? "" : modifier.trim(); + } + + public void setModifier(String modifier) { + this.modifier = modifier; + } + + public Integer getIsDelete() { + return isDelete == null ? 0 : isDelete; + } + + public void setIsDelete(Integer isDelete) { + this.isDelete = isDelete; + } +} diff --git a/src/main/java/cn/com/tenlion/operator/service/sys/callback/SysCallbackService.java b/src/main/java/cn/com/tenlion/operator/service/sys/callback/SysCallbackService.java new file mode 100644 index 0000000..4078f47 --- /dev/null +++ b/src/main/java/cn/com/tenlion/operator/service/sys/callback/SysCallbackService.java @@ -0,0 +1,85 @@ +package cn.com.tenlion.operator.service.sys.callback; + +import cn.com.tenlion.operator.dao.sys.callback.ISysCallbackDao; +import cn.com.tenlion.operator.enums.SysCallbackStatusEnum; +import cn.com.tenlion.operator.pojo.pos.sys.callback.SysCallbackPO; +import cn.com.tenlion.operator.service.sys.task.SysTaskService; +import com.alibaba.fastjson.JSONObject; +import ink.wgink.common.base.DefaultBaseService; +import ink.wgink.exceptions.base.SystemException; +import ink.wgink.util.UUIDUtil; +import ink.wgink.util.map.HashMapUtil; +import ink.wgink.util.thread.CachedThreadPoolUtil; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.io.BufferedReader; +import java.io.InputStreamReader; +import java.net.HttpURLConnection; +import java.net.URL; +import java.util.List; +import java.util.Map; + +@Service +public class SysCallbackService extends DefaultBaseService { + + @Autowired + private ISysCallbackDao sysCallbackDao; + @Autowired + private SysTaskService sysTaskService; + + public void save(String name, String url, JSONObject jsonBody) { + String callbackId = UUIDUtil.getUUID(); + String jsonBodyString = jsonBody == null ? "" : jsonBody.toJSONString(); + Map params = getHashMap(3); + params.put("callbackId", callbackId); + params.put("name", name); + params.put("url", url); + params.put("jsonBody", jsonBodyString); + params.put("status", SysCallbackStatusEnum.UN_SUCCESS); + params.put("failCount", 0); + setSaveInfoByUserId(params, "1"); + sysCallbackDao.save(params); + sysTaskService.runCallbackTask(callbackId); + } + + public void remove(List callbackIds) { + Map params = getHashMap(2); + params.put("callbackIds", callbackIds); + setUpdateInfo(params); + sysCallbackDao.remove(params); + } + + public void update(String callbackId, SysCallbackPO sysCallbackPO) { + Map params = HashMapUtil.beanToMap(sysCallbackPO); + setUpdateInfo(params); + params.put("callbackId", callbackId); + sysCallbackDao.update(params); + } + + public void update(String callbackId, SysCallbackStatusEnum status, int failCount) { + Map params = getHashMap(3); + params.put("callbackId", callbackId); + params.put("status", status); + params.put("failCount", failCount); + setUpdateInfoByUserId(params, "1"); + sysCallbackDao.update(params); + } + + public SysCallbackPO getPO(String callbackId) { + Map params = getHashMap(2); + params.put("callbackId", callbackId); + return sysCallbackDao.getPO(params); + } + + public List listPO(Map params) { + return sysCallbackDao.listPO(params); + } + + public List listPOByStatus(SysCallbackStatusEnum status) { + Map params = getHashMap(2); + params.put("status", status.name()); + return sysCallbackDao.listPO(params); + } + +} diff --git a/src/main/java/cn/com/tenlion/operator/service/sys/callback/SysCallbackTask.java b/src/main/java/cn/com/tenlion/operator/service/sys/callback/SysCallbackTask.java new file mode 100644 index 0000000..4b4f468 --- /dev/null +++ b/src/main/java/cn/com/tenlion/operator/service/sys/callback/SysCallbackTask.java @@ -0,0 +1,66 @@ +package cn.com.tenlion.operator.service.sys.callback; + +import cn.com.tenlion.operator.enums.SysCallbackStatusEnum; +import cn.com.tenlion.operator.pojo.pos.sys.callback.SysCallbackPO; +import cn.com.tenlion.operator.service.sys.task.SysTaskService; +import com.alibaba.fastjson.JSONObject; +import ink.wgink.exceptions.base.SystemException; +import org.quartz.Job; +import org.quartz.JobDataMap; +import org.quartz.JobExecutionContext; +import org.quartz.JobExecutionException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.BufferedReader; +import java.io.InputStreamReader; +import java.net.HttpURLConnection; +import java.net.URL; + +public class SysCallbackTask implements Job { + + private static final Logger LOG = LoggerFactory.getLogger(SysCallbackTask.class); + + @Override + public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException { + JobDataMap mergedJobDataMap = jobExecutionContext.getMergedJobDataMap(); + SysCallbackService sysCallbackService = (SysCallbackService) mergedJobDataMap.get("sysCallbackService"); + SysTaskService sysTaskService = (SysTaskService) mergedJobDataMap.get("sysTaskService"); + SysCallbackPO sysCallbackPO = (SysCallbackPO) mergedJobDataMap.get("sysCallbackPO"); + + try { + URL url = new URL(sysCallbackPO.getUrl()); + LOG.debug("回调地址:{}", url); + HttpURLConnection httpURLConnection = (HttpURLConnection) url.openConnection(); + httpURLConnection.setRequestMethod("POST"); + httpURLConnection.setDoOutput(true); + httpURLConnection.setDoInput(true); + httpURLConnection.setConnectTimeout(3000); + httpURLConnection.connect(); + int responseCode = httpURLConnection.getResponseCode(); + if (responseCode == HttpURLConnection.HTTP_OK) { + BufferedReader reader = new BufferedReader(new InputStreamReader(httpURLConnection.getInputStream())); + String line; + StringBuilder resultSB = new StringBuilder(); + while ((line = reader.readLine()) != null) { // 循环从流中读取 + resultSB.append(line); + } + reader.close(); + httpURLConnection.disconnect(); + if (!resultSB.toString().startsWith("{") && !resultSB.toString().endsWith("}")) { + throw new SystemException("返回结果不是JSON对象"); + } + LOG.debug("回调成功"); + sysCallbackService.update(sysCallbackPO.getCallbackId(), SysCallbackStatusEnum.SUCCESS, sysCallbackPO.getFailCount()); + } else { + httpURLConnection.disconnect(); + throw new SystemException("回调状态码不是200"); + } + } catch (Exception e) { + LOG.error("回调异常: {}", e.getMessage(), e); + sysCallbackService.update(sysCallbackPO.getCallbackId(), SysCallbackStatusEnum.UN_SUCCESS, sysCallbackPO.getFailCount() + 1); + sysTaskService.runCallbackTask(sysCallbackPO.getCallbackId()); + } + } + +} diff --git a/src/main/java/cn/com/tenlion/operator/service/sys/task/SysTaskService.java b/src/main/java/cn/com/tenlion/operator/service/sys/task/SysTaskService.java new file mode 100644 index 0000000..e7b8512 --- /dev/null +++ b/src/main/java/cn/com/tenlion/operator/service/sys/task/SysTaskService.java @@ -0,0 +1,78 @@ +package cn.com.tenlion.operator.service.sys.task; + +import cn.com.tenlion.operator.pojo.pos.sys.callback.SysCallbackPO; +import cn.com.tenlion.operator.service.sys.callback.SysCallbackService; +import cn.com.tenlion.operator.service.sys.callback.SysCallbackTask; +import com.google.gson.LongSerializationPolicy; +import ink.wgink.common.base.DefaultBaseService; +import ink.wgink.util.UUIDUtil; +import org.quartz.*; +import org.quartz.impl.StdSchedulerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Lazy; +import org.springframework.stereotype.Service; + +import java.util.Date; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.TimeUnit; + +@Service +public class SysTaskService extends DefaultBaseService { + + private static Scheduler scheduler = null; + @Autowired + @Lazy + private SysCallbackService sysCallbackService; + + public SysTaskService() { + try { + scheduler = StdSchedulerFactory.getDefaultScheduler(); + } catch (SchedulerException e) { + throw new RuntimeException(e); + } + } + + public void runCallbackTask(String callbackId) { + SysCallbackPO sysCallbackPO = sysCallbackService.getPO(callbackId); + if (sysCallbackPO == null) { + LOG.debug("回调任务不存在"); + return; + } + JobDataMap jobDataMap = new JobDataMap(); + jobDataMap.put("sysCallbackPO", sysCallbackPO); + jobDataMap.put("sysCallbackService", sysCallbackService); + jobDataMap.put("sysTaskService", this); + + JobDetail jobDetail = JobBuilder.newJob(SysCallbackTask.class).usingJobData(jobDataMap).build(); + try { + // 1、5、10、30、60、120、300 + long waitTime; + if (sysCallbackPO.getFailCount() == 0) { + waitTime = 0; + } else if (sysCallbackPO.getFailCount() == 1) { + waitTime = 1; + } else if (sysCallbackPO.getFailCount() == 2) { + waitTime = 5; + } else if (sysCallbackPO.getFailCount() == 3) { + waitTime = 10; + } else if (sysCallbackPO.getFailCount() == 4) { + waitTime = 30; + } else if (sysCallbackPO.getFailCount() == 5) { + waitTime = 60; + } else if (sysCallbackPO.getFailCount() == 6) { + waitTime = 120; + } else { + waitTime = 300; + } + Date startDate = new Date(System.currentTimeMillis() + waitTime * 1000); + TriggerKey triggerKey = new TriggerKey(UUIDUtil.getUUID(), "SYS_CALLBACK_TASK"); + Trigger trigger = TriggerBuilder.newTrigger().withIdentity(triggerKey).startAt(startDate).build(); + scheduler.scheduleJob(jobDetail, trigger); + scheduler.start(); + } catch (Exception e) { + LOG.error(e.getMessage(), e); + } + } + +} diff --git a/src/main/java/cn/com/tenlion/operator/startup/SystemStart.java b/src/main/java/cn/com/tenlion/operator/startup/SystemStart.java new file mode 100644 index 0000000..98dec99 --- /dev/null +++ b/src/main/java/cn/com/tenlion/operator/startup/SystemStart.java @@ -0,0 +1,32 @@ +package cn.com.tenlion.operator.startup; + +import cn.com.tenlion.operator.enums.SysCallbackStatusEnum; +import cn.com.tenlion.operator.pojo.pos.sys.callback.SysCallbackPO; +import cn.com.tenlion.operator.service.sys.callback.SysCallbackService; +import cn.com.tenlion.operator.service.sys.task.SysTaskService; +import ink.wgink.interfaces.start.IApplicationStart; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.util.List; + +@Component +public class SystemStart implements IApplicationStart { + + private static final Logger LOG = LoggerFactory.getLogger(SystemStart.class); + @Autowired + private SysCallbackService sysCallbackService; + @Autowired + private SysTaskService sysTaskService; + + @Override + public void run() throws Exception { + List sysCallbackPOS = sysCallbackService.listPOByStatus(SysCallbackStatusEnum.UN_SUCCESS); + for (SysCallbackPO sysCallbackPO : sysCallbackPOS) { + sysTaskService.runCallbackTask(sysCallbackPO.getCallbackId()); + } + } + +} diff --git a/src/main/resources/application-dev.yml b/src/main/resources/application-dev.yml index 3166a32..296a30d 100644 --- a/src/main/resources/application-dev.yml +++ b/src/main/resources/application-dev.yml @@ -175,7 +175,7 @@ pay: # 单笔最大金额 max-money: 20000 # 单笔最小金额 - min-money: 0.1 + min-money: 0.01 ali-pay: # 应用ID # 西藏 app-id: 2021004137636507 diff --git a/src/main/resources/mybatis/mapper/sys/callback/sys-callback-mapper.xml b/src/main/resources/mybatis/mapper/sys/callback/sys-callback-mapper.xml new file mode 100644 index 0000000..6964c5d --- /dev/null +++ b/src/main/resources/mybatis/mapper/sys/callback/sys-callback-mapper.xml @@ -0,0 +1,127 @@ + + + + + + + + + + + + + + + + + + + + INSERT INTO sys_callback (callback_id, name, url, json_body, status, fail_count, gmt_create, creator, gmt_modified, modifier, is_delete) + VALUES (#{callbackId}, #{name}, #{url}, #{jsonBody}, #{status}, #{failCount}, #{gmtCreate}, #{creator}, #{gmtModified}, #{modifier}, #{isDelete}) + + + + UPDATE sys_callback + SET + is_delete = #{isDelete}, + gmt_modified = #{gmtModified}, + modifier = #{modifier} + WHERE + + callback_id IN + + #{callbackId} + + + + + + UPDATE sys_callback + SET + + name = #{name}, + + + url = #{url}, + + + json_body = #{jsonBody}, + + + status = #{status}, + + + fail_count = #{failCount}, + + gmt_modified = #{gmtModified}, + modifier = #{modifier} + WHERE + callback_id = #{callbackId} + + + + UPDATE sys_callback + SET + + fail_count = #{failCount}, + + status = #{status}, + gmt_modified = #{gmtModified}, + modifier = #{modifier} + WHERE + callback_id = #{callbackId} + + + + + + + \ No newline at end of file diff --git a/src/main/resources/quartz.properties b/src/main/resources/quartz.properties new file mode 100644 index 0000000..0a59ed7 --- /dev/null +++ b/src/main/resources/quartz.properties @@ -0,0 +1,3 @@ +org.quartz.scheduler.instanceName = MyScheduler +org.quartz.threadPool.threadCount = 10 +org.quartz.jobStore.class = org.quartz.simpl.RAMJobStore \ No newline at end of file