diff --git a/Cpop-Core/src/main/java/com/cpop/core/utils/QuartzUtils.java b/Cpop-Core/src/main/java/com/cpop/core/utils/QuartzUtils.java index 7f310b4..4ce50f0 100644 --- a/Cpop-Core/src/main/java/com/cpop/core/utils/QuartzUtils.java +++ b/Cpop-Core/src/main/java/com/cpop/core/utils/QuartzUtils.java @@ -1,11 +1,13 @@ package com.cpop.core.utils; +import com.cpop.core.base.exception.UtilException; import org.quartz.*; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import java.text.ParseException; import java.time.LocalDate; +import java.time.LocalDateTime; import java.util.Calendar; import java.util.Date; @@ -30,7 +32,11 @@ public class QuartzUtils { */ public String getJobInfo(String name, String group) throws SchedulerException { TriggerKey triggerKey = new TriggerKey(name, group); - CronTrigger cronTrigger = (CronTrigger) scheduler.getTrigger(triggerKey); + Trigger trigger = scheduler.getTrigger(triggerKey); + if (trigger == null) { + throw new UtilException("获取定时任务失败"); + } + CronTrigger cronTrigger = (CronTrigger) trigger; return String.format("time:%s,state:%s", cronTrigger.getCronExpression(), scheduler.getTriggerState(triggerKey).name()); } @@ -47,15 +53,20 @@ public class QuartzUtils { public boolean modifyJob(String name, String group, String time) throws SchedulerException { Date date = null; TriggerKey triggerKey = new TriggerKey(name, group); - CronTrigger cronTrigger = (CronTrigger) scheduler.getTrigger(triggerKey); - String oldTime = cronTrigger.getCronExpression(); - if (!oldTime.equalsIgnoreCase(time)) { - CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(time); - CronTrigger trigger = TriggerBuilder.newTrigger().withIdentity(name, group) - .withSchedule(cronScheduleBuilder).build(); - date = scheduler.rescheduleJob(triggerKey, trigger); + Trigger triggerResult = scheduler.getTrigger(triggerKey); + if (triggerResult != null) { + CronTrigger cronTrigger = (CronTrigger) triggerResult; + String oldTime = cronTrigger.getCronExpression(); + if (!oldTime.equalsIgnoreCase(time)) { + CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(time); + CronTrigger trigger = TriggerBuilder.newTrigger().withIdentity(name, group) + .withSchedule(cronScheduleBuilder).build(); + date = scheduler.rescheduleJob(triggerKey, trigger); + } + return date != null; + } else { + return false; } - return date != null; } /** @@ -156,4 +167,17 @@ public class QuartzUtils { "? " + year; } + /** + * 获取任务执行时间 + * @author DB + * @since 2024/2/22 + * @param name + * @param group + * @return LocalDateTime + */ + public LocalDateTime getJobExecuteTime(String name, String group) throws SchedulerException { + String jobInfo = getJobInfo(name, group); + String[] split = jobInfo.split(",")[0].replaceAll("time:", "").split(" "); + return LocalDateTime.of(Integer.parseInt(split[6]), Integer.parseInt(split[4]), Integer.parseInt(split[3]), Integer.parseInt(split[2]), Integer.parseInt(split[1]), Integer.parseInt(split[0])); + } } diff --git a/Cpop-Jambox/src/main/java/com/cpop/jambox/business/controller/callback/CallBackEasyLearnController.java b/Cpop-Jambox/src/main/java/com/cpop/jambox/business/controller/callback/CallBackEasyLearnController.java index 88ff134..cdff91e 100644 --- a/Cpop-Jambox/src/main/java/com/cpop/jambox/business/controller/callback/CallBackEasyLearnController.java +++ b/Cpop-Jambox/src/main/java/com/cpop/jambox/business/controller/callback/CallBackEasyLearnController.java @@ -10,6 +10,9 @@ import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + /** * @author DB * @version 1.0.0 @@ -74,4 +77,14 @@ public class CallBackEasyLearnController { easyLearnOrderService.unionPayResult(xmlData); return WxPayNotifyResponse.success("成功"); } + + /** + * 微信支付联合支付普通订单通知 + * @return java.lang.String + */ + @ApiOperation("微信支付联合支付普通订单通知") + @PostMapping("/notify/normalUnionPay") + public String normalUnionPay(HttpServletRequest request, HttpServletResponse response){ + return easyLearnOrderService.normalUnionPay(request,response); + } } diff --git a/Cpop-Jambox/src/main/java/com/cpop/jambox/business/service/EasyLearnOrderService.java b/Cpop-Jambox/src/main/java/com/cpop/jambox/business/service/EasyLearnOrderService.java index e1bc19e..3ec128e 100644 --- a/Cpop-Jambox/src/main/java/com/cpop/jambox/business/service/EasyLearnOrderService.java +++ b/Cpop-Jambox/src/main/java/com/cpop/jambox/business/service/EasyLearnOrderService.java @@ -9,6 +9,8 @@ import com.github.binarywang.wxpay.bean.payscore.WxPartnerPayScoreUserSignPlanRe import com.mybatisflex.core.paginate.Page; import com.mybatisflex.core.service.IService; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; import java.util.List; /** @@ -125,4 +127,12 @@ public interface EasyLearnOrderService extends IService { * @param phone 手机号 */ void sendEcppMsmCode(String phone); + + /** + * 微信支付联合支付普通订单通知 + * @author DB + * @since 2024/2/22 + * @return String + */ + String normalUnionPay(HttpServletRequest request, HttpServletResponse response); } diff --git a/Cpop-Jambox/src/main/java/com/cpop/jambox/business/service/impl/EasyLearnOrderServiceImpl.java b/Cpop-Jambox/src/main/java/com/cpop/jambox/business/service/impl/EasyLearnOrderServiceImpl.java index 021f7b8..34d4af6 100644 --- a/Cpop-Jambox/src/main/java/com/cpop/jambox/business/service/impl/EasyLearnOrderServiceImpl.java +++ b/Cpop-Jambox/src/main/java/com/cpop/jambox/business/service/impl/EasyLearnOrderServiceImpl.java @@ -29,6 +29,8 @@ import com.cpop.pay.framewok.core.request.ecpp.EcppSmsCodeRequest; import com.cpop.pay.framewok.core.response.ecpp.EcppSmsCodeResponse; import com.cpop.pay.framewok.handler.ecpp.EcppHandler; import com.cpop.pay.framewok.handler.wxPay.WxPayHandler; +import com.cpop.pay.framewok.handler.wxPay.WxPayNormalHandler; +import com.cpop.pay.framewok.handler.wxPay.WxUtils; import com.cpop.pay.framewok.task.WxPayAsyncTask; import com.cpop.system.business.entity.Store; import com.cpop.system.business.entity.WxPayScore; @@ -62,12 +64,16 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.web.client.RestTemplate; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.math.BigDecimal; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.util.ArrayList; import java.util.List; +import java.util.Map; +import java.util.TreeMap; import java.util.concurrent.locks.Lock; import static com.cpop.jambox.business.entity.table.BrandExtendTableDef.BRAND_EXTEND; @@ -529,6 +535,16 @@ public class EasyLearnOrderServiceImpl extends ServiceImpl= Math.ceil(1 / easyLearnOrder.getRate())) { + wxPayService = wxPayHandler.getWxPayService(null, wxPayHandler.getSubMchId(easyLearnOrder.getBrandId(), easyLearnOrder.getStoreId()), payNotifyUrl); + if (StringUtils.isBlank(wxPayService.getConfig().getSubMchId())){ + if (StringUtils.equals("prod", SpringUtils.getActiveProfile())) { + //联合支付 + payNotifyUrl = SpringUtils.getBean(WxPayConfiguration.class).getProperties().getEasyLearnUnionPayNormalNotifyUrl(); + } else if (StringUtils.equals("test", SpringUtils.getActiveProfile())){ + payNotifyUrl = UNION_PAY_TEST_NORMAL_NOTIFY_URL; + } else { + payNotifyUrl = UNION_PAY_DEV_NORMAL_NOTIFY_URL; + } + return SpringUtils.getBean(WxPayNormalHandler.class).createOrder(easyLearnOrder.getId(),bo.getOpenId(), easyLearnOrder.getTotalPayAmount(), "联合支付", IpUtils.getHostIp(), payNotifyUrl); + } else { + WxPayUnifiedOrderRequest orderRequest = new WxPayUnifiedOrderRequest(); //需要分账 - orderRequest.setProfitSharing("Y"); + if (easyLearnOrder.getRate() != null && easyLearnOrder.getTotalAmount().scaleByPowerOfTen(2).intValue() >= Math.ceil(1 / easyLearnOrder.getRate())) { + //需要分账 + orderRequest.setProfitSharing("Y"); + } + orderRequest.setSpbillCreateIp(IpUtils.getHostIp()) + .setOpenid(bo.getOpenId()) + //商品描述 + .setBody("联合支付") + .setOutTradeNo(easyLearnOrder.getId()) + //元转分 + .setTotalFee(easyLearnOrder.getTotalAmount().scaleByPowerOfTen(2).intValue()) + .setTradeType("JSAPI"); + return wxPayService.createOrder(orderRequest); } - orderRequest.setSpbillCreateIp(IpUtils.getHostIp()) - .setOpenid(bo.getOpenId()) - //商品描述 - .setBody("联合支付") - .setOutTradeNo(easyLearnOrder.getId()) - //元转分 - .setTotalFee(easyLearnOrder.getTotalAmount().scaleByPowerOfTen(2).intValue()) - .setTradeType("JSAPI"); - return wxPayService.createOrder(orderRequest); } catch (Exception e) { throw new ServiceException(e.getMessage()); } finally { @@ -1005,4 +1034,60 @@ public class EasyLearnOrderServiceImpl extends ServiceImpl map = null; + Response okResponse = null; + try { + map = wxUtils.transferXmlToMap(wxUtils.readRequest(request)); + // 转换为有序 map,判断签名是否正确 + boolean isSignSuccess = wxUtils.checkSign(new TreeMap(map), "JamBox20220329174000000000000002"); + if (isSignSuccess) { + // 签名校验成功,说明是微信服务器发出的数据 + String orderId = map.get("out_trade_no"); + //获取订单信息 + EasyLearnOrder easyLearnOrder = this.getById(orderId); + JSONObject jsonObject = new JSONObject(); + EasyLearnPayPayEnum easyLearnPayPayEnum = EasyLearnPayPayEnum.getEasyLearnPayPayEnum(easyLearnOrder.getOrderType()); + jsonObject.put("wxOrderId", easyLearnOrder.getId()); + jsonObject.put("unionPayOutOrderId", easyLearnOrder.getUnionPayOutOrderId()); + jsonObject.put("easyLearnUnionPay", easyLearnPayPayEnum.toString()); + String url; + if (StringUtils.equals("prod", SpringUtils.getActiveProfile())) { + url = UNION_PAY_PROD_JAMBOX_NOTIFY_URL; + } else if (StringUtils.equals("test", SpringUtils.getActiveProfile())) { + url = UNION_PAY_TEST_JAMBOX_NOTIFY_URL; + } else { + url = UNION_PAY_DEV_JAMBOX_NOTIFY_URL; + } + //通知晖哥 + okResponse = HttpUtils.sendOkHttpPost(url, jsonObject.toJSONString()); + //更新订单 + this.updateChain().set(EASY_LEARN_ORDER.OUT_ORDER_NO, map.get("transaction_id")) + .set(EASY_LEARN_ORDER.ORDER_STATUS, 1) + .where(EASY_LEARN_ORDER.ID.eq(orderId)).update(); + return wxPayNormalHandler.success(); + } else { + // 签名校验失败(可能不是微信服务器发出的数据) + return wxPayNormalHandler.fail(); + } + } catch (Exception e) { + return wxPayNormalHandler.fail(); + } finally { + assert okResponse != null; + okResponse.close(); + } + } } diff --git a/Cpop-Jambox/src/main/resources/application-jambox.yml b/Cpop-Jambox/src/main/resources/application-jambox.yml index 3b378ac..e2c59a4 100644 --- a/Cpop-Jambox/src/main/resources/application-jambox.yml +++ b/Cpop-Jambox/src/main/resources/application-jambox.yml @@ -3,4 +3,6 @@ wx: #支付通知地址 easy-learn-once-pay-notify-url: https://oamapi.cpopsz.com/Cpop-Oam/callback/easyLearn/notify/oncePay #放心学联合支付 - easy-learn-union-pay-notify-url: https://oamapi.cpopsz.com/Cpop-Oam/callback/easyLearn/notify/unionPay \ No newline at end of file + easy-learn-union-pay-notify-url: https://oamapi.cpopsz.com/Cpop-Oam/callback/easyLearn/notify/unionPay + #放心学普通支付 + easy-learn-union-pay-normal-notify-url: https://oamapi.cpopsz.com/Cpop-Oam/callback/easyLearn/notify/normalUnionPay \ No newline at end of file diff --git a/Cpop-Mall/Cpop-Mall-Web/src/test/java/com/cpop/mall/web/CpopWxPayTests.java b/Cpop-Mall/Cpop-Mall-Web/src/test/java/com/cpop/mall/web/CpopWxPayTests.java index 4342408..1d16536 100644 --- a/Cpop-Mall/Cpop-Mall-Web/src/test/java/com/cpop/mall/web/CpopWxPayTests.java +++ b/Cpop-Mall/Cpop-Mall-Web/src/test/java/com/cpop/mall/web/CpopWxPayTests.java @@ -137,7 +137,7 @@ public class CpopWxPayTests { public void getOrderInfo() throws WxPayException { WxPayConfig config = wxPayService.getConfig(); config.setSubMchId("1661323640"); - WxPayOrderQueryResult wxPayOrderQueryResult = wxPayService.queryOrder("4200002096202402199975156147", null); + WxPayOrderQueryResult wxPayOrderQueryResult = wxPayService.queryOrder("4200002123202312279635916930", null); System.out.println(JSONObject.toJSONString(wxPayOrderQueryResult)); } @@ -150,7 +150,7 @@ public class CpopWxPayTests { @Test public void getSharingInfo() throws WxPayException { ProfitSharingQueryRequest profitSharingQueryRequest = new ProfitSharingQueryRequest(); - profitSharingQueryRequest.setOutOrderNo("1736629608413073417").setTransactionId("4200002111202402019716818254"); + profitSharingQueryRequest.setOutOrderNo("1736629608413073417").setTransactionId("4200002123202312279635916930"); profitSharingQueryRequest.setSubMchId("1661323640"); ProfitSharingQueryResult result = wxPayService.getProfitSharingService().profitSharingQuery(profitSharingQueryRequest); System.out.println(result.getResultCode()); diff --git a/Cpop-Oam/Cpop-Oam-Web/src/test/java/com/cpop/oam/web/CpopEasyLearnTest.java b/Cpop-Oam/Cpop-Oam-Web/src/test/java/com/cpop/oam/web/CpopEasyLearnTest.java index 3c5daa6..be32ecb 100644 --- a/Cpop-Oam/Cpop-Oam-Web/src/test/java/com/cpop/oam/web/CpopEasyLearnTest.java +++ b/Cpop-Oam/Cpop-Oam-Web/src/test/java/com/cpop/oam/web/CpopEasyLearnTest.java @@ -328,8 +328,8 @@ public class CpopEasyLearnTest { @Test public void queryOrder() throws WxPayException { - WxPayService wxPayService = wxPayHandler.getWxPayService(null, "1661323640"); - WxPayOrderQueryResult wxPayOrderQueryResult = wxPayService.queryOrder("4200002111202401103536519415",null); + WxPayService wxPayService = wxPayHandler.getWxPayService(null, "1666928264"); + WxPayOrderQueryResult wxPayOrderQueryResult = wxPayService.queryOrder("4200002130202402224927801847",null); System.out.println(JSONObject.toJSONString(wxPayOrderQueryResult)); } diff --git a/Cpop-Oam/Cpop-Oam-Web/src/test/java/com/cpop/oam/web/CpopQrtzTest.java b/Cpop-Oam/Cpop-Oam-Web/src/test/java/com/cpop/oam/web/CpopQrtzTest.java index 2a3bd61..a3ac12d 100644 --- a/Cpop-Oam/Cpop-Oam-Web/src/test/java/com/cpop/oam/web/CpopQrtzTest.java +++ b/Cpop-Oam/Cpop-Oam-Web/src/test/java/com/cpop/oam/web/CpopQrtzTest.java @@ -5,11 +5,16 @@ import com.cpop.core.utils.SpringUtils; import com.cpop.oam.business.entity.Task; import com.cpop.oam.business.service.TaskService; import com.cpop.oam.business.service.TaskStaffGroupService; +import com.cpop.oam.framework.enums.QuartzEnums; import org.junit.jupiter.api.Test; import org.quartz.SchedulerException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; +import java.lang.reflect.Array; +import java.time.LocalDateTime; +import java.util.Arrays; + import static com.cpop.oam.business.entity.table.TaskStaffGroupTableDef.TASK_STAFF_GROUP; /** @@ -63,4 +68,10 @@ public class CpopQrtzTest { .update(); } + @Test + public void getJobInfo() throws SchedulerException { + LocalDateTime jobExecuteTime = quartzUtils.getJobExecuteTime(QuartzEnums.WORK_ORDER_OVERTIME_TASK.getName() + "118089232185462784", QuartzEnums.WORK_ORDER_OVERTIME_TASK.getGroup()); + System.out.println(jobExecuteTime); + } + } diff --git a/Cpop-Oam/src/main/java/com/cpop/oam/business/bo/AgreeOrRejectEnforceFinishBo.java b/Cpop-Oam/src/main/java/com/cpop/oam/business/bo/AgreeOrRejectEnforceFinishBo.java new file mode 100644 index 0000000..072a4db --- /dev/null +++ b/Cpop-Oam/src/main/java/com/cpop/oam/business/bo/AgreeOrRejectEnforceFinishBo.java @@ -0,0 +1,29 @@ +package com.cpop.oam.business.bo; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.experimental.Accessors; + +/** + * @author DB + * @version 1.0.0 + * @since 2024-02-21 18:27 + */ +@Data +@Accessors(chain = true) +@ApiModel(value = "AgreeOrRejectEnforceFinishBo对象") +public class AgreeOrRejectEnforceFinishBo { + + /** + * 工单id + */ + @ApiModelProperty(value = "工单id") + private String id; + + /** + * 同意或拒绝 + */ + @ApiModelProperty(value = "同意或拒绝") + private Boolean agreeOrReject; +} diff --git a/Cpop-Oam/src/main/java/com/cpop/oam/business/controller/backstage/BackstageClueController.java b/Cpop-Oam/src/main/java/com/cpop/oam/business/controller/backstage/BackstageClueController.java index beed5b9..8413089 100644 --- a/Cpop-Oam/src/main/java/com/cpop/oam/business/controller/backstage/BackstageClueController.java +++ b/Cpop-Oam/src/main/java/com/cpop/oam/business/controller/backstage/BackstageClueController.java @@ -53,8 +53,9 @@ public class BackstageClueController { @ApiParam(value = "品牌") @RequestParam(value = "brandId", required = false) String brandId, @ApiParam(value = "校区") @RequestParam(value = "storeId", required = false) String storeId, @ApiParam(value = "负责人或手机号") @RequestParam(value = "chargeOrPhone", required = false) String chargeOrPhone, - @ApiParam(value = "员工id") @RequestParam(value = "staffId", required = false) String staffId) { - Page pageVo = clueService.getPersonCluePage(clueStatus, city, brandId, storeId, chargeOrPhone, staffId); + @ApiParam(value = "员工id") @RequestParam(value = "staffId", required = false) String staffId, + @ApiParam(value = "签约月份") @RequestParam(value = "signMonth", required = false) String signMonth) { + Page pageVo = clueService.getPersonCluePage(clueStatus, city, brandId, storeId, chargeOrPhone, staffId, signMonth); return R.ok(pageVo); } @@ -98,11 +99,12 @@ public class BackstageClueController { @GetMapping("/getClueFollowUpRecord") public R> getClueFollowUpRecord(@ApiParam(value = "线索id") @RequestParam(value = "clueId") String clueId) { List vos = clueRecordService.listAs(QueryWrapper.create() - .select(CLUE_RECORD.CREATE_TIME,CLUE_RECORD.RECORD_CONTENT,CLUE_RECORD.RECORD_FILE_URL) + .select(CLUE_RECORD.CREATE_TIME, CLUE_RECORD.RECORD_CONTENT, CLUE_RECORD.RECORD_FILE_URL) .select(STAFF.NAME.as(ClueFollowUpRecordVo::getStaffName)) .leftJoin(STAFF).on(STAFF.ID.eq(CLUE_RECORD.RECORD_STAFF_ID)) .where(CLUE_RECORD.RECORD_TYPE.eq(2) - .and(CLUE_RECORD.CLUE_ID.eq(clueId))), + .and(CLUE_RECORD.CLUE_ID.eq(clueId))) + .orderBy(CLUE_RECORD.CREATE_TIME.desc()), ClueFollowUpRecordVo.class); return R.ok(vos); } @@ -118,8 +120,9 @@ public class BackstageClueController { @GetMapping("/getClueRecordList") public R> getClueRecordList(@ApiParam(value = "线索id") @RequestParam(value = "clueId") String clueId) { List vos = clueRecordService.listAs(QueryWrapper.create() - .select(CLUE_RECORD.CREATE_TIME, CLUE_RECORD.RECORD_CONTENT) - .and(CLUE_RECORD.CLUE_ID.eq(clueId)), + .select(CLUE_RECORD.CREATE_TIME, CLUE_RECORD.RECORD_CONTENT,CLUE_RECORD.RECORD_TYPE) + .and(CLUE_RECORD.CLUE_ID.eq(clueId)) + .orderBy(CLUE_RECORD.CREATE_TIME.desc()), ClueRecordVo.class); return R.ok(vos); } diff --git a/Cpop-Oam/src/main/java/com/cpop/oam/business/controller/backstage/BackstageTaskWorkOrderController.java b/Cpop-Oam/src/main/java/com/cpop/oam/business/controller/backstage/BackstageTaskWorkOrderController.java index 7979864..2f063ac 100644 --- a/Cpop-Oam/src/main/java/com/cpop/oam/business/controller/backstage/BackstageTaskWorkOrderController.java +++ b/Cpop-Oam/src/main/java/com/cpop/oam/business/controller/backstage/BackstageTaskWorkOrderController.java @@ -2,6 +2,7 @@ package com.cpop.oam.business.controller.backstage; import com.cpop.core.base.R; import com.cpop.jambox.business.vo.BrandListVo; +import com.cpop.oam.business.bo.AgreeOrRejectEnforceFinishBo; import com.cpop.oam.business.bo.PauseWorkOrderBo; import com.cpop.oam.business.bo.TaskWorkOrderBo; import com.cpop.oam.business.bo.TaskWorkOrderRecordBo; @@ -217,4 +218,18 @@ public class BackstageTaskWorkOrderController { Page page = taskWorkOrderService.getWorkOrderStatPage(staffId, startDate, endDate); return R.ok(page); } + + /** + * 同意与拒绝强制办结 + * @author DB + * @since 2024/2/21 + * @param bo 请求参数 + * @return R + */ + @ApiOperation("工单模块-工单提交-同意与拒绝强制办结") + @PutMapping("/agreeOrRejectEnforceFinish") + public R agreeOrRejectEnforceFinish(@RequestBody @Validated AgreeOrRejectEnforceFinishBo bo) { + taskWorkOrderService.agreeOrRejectEnforceFinish(bo); + return R.ok(); + } } diff --git a/Cpop-Oam/src/main/java/com/cpop/oam/business/entity/Clue.java b/Cpop-Oam/src/main/java/com/cpop/oam/business/entity/Clue.java index afe420a..d94af86 100644 --- a/Cpop-Oam/src/main/java/com/cpop/oam/business/entity/Clue.java +++ b/Cpop-Oam/src/main/java/com/cpop/oam/business/entity/Clue.java @@ -3,6 +3,7 @@ package com.cpop.oam.business.entity; import com.cpop.core.base.entity.BaseEntity; import com.cpop.core.base.entity.BaseInsertListener; import com.cpop.core.base.entity.BaseUpdateListener; +import com.fasterxml.jackson.annotation.JsonFormat; import com.mybatisflex.annotation.Column; import com.mybatisflex.annotation.Id; import com.mybatisflex.annotation.Table; @@ -74,6 +75,16 @@ public class Clue extends BaseEntity implements Serializable { */ private Integer clueStatus; + /** + * 最后接收时间 + */ + private LocalDateTime lastReceiptTime; + + /** + * 最后接收员工id + */ + private String lastReceiptStaffId; + /** * 是否删除(0否1是) */ diff --git a/Cpop-Oam/src/main/java/com/cpop/oam/business/entity/Task.java b/Cpop-Oam/src/main/java/com/cpop/oam/business/entity/Task.java index 0efe888..3ca3f3b 100644 --- a/Cpop-Oam/src/main/java/com/cpop/oam/business/entity/Task.java +++ b/Cpop-Oam/src/main/java/com/cpop/oam/business/entity/Task.java @@ -45,7 +45,7 @@ public class Task extends BaseEntity implements Serializable { private String responsibleStaffId; /** - * 任务状态(-1:失败;0:待审核;1:待接受;2:进行中;3:待测试;5:暂停;7:接收超时;8:待部署;9:已归档;10:审核不通过) + * 任务状态(-1:失败;0:待审核;1:待接受;2:进行中;3:待测试;5:暂停;6:强制办结;7:接收超时;8:待部署;9:已归档;10:审核不通过) */ private Integer taskStatus; diff --git a/Cpop-Oam/src/main/java/com/cpop/oam/business/entity/TaskWorkOrder.java b/Cpop-Oam/src/main/java/com/cpop/oam/business/entity/TaskWorkOrder.java index 9e0f4fd..b9e9ee5 100644 --- a/Cpop-Oam/src/main/java/com/cpop/oam/business/entity/TaskWorkOrder.java +++ b/Cpop-Oam/src/main/java/com/cpop/oam/business/entity/TaskWorkOrder.java @@ -88,6 +88,21 @@ public class TaskWorkOrder extends BaseEntity implements Serializable { */ private String enforceFinishReason; + /** + * 强制办结员工id + */ + private String enforceFinishStaffId; + + /** + * 强制办结时间 + */ + private LocalDateTime enforceFinishTime; + + /** + * 强制办结剩余时间 + */ + private Integer enforceFinishSurplusTime; + /** * 是否删除(0否1是) */ diff --git a/Cpop-Oam/src/main/java/com/cpop/oam/business/service/ClueService.java b/Cpop-Oam/src/main/java/com/cpop/oam/business/service/ClueService.java index 7a47c4e..be7aa9b 100644 --- a/Cpop-Oam/src/main/java/com/cpop/oam/business/service/ClueService.java +++ b/Cpop-Oam/src/main/java/com/cpop/oam/business/service/ClueService.java @@ -24,7 +24,7 @@ public interface ClueService extends IService { * @since 2024/2/19 * @return R> */ - Page getPersonCluePage(Integer clueStatus, String city, String brandId, String storeId, String chargeOrPhone, String staffId); + Page getPersonCluePage(Integer clueStatus, String city, String brandId, String storeId, String chargeOrPhone, String staffId, String signMonth); /** * 个人签约目标 diff --git a/Cpop-Oam/src/main/java/com/cpop/oam/business/service/TaskWorkOrderService.java b/Cpop-Oam/src/main/java/com/cpop/oam/business/service/TaskWorkOrderService.java index be5c2fb..12a4b53 100644 --- a/Cpop-Oam/src/main/java/com/cpop/oam/business/service/TaskWorkOrderService.java +++ b/Cpop-Oam/src/main/java/com/cpop/oam/business/service/TaskWorkOrderService.java @@ -1,12 +1,9 @@ package com.cpop.oam.business.service; -import com.cpop.oam.business.bo.EnforceFinishWorkOrderBo; +import com.cpop.oam.business.bo.*; import com.cpop.oam.business.vo.*; import com.mybatisflex.core.paginate.Page; import com.mybatisflex.core.service.IService; -import com.cpop.oam.business.bo.PauseWorkOrderBo; -import com.cpop.oam.business.bo.TaskWorkOrderBo; -import com.cpop.oam.business.bo.TaskWorkOrderRecordBo; import com.cpop.oam.business.entity.TaskWorkOrder; import java.time.LocalDate; @@ -131,4 +128,12 @@ public interface TaskWorkOrderService extends IService { * @param bo 请求参数 */ void enforceFinishWorkOrder(EnforceFinishWorkOrderBo bo); + + /** + * 工单模块-工单提交-同意与拒绝强制办结 + * @author DB + * @since 2024/2/21 + * @param bo 请求参数 + */ + void agreeOrRejectEnforceFinish(AgreeOrRejectEnforceFinishBo bo); } diff --git a/Cpop-Oam/src/main/java/com/cpop/oam/business/service/impl/BusinessServiceImpl.java b/Cpop-Oam/src/main/java/com/cpop/oam/business/service/impl/BusinessServiceImpl.java index 2ced07e..899aa36 100644 --- a/Cpop-Oam/src/main/java/com/cpop/oam/business/service/impl/BusinessServiceImpl.java +++ b/Cpop-Oam/src/main/java/com/cpop/oam/business/service/impl/BusinessServiceImpl.java @@ -400,6 +400,7 @@ public class BusinessServiceImpl extends ServiceImpl i StoreSign storeSign = new StoreSign(); storeSign.setStoreId(id); storeSign.setSignStaffId(loginUserInfo.getString("id")); + storeSign.setSignMonth(LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy-MM"))); StoreSignService storeSignService = SpringUtils.getBean(StoreSignService.class); storeSignService.save(storeSign); SpringUtils.getBean(StoreService.class).updateChain() diff --git a/Cpop-Oam/src/main/java/com/cpop/oam/business/service/impl/ClueServiceImpl.java b/Cpop-Oam/src/main/java/com/cpop/oam/business/service/impl/ClueServiceImpl.java index 51dd4c4..8804854 100644 --- a/Cpop-Oam/src/main/java/com/cpop/oam/business/service/impl/ClueServiceImpl.java +++ b/Cpop-Oam/src/main/java/com/cpop/oam/business/service/impl/ClueServiceImpl.java @@ -28,15 +28,18 @@ import org.springframework.transaction.annotation.Transactional; import java.time.LocalDate; import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; import java.util.List; import static com.cpop.oam.business.entity.table.CluePutOffTableDef.CLUE_PUT_OFF; +import static com.cpop.oam.business.entity.table.ClueRecordTableDef.CLUE_RECORD; import static com.cpop.oam.business.entity.table.ClueTableDef.CLUE; import static com.cpop.oam.business.entity.table.ClueUpdateTableDef.CLUE_UPDATE; import static com.cpop.oam.business.entity.table.SignAreaTableDef.SIGN_AREA; import static com.cpop.oam.business.entity.table.SignGoalTableDef.SIGN_GOAL; import static com.cpop.oam.business.entity.table.StaffTableDef.STAFF; import static com.cpop.system.business.entity.table.BrandTableDef.BRAND; +import static com.cpop.system.business.entity.table.StoreSignTableDef.STORE_SIGN; import static com.cpop.system.business.entity.table.StoreTableDef.STORE; import static com.mybatisflex.core.query.QueryMethods.*; @@ -56,7 +59,7 @@ public class ClueServiceImpl extends ServiceImpl implements Cl * @return R> */ @Override - public Page getPersonCluePage(Integer clueStatus, String city, String brandId, String storeId, String chargeOrPhone, String staffId) { + public Page getPersonCluePage(Integer clueStatus, String city, String brandId, String storeId, String chargeOrPhone, String staffId, String signMonth) { PageDomain pageDomain = SqlUtils.getInstance().getPageDomain(); QueryWrapper queryWrapper = QueryWrapper.create(); if (clueStatus != null){ @@ -78,6 +81,7 @@ public class ClueServiceImpl extends ServiceImpl implements Cl .select(CLUE.CITY,CLUE.ID,CLUE.CREATE_TIME,CLUE.DEV_STAFF_ID,CLUE.LAST_FOLLOW_UP_TIME,CLUE.LAST_FOLLOW_UP_CONTENT,CLUE.RECEIPT_TIME,CLUE.STORE_ID) .select(STORE.STORE_NAME,STORE.STORE_ADDR,STORE.PERSON_CHARGE,STORE.PHONE) .select(BRAND.BRAND_NAME) + .select(STORE_SIGN.EXPIRE_DATE) // 跟进间隔 .select(dateDiff(now(), CLUE.LAST_FOLLOW_UP_TIME).as(CluePageVo::getFollowUpInterval)) // 签约时间 @@ -85,6 +89,7 @@ public class ClueServiceImpl extends ServiceImpl implements Cl .from(CLUE) .leftJoin(STORE).on(STORE.ID.eq(CLUE.STORE_ID)) .leftJoin(BRAND).on(BRAND.ID.eq(STORE.BRAND_ID)) + .leftJoin(STORE_SIGN).on(STORE_SIGN.STORE_ID.eq(STORE.ID)) //地区 .and(CLUE.CITY.eq(city)) //品牌 @@ -92,6 +97,7 @@ public class ClueServiceImpl extends ServiceImpl implements Cl //校区 .and(CLUE.STORE_ID.eq(storeId)) .and(STORE.PERSON_CHARGE.likeLeft(chargeOrPhone).or(STORE.PHONE.eq(chargeOrPhone))) + .and(STORE_SIGN.SIGN_MONTH.eq(signMonth)) .orderBy(changeOrderColumn(pageDomain)), CluePageVo.class, //开发员工 @@ -156,6 +162,9 @@ public class ClueServiceImpl extends ServiceImpl implements Cl queryWrapper.and(SIGN_GOAL.STAFF_ID.eq(loginUserInfo.getString("id"))); } SignGoalService signGoalService = SpringUtils.getBean(SignGoalService.class); + if (StringUtils.isBlank(signMonth)) { + signMonth = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy-MM")); + } String[] split = signMonth.split("-"); SignGoal signGoal = signGoalService.getOne(queryWrapper.where(SIGN_GOAL.YEAR.eq(Integer.parseInt(split[0])))); PersonSignGoalVo vo = new PersonSignGoalVo(); @@ -284,6 +293,7 @@ public class ClueServiceImpl extends ServiceImpl implements Cl .select(STAFF.NAME.as(ClueUpdateVo.ClueUpdateRecordVo::getStaffName)) .leftJoin(STAFF).on(STAFF.ID.eq(CLUE_UPDATE.UPDATE_STAFF_ID)) .where(CLUE_UPDATE.CLUE_ID.eq(clueId)) + .orderBy(CLUE_UPDATE.CREATE_TIME.desc()) .listAs(ClueUpdateVo.ClueUpdateRecordVo.class); clueUpdateVo.setRecordVos(recordVos); //获取现有校区数据 @@ -328,7 +338,9 @@ public class ClueServiceImpl extends ServiceImpl implements Cl @Override public List getCluePutOffRecord(String clueId) { return SpringUtils.getBean(CluePutOffService.class).getMapper().selectListByQueryAs(QueryWrapper.create() - .select(CLUE_PUT_OFF.PUT_OFF_DATE, CLUE_PUT_OFF.PUT_OFF_REASON, CLUE_PUT_OFF.CREATE_TIME, CLUE_PUT_OFF.PUT_OFF_STAFF_ID, CLUE_PUT_OFF.AUDIT_STAFF_ID, CLUE_PUT_OFF.AUDIT_STATUS), + .select(CLUE_PUT_OFF.PUT_OFF_DATE, CLUE_PUT_OFF.PUT_OFF_REASON, CLUE_PUT_OFF.CREATE_TIME, CLUE_PUT_OFF.PUT_OFF_STAFF_ID, CLUE_PUT_OFF.AUDIT_STAFF_ID, CLUE_PUT_OFF.AUDIT_STATUS, + CLUE_PUT_OFF.CREATE_TIME,CLUE_PUT_OFF.PUT_OFF_FILE_URL) + .orderBy(CLUE_PUT_OFF.CREATE_TIME.desc()), CluePutOffVo.class, //延迟员工 item -> item.field(CluePutOffVo::getPutOffStaffName) @@ -367,8 +379,10 @@ public class ClueServiceImpl extends ServiceImpl implements Cl if (clue.getClueStatus() == 1) { throw new ServiceException("当前线索已签约,不允许脱离"); } - clue.setClueStatus(0).setResponsibleStaffId(null); - this.updateById(clue); + this.updateChain().set(CLUE.CLUE_STATUS, 0) + .set(CLUE.RESPONSIBLE_STAFF_ID, null) + .where(CLUE.ID.eq(clue.getId())) + .update(); JSONObject loginUserInfo = SecurityUtils.getInstance().getLoginUserInfo(); ClueRecord clueRecord = new ClueRecord(); clueRecord.setClueId(id) @@ -406,7 +420,7 @@ public class ClueServiceImpl extends ServiceImpl implements Cl } return this.mapper.paginateAs(Page.of(pageDomain.getPageNum(),pageDomain.getPageSize()), queryWrapper - .select(CLUE.CITY,CLUE.ID,CLUE.CREATE_TIME,CLUE.DEV_STAFF_ID,CLUE.LAST_FOLLOW_UP_TIME,CLUE.LAST_FOLLOW_UP_CONTENT,CLUE.RECEIPT_TIME,CLUE.STORE_ID) + .select(CLUE.CITY,CLUE.ID,CLUE.CREATE_TIME,CLUE.DEV_STAFF_ID,CLUE.LAST_FOLLOW_UP_TIME,CLUE.LAST_FOLLOW_UP_CONTENT,CLUE.RECEIPT_TIME,CLUE.STORE_ID,CLUE.LAST_RECEIPT_TIME,CLUE.LAST_RECEIPT_STAFF_ID) .select(STORE.STORE_NAME,STORE.STORE_ADDR,STORE.PERSON_CHARGE,STORE.PHONE) .select(BRAND.BRAND_NAME) // 跟进间隔 @@ -437,6 +451,17 @@ public class ClueServiceImpl extends ServiceImpl implements Cl } else { return null; } + }), + //最后领取员工 + item -> item.field(CluePageVo::getLastReceiptStaffName) + .queryWrapper(lastReceiptStaff -> { + if (StringUtils.isNotBlank(lastReceiptStaff.getLastReceiptStaffId())) { + return queryChain().select(STAFF.NAME.as(CluePageVo::getDevStaffName)) + .from(STAFF) + .where(STAFF.ID.eq(lastReceiptStaff.getLastReceiptStaffId())); + } else { + return null; + } })); } @@ -465,7 +490,7 @@ public class ClueServiceImpl extends ServiceImpl implements Cl } return this.mapper.paginateAs(Page.of(pageDomain.getPageNum(),pageDomain.getPageSize()), queryWrapper - .select(CLUE.CITY,CLUE.ID,CLUE.CREATE_TIME,CLUE.DEV_STAFF_ID,CLUE.LAST_FOLLOW_UP_TIME,CLUE.LAST_FOLLOW_UP_CONTENT,CLUE.RECEIPT_TIME,CLUE.STORE_ID) + .select(CLUE.CITY,CLUE.ID,CLUE.CREATE_TIME,CLUE.DEV_STAFF_ID,CLUE.LAST_FOLLOW_UP_TIME,CLUE.LAST_FOLLOW_UP_CONTENT,CLUE.RECEIPT_TIME,CLUE.STORE_ID,CLUE.LAST_RECEIPT_TIME,CLUE.LAST_RECEIPT_STAFF_ID) .select(STORE.STORE_NAME,STORE.STORE_ADDR,STORE.PERSON_CHARGE,STORE.PHONE) .select(BRAND.BRAND_NAME) // 跟进间隔 @@ -496,6 +521,17 @@ public class ClueServiceImpl extends ServiceImpl implements Cl } else { return null; } + }), + //最后领取员工 + item -> item.field(CluePageVo::getLastReceiptStaffName) + .queryWrapper(lastReceiptStaff -> { + if (StringUtils.isNotBlank(lastReceiptStaff.getLastReceiptStaffId())) { + return queryChain().select(STAFF.NAME.as(CluePageVo::getDevStaffName)) + .from(STAFF) + .where(STAFF.ID.eq(lastReceiptStaff.getLastReceiptStaffId())); + } else { + return null; + } })); } @@ -518,7 +554,9 @@ public class ClueServiceImpl extends ServiceImpl implements Cl clue.setResponsibleStaffId(loginUserInfo.getString("id")) .setReceiptTime(now) .setLastFollowUpTime(now) - .setLastFollowUpContent(content); + .setLastFollowUpContent(content) + .setLastReceiptStaffId(loginUserInfo.getString("id")) + .setLastReceiptTime(now); this.updateById(clue); //插入记录 ClueRecord clueRecord = new ClueRecord(); diff --git a/Cpop-Oam/src/main/java/com/cpop/oam/business/service/impl/SignGoalServiceImpl.java b/Cpop-Oam/src/main/java/com/cpop/oam/business/service/impl/SignGoalServiceImpl.java index 0a13c7a..b585504 100644 --- a/Cpop-Oam/src/main/java/com/cpop/oam/business/service/impl/SignGoalServiceImpl.java +++ b/Cpop-Oam/src/main/java/com/cpop/oam/business/service/impl/SignGoalServiceImpl.java @@ -48,7 +48,7 @@ public class SignGoalServiceImpl extends ServiceImpl i PageDomain pageDomain = SqlUtils.getInstance().getPageDomain(); return this.mapper.paginateAs(Page.of(pageDomain.getPageNum(),pageDomain.getPageSize()), QueryWrapper.create() - .select(SIGN_GOAL.ID,SIGN_GOAL.YEAR,SIGN_GOAL.JAN_GOAL,SIGN_GOAL.JAN_FINISH,SIGN_GOAL.FEB_GOAL,SIGN_GOAL.FEB_FINISH,SIGN_GOAL.MAR_GOAL,SIGN_GOAL.MAR_FINISH, + .select(SIGN_GOAL.ID,SIGN_GOAL.STAFF_ID,SIGN_GOAL.YEAR,SIGN_GOAL.JAN_GOAL,SIGN_GOAL.JAN_FINISH,SIGN_GOAL.FEB_GOAL,SIGN_GOAL.FEB_FINISH,SIGN_GOAL.MAR_GOAL,SIGN_GOAL.MAR_FINISH, SIGN_GOAL.APR_GOAL,SIGN_GOAL.APR_FINISH,SIGN_GOAL.MAY_GOAL,SIGN_GOAL.MAY_FINISH,SIGN_GOAL.JUN_GOAL,SIGN_GOAL.JUN_FINISH,SIGN_GOAL.JUL_GOAL, SIGN_GOAL.JUL_FINISH,SIGN_GOAL.AUG_GOAL,SIGN_GOAL.AUG_FINISH,SIGN_GOAL.SEP_GOAL,SIGN_GOAL.SEP_FINISH,SIGN_GOAL.OCT_GOAL,SIGN_GOAL.OCT_FINISH, SIGN_GOAL.NOV_GOAL,SIGN_GOAL.NOV_FINISH,SIGN_GOAL.DEC_GOAL,SIGN_GOAL.DEC_FINISH) diff --git a/Cpop-Oam/src/main/java/com/cpop/oam/business/service/impl/TaskServiceImpl.java b/Cpop-Oam/src/main/java/com/cpop/oam/business/service/impl/TaskServiceImpl.java index 00c86fb..c2f1363 100644 --- a/Cpop-Oam/src/main/java/com/cpop/oam/business/service/impl/TaskServiceImpl.java +++ b/Cpop-Oam/src/main/java/com/cpop/oam/business/service/impl/TaskServiceImpl.java @@ -811,7 +811,7 @@ public class TaskServiceImpl extends ServiceImpl implements Ta .select(TASK.ID, TASK.TASK_CONTENT, TASK.TASK_STATUS) .select(TASK_STAFF_GROUP.GRADE_POINT, TASK_STAFF_GROUP.REMARK) .leftJoin(TASK_STAFF_GROUP).on(TASK_STAFF_GROUP.TASK_ID.eq(TASK.ID)) - .where(TASK.TASK_STATUS.in(2, 3, 5, 7, 8, 9)) + .where(TASK.TASK_STATUS.in(2, 3, 5, 6, 7, 8, 9)) , TaskIndividualGpaDetailVo.class); TaskIndividualGpaVo vo = new TaskIndividualGpaVo(); if (individualGpas.isEmpty()) { diff --git a/Cpop-Oam/src/main/java/com/cpop/oam/business/service/impl/TaskWorkOrderServiceImpl.java b/Cpop-Oam/src/main/java/com/cpop/oam/business/service/impl/TaskWorkOrderServiceImpl.java index 017947b..65e0965 100644 --- a/Cpop-Oam/src/main/java/com/cpop/oam/business/service/impl/TaskWorkOrderServiceImpl.java +++ b/Cpop-Oam/src/main/java/com/cpop/oam/business/service/impl/TaskWorkOrderServiceImpl.java @@ -7,6 +7,7 @@ import com.cpop.common.utils.StringUtils; import com.cpop.common.utils.bean.BeanUtils; import com.cpop.core.base.entity.PageDomain; import com.cpop.core.base.exception.ServiceException; +import com.cpop.core.base.exception.UtilException; import com.cpop.core.service.RedisService; import com.cpop.core.utils.QuartzUtils; import com.cpop.core.utils.SecurityUtils; @@ -101,7 +102,7 @@ public class TaskWorkOrderServiceImpl extends ServiceImpl item.field(TaskWorkOrderReceiveDealPauseDto::getRecordStaffName) @@ -139,6 +140,7 @@ public class TaskWorkOrderServiceImpl extends ServiceImpl item)); @@ -800,7 +816,7 @@ public class TaskWorkOrderServiceImpl extends ServiceImpl item.field(TaskWorkOrderPersonVo::getRecordStaffName) @@ -943,7 +959,148 @@ public class TaskWorkOrderServiceImpl extends ServiceImpl staffIds = new HashSet<>(); + staffIds.add(task.getResponsibleStaffId()); + staffIds.add(task.getRecordStaffId()); + Map staffMap = staffService.listAs(QueryWrapper.create() + .select(STAFF.ID, STAFF.NAME) + .select(SYS_USER.PHONE_NUMBER.as(StaffInfoVo::getPhoneNumber)) + .from(STAFF) + .leftJoin(SYS_USER) + .on(SYS_USER.ID.eq(STAFF.USER_ID)) + .where(STAFF.ID.in(staffIds)), + StaffInfoVo.class) + .stream().collect(Collectors.toMap(StaffInfoVo::getId, item -> item)); + List phoneList = new ArrayList<>(); + phoneList.add(staffMap.get(task.getResponsibleStaffId()).getPhoneNumber()); + // 记录人手机号 + phoneList.add(staffMap.get(task.getRecordStaffId()).getPhoneNumber()); + // 通知记录 + try { + SpringUtils.getBean(WebHookSendHandler.class).webHookSendText(WebHookKeyConstant.ORDER_INFO_BOT, phoneList, task.getTaskContent() + "\n" + finishTimeString, false); + } catch (IOException e) { + throw new ServiceException("发送消息通知失败!"); + } + // 删除工单完结超时任务 + try { + SpringUtils.getBean(QuartzUtils.class).deleteJob(QuartzEnums.WORK_ORDER_OVERTIME_TASK.getName() + bo.getId(), QuartzEnums.WORK_ORDER_OVERTIME_TASK.getGroup()); + // 删除工单暂停恢复任务 + startOrRemoveWorkOrderPauseRecover(bo.getId(), false, null); + // 删除工单完结超时任务 + workOrderFinishOverTimeRemindTask(bo.getId(), 2, null); + } catch (SchedulerException e) { + throw new ServiceException("删除工单完结超时任务失败"); + } + } + + /** + * 工单模块-工单提交-同意与拒绝强制办结 + * @author DB + * @since 2024/2/21 + * @param bo 请求参数 + */ + @Override + public void agreeOrRejectEnforceFinish(AgreeOrRejectEnforceFinishBo bo) { + TaskWorkOrder workOrder = this.getById(bo.getId()); + TaskService taskService = SpringUtils.getBean(TaskService.class); + Task task = taskService.getById(workOrder.getTaskId()); + JSONObject loginUserInfo = SecurityUtils.getInstance().getLoginUserInfo(); + String finishString; + //同意 + if (bo.getAgreeOrReject()){ + task.setTaskStatus(9); + task.setTaskItem(3); + taskService.updateById(task); + workOrder.setFinishStaffId(loginUserInfo.getString("id")); + workOrder.setFinishTime(workOrder.getEnforceFinishTime()); + this.updateById(workOrder); + finishString = "\n工单办结申请通过,办结时间为:" + workOrder.getFinishTime().format(DateTimeFormatter.ofPattern(DateUtils.YYYY_MM_DD_HH_MM_SS)); + } else { + //拒绝继续计时 + task.setTaskStatus(2); + task.setTaskItem(0); + taskService.updateById(task); + LocalDateTime now = LocalDateTime.now(); + finishString = "\n工单办结申请未通过,工单到期时间为:" + now.plusMinutes(workOrder.getEnforceFinishSurplusTime().longValue()).format(DateTimeFormatter.ofPattern(DateUtils.YYYY_MM_DD_HH_MM_SS)) + + "\n工单到期剩余时间为:" + workOrder.getEnforceFinishSurplusTime() + "分钟"; + //重新发起工单超时任务 + startOrUpdateWorkOrderOvertimeTask(workOrder.getId(), false, now, workOrder.getEnforceFinishSurplusTime()); + } + // 插入工单暂停记录 + TaskWorkOrderRecord taskWorkOrderRecord = new TaskWorkOrderRecord(); + // 读取员工信息 + taskWorkOrderRecord.setRecordStaffId(loginUserInfo.getString("id")) + .setTaskWorkOrderId(workOrder.getId()) + .setRecordText(finishString); + // 插入记录 + SpringUtils.getBean(TaskWorkOrderRecordService.class).save(taskWorkOrderRecord); + // 负责人手机号 + StaffService staffService = SpringUtils.getBean(StaffService.class); + Set staffIds = new HashSet<>(); + staffIds.add(task.getResponsibleStaffId()); + staffIds.add(task.getRecordStaffId()); + Map staffMap = staffService.listAs(QueryWrapper.create() + .select(STAFF.ID, STAFF.NAME) + .select(SYS_USER.PHONE_NUMBER.as(StaffInfoVo::getPhoneNumber)) + .from(STAFF) + .leftJoin(SYS_USER) + .on(SYS_USER.ID.eq(STAFF.USER_ID)) + .where(STAFF.ID.in(staffIds)), + StaffInfoVo.class) + .stream().collect(Collectors.toMap(StaffInfoVo::getId, item -> item)); + List phoneList = new ArrayList<>(); + phoneList.add(staffMap.get(task.getResponsibleStaffId()).getPhoneNumber()); + // 记录人手机号 + phoneList.add(staffMap.get(task.getRecordStaffId()).getPhoneNumber()); + // 通知记录 + try { + SpringUtils.getBean(WebHookSendHandler.class).webHookSendText(WebHookKeyConstant.ORDER_INFO_BOT, phoneList, task.getTaskContent() + "\n" + finishString, false); + } catch (IOException e) { + throw new ServiceException("发送消息通知失败!"); + } } } diff --git a/Cpop-Oam/src/main/java/com/cpop/oam/business/vo/CluePageVo.java b/Cpop-Oam/src/main/java/com/cpop/oam/business/vo/CluePageVo.java index df783cc..0370fcc 100644 --- a/Cpop-Oam/src/main/java/com/cpop/oam/business/vo/CluePageVo.java +++ b/Cpop-Oam/src/main/java/com/cpop/oam/business/vo/CluePageVo.java @@ -136,4 +136,29 @@ public class CluePageVo { @ApiModelProperty(value = "签约状态(0:待签约;1:已签约;2:警告线索)") private Integer clueStatus; + /** + * 到期日期 + */ + @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8") + private LocalDate expireDate; + + /** + * 最后接收时间 + */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + @ApiModelProperty(value = "最后接收时间") + private String lastReceiptTime; + + /** + * 最后接收员工id + */ + @ApiModelProperty(value = "最后接收员工id") + private String lastReceiptStaffId; + + /** + * 最后接收员工姓名 + */ + @ApiModelProperty(value = "最后接收员工姓名") + private String lastReceiptStaffName; + } diff --git a/Cpop-Oam/src/main/java/com/cpop/oam/business/vo/CluePutOffVo.java b/Cpop-Oam/src/main/java/com/cpop/oam/business/vo/CluePutOffVo.java index ce31380..8f0b7de 100644 --- a/Cpop-Oam/src/main/java/com/cpop/oam/business/vo/CluePutOffVo.java +++ b/Cpop-Oam/src/main/java/com/cpop/oam/business/vo/CluePutOffVo.java @@ -1,5 +1,6 @@ package com.cpop.oam.business.vo; +import com.fasterxml.jackson.annotation.JsonFormat; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.Data; @@ -7,6 +8,7 @@ import lombok.experimental.Accessors; import org.springframework.format.annotation.DateTimeFormat; import java.time.LocalDate; +import java.time.LocalDateTime; /** * @author DB @@ -60,4 +62,17 @@ public class CluePutOffVo { */ @ApiModelProperty(value = "延期原因") private String putOffReason; + + /** + * 创建时间 + */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + @ApiModelProperty(value = "创建时间") + private LocalDateTime createTime; + + /** + * 延期附件 + */ + @ApiModelProperty(value = "延期附件") + private String putOffFileUrl; } diff --git a/Cpop-Oam/src/main/java/com/cpop/oam/business/vo/SignGoalPageVo.java b/Cpop-Oam/src/main/java/com/cpop/oam/business/vo/SignGoalPageVo.java index a358ce7..d5c8bd8 100644 --- a/Cpop-Oam/src/main/java/com/cpop/oam/business/vo/SignGoalPageVo.java +++ b/Cpop-Oam/src/main/java/com/cpop/oam/business/vo/SignGoalPageVo.java @@ -30,6 +30,12 @@ public class SignGoalPageVo { @ApiModelProperty(value = "员工名") private String staffName; + /** + * 员工Id + */ + @ApiModelProperty(value = "员工Id") + private String staffId; + /** * 签约区域 */ diff --git a/Cpop-Oam/src/main/java/com/cpop/oam/business/vo/TaskWorkOrderPersonVo.java b/Cpop-Oam/src/main/java/com/cpop/oam/business/vo/TaskWorkOrderPersonVo.java index 42fd19b..fc246b9 100644 --- a/Cpop-Oam/src/main/java/com/cpop/oam/business/vo/TaskWorkOrderPersonVo.java +++ b/Cpop-Oam/src/main/java/com/cpop/oam/business/vo/TaskWorkOrderPersonVo.java @@ -72,5 +72,5 @@ public class TaskWorkOrderPersonVo { * 工单状态 */ @ApiModelProperty(value = "工单状态") - private Integer workOrderStatus; + private Integer taskStatus; } diff --git a/Cpop-Oam/src/main/java/com/cpop/oam/business/vo/TaskWorkOrderReceiveDealPauseVo.java b/Cpop-Oam/src/main/java/com/cpop/oam/business/vo/TaskWorkOrderReceiveDealPauseVo.java index 26274f8..5344674 100644 --- a/Cpop-Oam/src/main/java/com/cpop/oam/business/vo/TaskWorkOrderReceiveDealPauseVo.java +++ b/Cpop-Oam/src/main/java/com/cpop/oam/business/vo/TaskWorkOrderReceiveDealPauseVo.java @@ -153,6 +153,12 @@ public class TaskWorkOrderReceiveDealPauseVo { @ApiModelProperty(value = "任务接收用时/min") private Integer receivingTime; + /** + * 任务状态(-1:失败;0:待审核;1:待接受;2:进行中;3:待测试;5:暂停;7:接收超时;8:待部署;9:待归档;10:审核不通过) + */ + @ApiModelProperty(value = "任务状态(-1:失败;0:待审核;1:待接受;2:进行中;3:待测试;5:暂停;7:接收超时;8:待部署;9:待归档;10:审核不通过)") + private Integer taskStatus; + } /** diff --git a/Cpop-Pay/pom.xml b/Cpop-Pay/pom.xml index 7ed7148..20788b3 100644 --- a/Cpop-Pay/pom.xml +++ b/Cpop-Pay/pom.xml @@ -24,6 +24,11 @@ com.github.binarywang weixin-java-pay + + + org.jdom + jdom2 + diff --git a/Cpop-Pay/src/main/java/com/cpop/pay/framewok/config/wxPay/WxPayProperties.java b/Cpop-Pay/src/main/java/com/cpop/pay/framewok/config/wxPay/WxPayProperties.java index 1c63503..4512750 100644 --- a/Cpop-Pay/src/main/java/com/cpop/pay/framewok/config/wxPay/WxPayProperties.java +++ b/Cpop-Pay/src/main/java/com/cpop/pay/framewok/config/wxPay/WxPayProperties.java @@ -100,4 +100,9 @@ public class WxPayProperties { * 先学后付用户支付成功通知地址 */ private String learnNowPayLaterServiceOrderNotifyUrl; + + /** + * 普通模式下的商户号 + */ + private String easyLearnUnionPayNormalNotifyUrl; } diff --git a/Cpop-Pay/src/main/java/com/cpop/pay/framewok/handler/wxPay/WxPayHandler.java b/Cpop-Pay/src/main/java/com/cpop/pay/framewok/handler/wxPay/WxPayHandler.java index 167b4df..68a6eba 100644 --- a/Cpop-Pay/src/main/java/com/cpop/pay/framewok/handler/wxPay/WxPayHandler.java +++ b/Cpop-Pay/src/main/java/com/cpop/pay/framewok/handler/wxPay/WxPayHandler.java @@ -5,6 +5,8 @@ import com.cpop.common.utils.ServletUtils; import com.cpop.common.utils.StringUtils; import com.cpop.core.base.exception.ServiceException; import com.cpop.core.utils.SecurityUtils; +import com.cpop.core.utils.SpringUtils; +import com.cpop.pay.framewok.config.wxPay.WxPayConfiguration; import com.github.binarywang.wxpay.bean.ecommerce.SignatureHeader; import com.github.binarywang.wxpay.config.WxPayConfig; import com.github.binarywang.wxpay.service.WxPayService; @@ -44,7 +46,7 @@ public class WxPayHandler { } String brandId = loginUserInfo.getString("brandId"); Row brand = Db.selectOneByQuery("cp_sys_brand", QueryWrapper.create().where("id = ?", brandId)); - if (brand.isEmpty()) { + if (brand.isEmpty() || StringUtils.isBlank(brand.getString("wxMchId"))) { throw new ServiceException("当前品牌暂未开通微信支付,请联系相关客服"); } //检查是否开启分账 @@ -121,7 +123,7 @@ public class WxPayHandler { if (brand != null && StringUtils.isNotBlank(brand.getString("wxMchId"))){ wxMchId = brand.getString("wxMchId"); }else { - wxMchId = "1661323640"; + wxMchId = null; } } } else { @@ -130,7 +132,7 @@ public class WxPayHandler { if (brand != null && StringUtils.isNotBlank(brand.getString("wxMchId"))){ wxMchId = brand.getString("wxMchId"); }else { - wxMchId = "1661323640"; + wxMchId = null; } } return wxMchId; @@ -152,4 +154,5 @@ public class WxPayHandler { log.info("微信支付分回调ID:{}",request.getHeader("Request-ID")); return signatureHeader; } + } diff --git a/Cpop-Pay/src/main/java/com/cpop/pay/framewok/handler/wxPay/WxPayNormalHandler.java b/Cpop-Pay/src/main/java/com/cpop/pay/framewok/handler/wxPay/WxPayNormalHandler.java new file mode 100644 index 0000000..7924bc6 --- /dev/null +++ b/Cpop-Pay/src/main/java/com/cpop/pay/framewok/handler/wxPay/WxPayNormalHandler.java @@ -0,0 +1,65 @@ +package com.cpop.pay.framewok.handler.wxPay; + +import com.cpop.core.utils.SpringUtils; +import com.cpop.pay.framewok.config.wxPay.WxPayConfiguration; +import org.checkerframework.checker.units.qual.A; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.springframework.transaction.annotation.Transactional; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.math.BigDecimal; +import java.util.Map; +import java.util.SortedMap; +import java.util.TreeMap; + +/** + * @author DB + * @version 1.0.0 + * @since 2024-02-22 20:10 + */ +@Component +public class WxPayNormalHandler { + + @Autowired + private WxUtils wxUtils; + + public T createOrder(String orderId,String openId, BigDecimal price, String body, String ipAddress,String notifyUrl) throws IOException { + SortedMap parameters = new TreeMap(); + parameters.put("appid", "wx20853d18c455e874"); + parameters.put("mch_id", "1618436087"); + parameters.put("openid", openId); + // 默认 "WEB" + parameters.put("device_info", "WEB"); + parameters.put("body", body); + // 32 位随机字符串 + parameters.put("nonce_str", wxUtils.gen32RandomString()); + parameters.put("notify_url", notifyUrl); + parameters.put("out_trade_no", orderId); + parameters.put("total_fee", price.multiply(BigDecimal.valueOf(100)).intValue()); + // parameters.put("total_fee", 1); // 测试时,将支付金额设置为 1 分钱 + parameters.put("spbill_create_ip", ipAddress); + parameters.put("trade_type", "JSAPI"); + // sign 必须在最后 + parameters.put("sign", wxUtils.createSign(parameters, "JamBox20220329174000000000000002")); + // 执行 HTTP 请求,获取接收的字符串(一段 XML) + String result = wxUtils.executeHttpPost("https://api.mch.weixin.qq.com/pay/unifiedorder", parameters); + return wxUtils.createSign2(result, "JamBox20220329174000000000000002"); + } + + public String fail() { + return "\n" + + " \n" + + " \n" + + ""; + } + + public String success() { + return "\n" + + " \n" + + " \n" + + ""; + } +} diff --git a/Cpop-Pay/src/main/java/com/cpop/pay/framewok/handler/wxPay/WxUtils.java b/Cpop-Pay/src/main/java/com/cpop/pay/framewok/handler/wxPay/WxUtils.java new file mode 100644 index 0000000..06f672d --- /dev/null +++ b/Cpop-Pay/src/main/java/com/cpop/pay/framewok/handler/wxPay/WxUtils.java @@ -0,0 +1,283 @@ +package com.cpop.pay.framewok.handler.wxPay; + +import com.cpop.common.utils.StringUtils; +import com.cpop.core.base.exception.UtilException; +import com.github.binarywang.wxpay.bean.order.WxPayAppOrderResult; +import com.github.binarywang.wxpay.bean.order.WxPayMpOrderResult; +import com.github.binarywang.wxpay.constant.WxPayConstants; +import com.github.binarywang.wxpay.util.SignUtils; +import org.apache.http.HttpResponse; +import org.apache.http.client.HttpClient; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.entity.StringEntity; +import org.apache.http.impl.client.HttpClients; +import org.apache.poi.ss.formula.functions.T; +import org.springframework.stereotype.Component; +import org.jdom2.Document; +import org.jdom2.Element; +import org.jdom2.JDOMException; +import org.jdom2.input.SAXBuilder; + +import javax.servlet.http.HttpServletRequest; +import java.io.*; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.*; + +/** + * @author DB + * @version 1.0.0 + * @since 2024-02-22 20:13 + */ +@Component +public class WxUtils { + + /** + * 执行 POST 方法的 HTTP 请求 + * + * @param url + * @param parameters + * @return + * @throws IOException + */ + public String executeHttpPost(String url, SortedMap parameters) throws IOException { + HttpClient client = HttpClients.createDefault(); + HttpPost request = new HttpPost(url); + request.setHeader("Content-type", "application/xml"); + request.setHeader("Accept", "application/xml"); + request.setEntity(new StringEntity(transferMapToXml(parameters), "UTF-8")); + HttpResponse response = client.execute(request); + return readResponse(response); + } + + + /** + * 第一次签名 + * + * @param parameters 数据为服务器生成,下单时必须的字段排序签名 + * @param key + * @return + */ + public String createSign(SortedMap parameters, String key) { + StringBuffer sb = new StringBuffer(); + Set es = parameters.entrySet();//所有参与传参的参数按照accsii排序(升序) + Iterator it = es.iterator(); + while (it.hasNext()) { + Map.Entry entry = (Map.Entry) it.next(); + String k = (String) entry.getKey(); + Object v = entry.getValue(); + if (null != v && !"".equals(v) + && !"sign".equals(k) && !"key".equals(k)) { + sb.append(k + "=" + v + "&"); + } + } + sb.append("key=" + key); + return encodeMD5(sb.toString()); + } + + /** + * 第二次签名 + * + * @param result 数据为微信返回给服务器的数据(XML 的 String),再次签名后传回给客户端(APP)使用 + * @param key 密钥 + * @return + * @throws IOException + */ + public T createSign2(String result, String key) throws IOException { + SortedMap map = new TreeMap<>(transferXmlToMap(result)); + if (StringUtils.equals(map.get("result_code").toString(),"FAIL")){ + throw new UtilException(map.get("err_code_des").toString()); + } + //wxJava获取签名 + String signType = WxPayConstants.SignType.MD5; + String appid = map.get("appid").toString(); + WxPayMpOrderResult payResult = WxPayMpOrderResult.builder() + .appId(appid) + .timeStamp(String.valueOf(System.currentTimeMillis() / 1000)) + .nonceStr(map.get("nonce_str").toString()) + .packageValue("prepay_id=" + map.get("prepay_id")) + .signType(signType) + .build(); + payResult.setPaySign(SignUtils.createSign(payResult, signType, key, null)); + return (T) payResult; + } + + /** + * 验证签名是否正确 + * + * @return boolean + * @throws Exception + */ + public boolean checkSign(SortedMap parameters, String key) throws Exception { + String signWx = parameters.get("sign").toString(); + if (signWx == null) return false; + parameters.remove("sign"); // 需要去掉原 map 中包含的 sign 字段再进行签名 + String signMe = createSign(parameters, key); + return signWx.equals(signMe); + } + + /** + * 读取 request body 内容作为字符串 + * + * @param request + * @return + * @throws IOException + */ + public String readRequest(HttpServletRequest request) throws IOException { + InputStream inputStream; + StringBuffer sb = new StringBuffer(); + inputStream = request.getInputStream(); + String str; + BufferedReader in = new BufferedReader(new InputStreamReader(inputStream, "UTF-8")); + while ((str = in.readLine()) != null) { + sb.append(str); + } + in.close(); + inputStream.close(); + return sb.toString(); + } + + /** + * 读取 response body 内容为字符串 + */ + public String readResponse(HttpResponse response) throws IOException { + BufferedReader in = new BufferedReader( + new InputStreamReader(response.getEntity().getContent())); + String result = new String(); + String line; + while ((line = in.readLine()) != null) { + result += line; + } + return result; + } + + /** + * 将 Map 转化为 XML + * + * @param map + * @return + */ + public String transferMapToXml(SortedMap map) { + StringBuffer sb = new StringBuffer(); + sb.append(""); + for (String key : map.keySet()) { + sb.append("<").append(key).append(">") + .append(map.get(key)) + .append(""); + } + return sb.append("").toString(); + } + + /** + * 将 XML 转化为 map + * + * @param strxml + * @return + * @throws JDOMException + * @throws IOException + */ + public Map transferXmlToMap(String strxml) throws IOException { + strxml = strxml.replaceFirst("encoding=\".*\"", "encoding=\"UTF-8\""); + if (null == strxml || "".equals(strxml)) { + return null; + } + Map m = new HashMap(); + InputStream in = new ByteArrayInputStream(strxml.getBytes("UTF-8")); + SAXBuilder builder = new SAXBuilder(); + Document doc = null; + try { + doc = builder.build(in); + } catch (JDOMException e) { + throw new IOException(e.getMessage()); // 统一转化为 IO 异常输出 + } + // 解析 DOM + Element root = doc.getRootElement(); + List list = root.getChildren(); + Iterator it = list.iterator(); + while (it.hasNext()) { + Element e = (Element) it.next(); + String k = e.getName(); + String v = ""; + List children = e.getChildren(); + if (children.isEmpty()) { + v = e.getTextNormalize(); + } else { + v = getChildrenText(children); + } + m.put(k, v); + } + //关闭流 + in.close(); + return m; + } + + // 辅助 transferXmlToMap 方法递归提取子节点数据 + private String getChildrenText(List children) { + StringBuffer sb = new StringBuffer(); + if (!children.isEmpty()) { + Iterator it = children.iterator(); + while (it.hasNext()) { + Element e = (Element) it.next(); + String name = e.getName(); + String value = e.getTextNormalize(); + List list = e.getChildren(); + sb.append("<" + name + ">"); + if (!list.isEmpty()) { + sb.append(getChildrenText(list)); + } + sb.append(value); + sb.append(""); + } + } + return sb.toString(); + } + + + /** + * 生成 32 位随机字符串,包含:数字、字母大小写 + * + * @return + */ + public String gen32RandomString() { + char[] dict = {'1', '2', '3', '4', '5', '6', '7', '8', '9', '0', + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', + 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'}; + StringBuffer sb = new StringBuffer(); + Random random = new Random(); + for (int i = 0; i < 32; i++) { + sb.append(String.valueOf(dict[(int) (Math.random() * 36)])); + } + return sb.toString(); + } + + /** + * MD5 签名 + * + * @param str + * @return 签名后的字符串信息 + */ + public String encodeMD5(String str) { + try { + MessageDigest messageDigest = MessageDigest.getInstance("MD5"); + byte[] inputByteArray = (str).getBytes(); + messageDigest.update(inputByteArray); + byte[] resultByteArray = messageDigest.digest(); + return byteArrayToHex(resultByteArray); + } catch (NoSuchAlgorithmException e) { + return null; + } + } + + // 辅助 encodeMD5 方法实现 + private String byteArrayToHex(byte[] byteArray) { + char[] hexDigits = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'}; + char[] resultCharArray = new char[byteArray.length * 2]; + int index = 0; + for (byte b : byteArray) { + resultCharArray[index++] = hexDigits[b >>> 4 & 0xf]; + resultCharArray[index++] = hexDigits[b & 0xf]; + } + // 字符数组组合成字符串返回 + return new String(resultCharArray); + } +} diff --git a/Cpop-System/src/main/java/com/cpop/system/business/entity/StoreSign.java b/Cpop-System/src/main/java/com/cpop/system/business/entity/StoreSign.java index 2703fb5..b58fcba 100644 --- a/Cpop-System/src/main/java/com/cpop/system/business/entity/StoreSign.java +++ b/Cpop-System/src/main/java/com/cpop/system/business/entity/StoreSign.java @@ -66,6 +66,11 @@ public class StoreSign extends BaseEntity implements Serializable { */ private String runOffReason; + /** + * 签约月份 + */ + private String signMonth; + /** * 逻辑删除(0否1是) */ diff --git a/pom.xml b/pom.xml index 4ba3b2f..1f94542 100644 --- a/pom.xml +++ b/pom.xml @@ -51,6 +51,7 @@ 5.6.155 5.3.31 5.8.23 + 2.0.6.1 @@ -226,6 +227,12 @@ hutool-all ${hutool.version} + + + org.jdom + jdom2 + ${jdom2.version} +