Sfoglia il codice sorgente

添加极光推送通知

Yangzw 5 mesi fa
parent
commit
dab228a372
23 ha cambiato i file con 1037 aggiunte e 6 eliminazioni
  1. 1 3
      feifan-module-mall/feifan-module-trade-api/src/main/java/cn/newfeifan/mall/module/trade/enums/ErrorCodeConstants.java
  2. 94 0
      feifan-module-mall/feifan-module-trade-biz/src/main/java/cn/newfeifan/mall/module/trade/controller/admin/jpushmessagelog/JPushMessageLogController.java
  3. 43 0
      feifan-module-mall/feifan-module-trade-biz/src/main/java/cn/newfeifan/mall/module/trade/controller/admin/jpushmessagelog/vo/JPushMessageLogPageReqVO.java
  4. 49 0
      feifan-module-mall/feifan-module-trade-biz/src/main/java/cn/newfeifan/mall/module/trade/controller/admin/jpushmessagelog/vo/JPushMessageLogRespVO.java
  5. 40 0
      feifan-module-mall/feifan-module-trade-biz/src/main/java/cn/newfeifan/mall/module/trade/controller/admin/jpushmessagelog/vo/JPushMessageLogSaveReqVO.java
  6. 57 0
      feifan-module-mall/feifan-module-trade-biz/src/main/java/cn/newfeifan/mall/module/trade/dal/dataobject/jpushmessagelog/JPushMessageLogDO.java
  7. 32 0
      feifan-module-mall/feifan-module-trade-biz/src/main/java/cn/newfeifan/mall/module/trade/dal/mysql/jpushmessagelog/JPushMessageLogMapper.java
  8. 46 0
      feifan-module-mall/feifan-module-trade-biz/src/main/java/cn/newfeifan/mall/module/trade/service/aftersale/AfterSaleServiceImpl.java
  9. 53 0
      feifan-module-mall/feifan-module-trade-biz/src/main/java/cn/newfeifan/mall/module/trade/service/jpushmessagelog/JPushMessageLogService.java
  10. 70 0
      feifan-module-mall/feifan-module-trade-biz/src/main/java/cn/newfeifan/mall/module/trade/service/jpushmessagelog/JPushMessageLogServiceImpl.java
  11. 47 2
      feifan-module-mall/feifan-module-trade-biz/src/main/java/cn/newfeifan/mall/module/trade/service/order/TradeOrderUpdateServiceImpl.java
  12. 177 0
      feifan-module-mall/feifan-module-trade-biz/src/main/java/cn/newfeifan/mall/module/trade/utils/push/JPushUtils.java
  13. 34 0
      feifan-module-mall/feifan-module-trade-biz/src/main/java/cn/newfeifan/mall/module/trade/utils/push/pojo/Callback.java
  14. 33 0
      feifan-module-mall/feifan-module-trade-biz/src/main/java/cn/newfeifan/mall/module/trade/utils/push/pojo/Message.java
  15. 64 0
      feifan-module-mall/feifan-module-trade-biz/src/main/java/cn/newfeifan/mall/module/trade/utils/push/pojo/NotificationData.java
  16. 90 0
      feifan-module-mall/feifan-module-trade-biz/src/main/java/cn/newfeifan/mall/module/trade/utils/push/pojo/Options.java
  17. 56 0
      feifan-module-mall/feifan-module-trade-biz/src/main/java/cn/newfeifan/mall/module/trade/utils/push/pojo/RequestBody.java
  18. 12 0
      feifan-module-mall/feifan-module-trade-biz/src/main/resources/mapper/jpushmessagelog/JPushMessageLogMapper.xml
  19. 5 0
      feifan-module-member/feifan-module-member-biz/src/main/java/cn/newfeifan/mall/module/member/dal/dataobject/user/MemberUserDO.java
  20. 8 0
      feifan-module-member/feifan-module-member-biz/src/main/java/cn/newfeifan/mall/module/member/service/user/MemberUserService.java
  21. 18 0
      feifan-module-member/feifan-module-member-biz/src/main/java/cn/newfeifan/mall/module/member/service/user/MemberUserServiceImpl.java
  22. 4 0
      feifan-server/src/main/resources/application-local.yaml
  23. 4 1
      feifan-server/src/main/resources/application-prod.yaml

+ 1 - 3
feifan-module-mall/feifan-module-trade-api/src/main/java/cn/newfeifan/mall/module/trade/enums/ErrorCodeConstants.java

@@ -25,7 +25,6 @@ public interface ErrorCodeConstants {
     ErrorCode ORDER_COMMENT_STATUS_NOT_FALSE = new ErrorCode(1_011_000_020, "创建交易订单项的评价失败,订单已评价");
     ErrorCode ORDER_DELIVERY_FAIL_REFUND_STATUS_NOT_NONE = new ErrorCode(1_011_000_021, "交易订单发货失败,订单已退款或部分退款");
     ErrorCode ORDER_DELIVERY_FAIL_COMBINATION_RECORD_STATUS_NOT_SUCCESS = new ErrorCode(1_011_000_022, "交易订单发货失败,拼团未成功");
-    ErrorCode ORDER_DELIVERY_FAIL_BARGAIN_RECORD_STATUS_NOT_SUCCESS = new ErrorCode(1_011_000_023, "交易订单发货失败,砍价未成功");
     ErrorCode ORDER_DELIVERY_FAIL_DELIVERY_TYPE_NOT_EXPRESS = new ErrorCode(1_011_000_024, "交易订单发货失败,发货类型不是快递");
     ErrorCode ORDER_CANCEL_FAIL_STATUS_NOT_UNPAID = new ErrorCode(1_011_000_025, "交易订单取消失败,订单不是【待支付】状态");
     ErrorCode ORDER_UPDATE_PRICE_FAIL_PAID = new ErrorCode(1_011_000_026, "支付订单调价失败,原因:支付订单已付款,不能调价");
@@ -97,9 +96,8 @@ public interface ErrorCodeConstants {
     ErrorCode NOT_SUFFICIENT_FUNDS = new ErrorCode(1_011_008_004, "账号无可用单量,需要充值");
     ErrorCode CALLBACK_PARAMETER_ERROR = new ErrorCode(1_011_008_005, "第三方回调参数为Null");
     ErrorCode ORDER_CALLBACK_LOGS_NOT_EXISTS = new ErrorCode(1_011_008_007, "回调日志不存在");
-    ErrorCode CREATE_TRADE_ORDER_LOGS_ERROR = new ErrorCode(1_011_008_008, "创建第三方日志异常");
     ErrorCode MESSAGE_LOG_NOT_EXISTS = new ErrorCode(1_011_008_009, "微信模板消息日志不存在");
-    ErrorCode DAILY_BILL_NOT_EXISTS = new ErrorCode(1_011_008_010, "每日账单详细不存在");
+    ErrorCode J_PUSH_MESSAGE_LOG_NOT_EXISTS = new ErrorCode(1_011_008_011, "极光推送消息日志不存在");
 
 
 }

+ 94 - 0
feifan-module-mall/feifan-module-trade-biz/src/main/java/cn/newfeifan/mall/module/trade/controller/admin/jpushmessagelog/JPushMessageLogController.java

@@ -0,0 +1,94 @@
+package cn.newfeifan.mall.module.trade.controller.admin.jpushmessagelog;
+
+import org.springframework.web.bind.annotation.*;
+import javax.annotation.Resource;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.security.access.prepost.PreAuthorize;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.Operation;
+
+import javax.validation.*;
+import javax.servlet.http.*;
+import java.util.*;
+import java.io.IOException;
+
+import cn.newfeifan.mall.framework.common.pojo.PageParam;
+import cn.newfeifan.mall.framework.common.pojo.PageResult;
+import cn.newfeifan.mall.framework.common.pojo.CommonResult;
+import cn.newfeifan.mall.framework.common.util.object.BeanUtils;
+import static cn.newfeifan.mall.framework.common.pojo.CommonResult.success;
+
+import cn.newfeifan.mall.framework.excel.core.util.ExcelUtils;
+
+import cn.newfeifan.mall.framework.operatelog.core.annotations.OperateLog;
+import static cn.newfeifan.mall.framework.operatelog.core.enums.OperateTypeEnum.*;
+
+import cn.newfeifan.mall.module.trade.controller.admin.jpushmessagelog.vo.*;
+import cn.newfeifan.mall.module.trade.dal.dataobject.jpushmessagelog.JPushMessageLogDO;
+import cn.newfeifan.mall.module.trade.service.jpushmessagelog.JPushMessageLogService;
+
+@Tag(name = "管理后台 - 极光推送记录")
+@RestController
+@RequestMapping("/trade/J-push-message-log")
+@Validated
+public class JPushMessageLogController {
+
+    @Resource
+    private JPushMessageLogService jPushMessageLogService;
+
+    @PostMapping("/create")
+    @Operation(summary = "创建极光推送记录")
+    @PreAuthorize("@ss.hasPermission('trade:J-push-message-log:create')")
+    public CommonResult<Long> createJPushMessageLog(@Valid @RequestBody JPushMessageLogSaveReqVO createReqVO) {
+        return success(jPushMessageLogService.createJPushMessageLog(createReqVO));
+    }
+
+    @PutMapping("/update")
+    @Operation(summary = "更新极光推送记录")
+    @PreAuthorize("@ss.hasPermission('trade:J-push-message-log:update')")
+    public CommonResult<Boolean> updateJPushMessageLog(@Valid @RequestBody JPushMessageLogSaveReqVO updateReqVO) {
+        jPushMessageLogService.updateJPushMessageLog(updateReqVO);
+        return success(true);
+    }
+
+    @DeleteMapping("/delete")
+    @Operation(summary = "删除极光推送记录")
+    @Parameter(name = "id", description = "编号", required = true)
+    @PreAuthorize("@ss.hasPermission('trade:J-push-message-log:delete')")
+    public CommonResult<Boolean> deleteJPushMessageLog(@RequestParam("id") Long id) {
+        jPushMessageLogService.deleteJPushMessageLog(id);
+        return success(true);
+    }
+
+    @GetMapping("/get")
+    @Operation(summary = "获得极光推送记录")
+    @Parameter(name = "id", description = "编号", required = true, example = "1024")
+    @PreAuthorize("@ss.hasPermission('trade:J-push-message-log:query')")
+    public CommonResult<JPushMessageLogRespVO> getJPushMessageLog(@RequestParam("id") Long id) {
+        JPushMessageLogDO jPushMessageLog = jPushMessageLogService.getJPushMessageLog(id);
+        return success(BeanUtils.toBean(jPushMessageLog, JPushMessageLogRespVO.class));
+    }
+
+    @GetMapping("/page")
+    @Operation(summary = "获得极光推送记录分页")
+    @PreAuthorize("@ss.hasPermission('trade:J-push-message-log:query')")
+    public CommonResult<PageResult<JPushMessageLogRespVO>> getJPushMessageLogPage(@Valid JPushMessageLogPageReqVO pageReqVO) {
+        PageResult<JPushMessageLogDO> pageResult = jPushMessageLogService.getJPushMessageLogPage(pageReqVO);
+        return success(BeanUtils.toBean(pageResult, JPushMessageLogRespVO.class));
+    }
+
+    @GetMapping("/export-excel")
+    @Operation(summary = "导出极光推送记录 Excel")
+    @PreAuthorize("@ss.hasPermission('trade:J-push-message-log:export')")
+    @OperateLog(type = EXPORT)
+    public void exportJPushMessageLogExcel(@Valid JPushMessageLogPageReqVO pageReqVO,
+              HttpServletResponse response) throws IOException {
+        pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
+        List<JPushMessageLogDO> list = jPushMessageLogService.getJPushMessageLogPage(pageReqVO).getList();
+        // 导出 Excel
+        ExcelUtils.write(response, "极光推送记录.xls", "数据", JPushMessageLogRespVO.class,
+                        BeanUtils.toBean(list, JPushMessageLogRespVO.class));
+    }
+
+}

+ 43 - 0
feifan-module-mall/feifan-module-trade-biz/src/main/java/cn/newfeifan/mall/module/trade/controller/admin/jpushmessagelog/vo/JPushMessageLogPageReqVO.java

@@ -0,0 +1,43 @@
+package cn.newfeifan.mall.module.trade.controller.admin.jpushmessagelog.vo;
+
+import lombok.*;
+import io.swagger.v3.oas.annotations.media.Schema;
+import cn.newfeifan.mall.framework.common.pojo.PageParam;
+import org.springframework.format.annotation.DateTimeFormat;
+import java.time.LocalDateTime;
+
+import static cn.newfeifan.mall.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
+
+@Schema(description = "管理后台 - 极光推送记录分页 Request VO")
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+public class JPushMessageLogPageReqVO extends PageParam {
+
+    @Schema(description = "接收消息用户的openid", example = "25139")
+    private String regId;
+
+    @Schema(description = "会员用户ID", example = "13890")
+    private Long memberUserId;
+
+    @Schema(description = "系统用户ID", example = "12879")
+    private Long systemUserId;
+
+    @Schema(description = "微信消息模板参数")
+    private String jpushParams;
+
+    @Schema(description = "对应的业务对象ID,如订单ID、售后订单ID", example = "26581")
+    private Long objectId;
+
+    @Schema(description = "发送时间")
+    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
+    private LocalDateTime[] sendTime;
+
+    @Schema(description = "极光返回的消息响应结果")
+    private String responseResult;
+
+    @Schema(description = "创建时间")
+    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
+    private LocalDateTime[] createTime;
+
+}

+ 49 - 0
feifan-module-mall/feifan-module-trade-biz/src/main/java/cn/newfeifan/mall/module/trade/controller/admin/jpushmessagelog/vo/JPushMessageLogRespVO.java

@@ -0,0 +1,49 @@
+package cn.newfeifan.mall.module.trade.controller.admin.jpushmessagelog.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.*;
+import java.time.LocalDateTime;
+import com.alibaba.excel.annotation.*;
+
+@Schema(description = "管理后台 - 极光推送记录 Response VO")
+@Data
+@ExcelIgnoreUnannotated
+public class JPushMessageLogRespVO {
+
+    @Schema(description = "消息记录ID,自增主键", requiredMode = Schema.RequiredMode.REQUIRED, example = "22852")
+    @ExcelProperty("消息记录ID,自增主键")
+    private Long id;
+
+    @Schema(description = "接收消息用户的openid", requiredMode = Schema.RequiredMode.REQUIRED, example = "25139")
+    @ExcelProperty("接收消息用户的openid")
+    private String regId;
+
+    @Schema(description = "会员用户ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "13890")
+    @ExcelProperty("会员用户ID")
+    private Long memberUserId;
+
+    @Schema(description = "系统用户ID", example = "12879")
+    @ExcelProperty("系统用户ID")
+    private Long systemUserId;
+
+    @Schema(description = "微信消息模板参数")
+    @ExcelProperty("微信消息模板参数")
+    private String jpushParams;
+
+    @Schema(description = "对应的业务对象ID,如订单ID、售后订单ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "26581")
+    @ExcelProperty("对应的业务对象ID,如订单ID、售后订单ID")
+    private Long objectId;
+
+    @Schema(description = "发送时间")
+    @ExcelProperty("发送时间")
+    private LocalDateTime sendTime;
+
+    @Schema(description = "极光返回的消息响应结果")
+    @ExcelProperty("极光返回的消息响应结果")
+    private String responseResult;
+
+    @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
+    @ExcelProperty("创建时间")
+    private LocalDateTime createTime;
+
+}

+ 40 - 0
feifan-module-mall/feifan-module-trade-biz/src/main/java/cn/newfeifan/mall/module/trade/controller/admin/jpushmessagelog/vo/JPushMessageLogSaveReqVO.java

@@ -0,0 +1,40 @@
+package cn.newfeifan.mall.module.trade.controller.admin.jpushmessagelog.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.*;
+import javax.validation.constraints.*;
+import java.time.LocalDateTime;
+
+@Schema(description = "管理后台 - 极光推送记录新增/修改 Request VO")
+@Data
+@Builder
+public class JPushMessageLogSaveReqVO {
+
+    @Schema(description = "消息记录ID,自增主键", requiredMode = Schema.RequiredMode.REQUIRED, example = "22852")
+    private Long id;
+
+    @Schema(description = "接收消息用户的openid", requiredMode = Schema.RequiredMode.REQUIRED, example = "25139")
+    @NotEmpty(message = "接收消息用户的openid不能为空")
+    private String regId;
+
+    @Schema(description = "会员用户ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "13890")
+    @NotNull(message = "会员用户ID不能为空")
+    private Long memberUserId;
+
+    @Schema(description = "系统用户ID", example = "12879")
+    private Long systemUserId;
+
+    @Schema(description = "微信消息模板参数")
+    private String jpushParams;
+
+    @Schema(description = "对应的业务对象ID,如订单ID、售后订单ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "26581")
+    @NotNull(message = "对应的业务对象ID,如订单ID、售后订单ID不能为空")
+    private Long objectId;
+
+    @Schema(description = "发送时间")
+    private LocalDateTime sendTime;
+
+    @Schema(description = "极光返回的消息响应结果")
+    private String responseResult;
+
+}

+ 57 - 0
feifan-module-mall/feifan-module-trade-biz/src/main/java/cn/newfeifan/mall/module/trade/dal/dataobject/jpushmessagelog/JPushMessageLogDO.java

@@ -0,0 +1,57 @@
+package cn.newfeifan.mall.module.trade.dal.dataobject.jpushmessagelog;
+
+import lombok.*;
+import java.time.LocalDateTime;
+import com.baomidou.mybatisplus.annotation.*;
+import cn.newfeifan.mall.framework.mybatis.core.dataobject.BaseDO;
+
+/**
+ * 极光推送记录 DO
+ *
+ * @author 非繁人
+ */
+@TableName("jpush_message_log")
+@KeySequence("jpush_message_log_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class JPushMessageLogDO extends BaseDO {
+
+    /**
+     * 消息记录ID,自增主键
+     */
+    @TableId
+    private Long id;
+    /**
+     * 接收消息用户的openid
+     */
+    private String regId;
+    /**
+     * 会员用户ID
+     */
+    private Long memberUserId;
+    /**
+     * 系统用户ID
+     */
+    private Long systemUserId;
+    /**
+     * 微信消息模板参数
+     */
+    private String jpushParams;
+    /**
+     * 对应的业务对象ID,如订单ID、售后订单ID
+     */
+    private Long objectId;
+    /**
+     * 发送时间
+     */
+    private LocalDateTime sendTime;
+    /**
+     * 极光返回的消息响应结果
+     */
+    private String responseResult;
+
+}

+ 32 - 0
feifan-module-mall/feifan-module-trade-biz/src/main/java/cn/newfeifan/mall/module/trade/dal/mysql/jpushmessagelog/JPushMessageLogMapper.java

@@ -0,0 +1,32 @@
+package cn.newfeifan.mall.module.trade.dal.mysql.jpushmessagelog;
+
+
+import cn.newfeifan.mall.framework.common.pojo.PageResult;
+import cn.newfeifan.mall.framework.mybatis.core.query.LambdaQueryWrapperX;
+import cn.newfeifan.mall.framework.mybatis.core.mapper.BaseMapperX;
+import cn.newfeifan.mall.module.trade.dal.dataobject.jpushmessagelog.JPushMessageLogDO;
+import org.apache.ibatis.annotations.Mapper;
+import cn.newfeifan.mall.module.trade.controller.admin.jpushmessagelog.vo.*;
+
+/**
+ * 极光推送记录 Mapper
+ *
+ * @author 非繁人
+ */
+@Mapper
+public interface JPushMessageLogMapper extends BaseMapperX<JPushMessageLogDO> {
+
+    default PageResult<JPushMessageLogDO> selectPage(JPushMessageLogPageReqVO reqVO) {
+        return selectPage(reqVO, new LambdaQueryWrapperX<JPushMessageLogDO>()
+                .eqIfPresent(JPushMessageLogDO::getRegId, reqVO.getRegId())
+                .eqIfPresent(JPushMessageLogDO::getMemberUserId, reqVO.getMemberUserId())
+                .eqIfPresent(JPushMessageLogDO::getSystemUserId, reqVO.getSystemUserId())
+                .eqIfPresent(JPushMessageLogDO::getJpushParams, reqVO.getJpushParams())
+                .eqIfPresent(JPushMessageLogDO::getObjectId, reqVO.getObjectId())
+                .betweenIfPresent(JPushMessageLogDO::getSendTime, reqVO.getSendTime())
+                .eqIfPresent(JPushMessageLogDO::getResponseResult, reqVO.getResponseResult())
+                .betweenIfPresent(JPushMessageLogDO::getCreateTime, reqVO.getCreateTime())
+                .orderByDesc(JPushMessageLogDO::getId));
+    }
+
+}

+ 46 - 0
feifan-module-mall/feifan-module-trade-biz/src/main/java/cn/newfeifan/mall/module/trade/service/aftersale/AfterSaleServiceImpl.java

@@ -16,6 +16,7 @@ import cn.newfeifan.mall.module.distri.enums.CaclEnum;
 import cn.newfeifan.mall.module.distri.service.dailybill.DailyBillService;
 import cn.newfeifan.mall.module.distri.service.integral.IntegralService;
 import cn.newfeifan.mall.module.distri.service.ptdailybill.PtDailyBillService;
+import cn.newfeifan.mall.module.member.service.user.MemberUserService;
 import cn.newfeifan.mall.module.pay.api.refund.PayRefundApi;
 import cn.newfeifan.mall.module.pay.api.refund.dto.PayRefundCreateReqDTO;
 import cn.newfeifan.mall.module.system.controller.admin.user.vo.user.UserShopDetailsVO;
@@ -48,6 +49,8 @@ import cn.newfeifan.mall.module.trade.framework.order.config.TradeOrderPropertie
 import cn.newfeifan.mall.module.trade.service.delivery.DeliveryExpressService;
 import cn.newfeifan.mall.module.trade.service.order.TradeOrderQueryService;
 import cn.newfeifan.mall.module.trade.service.order.TradeOrderUpdateService;
+import cn.newfeifan.mall.module.trade.utils.push.JPushUtils;
+import cn.newfeifan.mall.module.trade.utils.push.pojo.RequestBody;
 import cn.newfeifan.mall.module.trade.utils.wechat.WcChatMessageUtils;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.google.common.collect.Maps;
@@ -107,6 +110,10 @@ public class AfterSaleServiceImpl implements AfterSaleService {
 
     @Resource
     private WcChatMessageUtils wcChatMessageUtils;
+    @Resource
+    private JPushUtils jPushUtils;
+    @Resource
+    private MemberUserService memberUserService;
 
     @Resource
     private DailyBillService dailyBillService;
@@ -243,7 +250,10 @@ public class AfterSaleServiceImpl implements AfterSaleService {
 
         // TODO 发送售后消息
         if (afterSale.getWay().equals(AfterSaleWayEnum.RETURN_AND_REFUND.getWay())) {
+            // 发送微信消息
             sentWcChatMessage(afterSale, AfterSaleStatusEnum.SELLER_AGREE.getContent());
+            // 发送极光推送
+            sentJPush(afterSale, AfterSaleStatusEnum.SELLER_AGREE.getContent());
         }
     }
 
@@ -266,6 +276,9 @@ public class AfterSaleServiceImpl implements AfterSaleService {
         // 发送给微信用户售后消息
         sentWcChatMessage(afterSale, AfterSaleStatusEnum.SELLER_DISAGREE.getContent());
 
+        // 发送极光推送
+        sentJPush(afterSale, AfterSaleStatusEnum.SELLER_DISAGREE.getContent());
+
         // 更新交易订单项的售后状态为【未申请】
         tradeOrderUpdateService.updateOrderItemWhenAfterSaleCancel(afterSale.getOrderItemId());
     }
@@ -367,6 +380,9 @@ public class AfterSaleServiceImpl implements AfterSaleService {
         // TODO 发送售后消息
         sentWcChatMessage(afterSale, AfterSaleStatusEnum.SELLER_REFUSE.getContent());
 
+        // 发送极光推送
+        sentJPush(afterSale, AfterSaleStatusEnum.SELLER_REFUSE.getContent());
+
         // 更新交易订单项的售后状态为【未申请】
         tradeOrderUpdateService.updateOrderItemWhenAfterSaleCancel(afterSale.getOrderItemId());
     }
@@ -514,6 +530,9 @@ public class AfterSaleServiceImpl implements AfterSaleService {
         // 发送给微信用户售后消息
         sentWcChatMessage(afterSale, AfterSaleStatusEnum.COMPLETE.getContent());
 
+        // 发送极光推送
+        sentJPush(afterSale, AfterSaleStatusEnum.COMPLETE.getContent());
+
         // 更新交易订单项的售后状态为【已完成】
         tradeOrderUpdateService.updateOrderItemWhenAfterSaleSuccess(afterSale.getOrderItemId(), afterSale.getRefundPrice());
     }
@@ -540,6 +559,33 @@ public class AfterSaleServiceImpl implements AfterSaleService {
                 params, null, afterSale.getUserId(), afterSale.getId());
     }
 
+    /**
+     * 发送极光推送消息
+     *
+     * @param afterSale   售后订单
+     * @param afterStatus 是否同意退款
+     */
+    private void sentJPush(AfterSaleDO afterSale, String afterStatus) {
+        String title = WcChatMessageTemplateIdEnum.AFTER_SALE_CHECK2.getName();
+
+        String alert = "售后订单号: " + afterSale.getNo();
+        alert += "\n商品名称: " + afterSale.getSpuName();
+        DecimalFormat df = new DecimalFormat("0.00");
+        String formattedPrice = df.format((double) afterSale.getRefundPrice() / 100);
+        alert += "\n金额: ¥ " + formattedPrice;
+
+        alert += "\n售后状态: " + afterStatus;
+        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
+        alert += "\n时间: " + LocalDateTime.now().format(formatter);
+
+        List<String> userRsgIds = memberUserService.getUserRsgIds(null, afterSale.getUserId());
+        if (userRsgIds != null) {
+            String url = jPushUtils.getUrl(title, afterSale.getUserId(), afterSale.getId());
+            RequestBody requestBody = jPushUtils.getRequestBody(userRsgIds, alert, title, url);
+            jPushUtils.jPush(requestBody, afterSale.getUserId(), null, afterSale.getId());
+        }
+    }
+
     private void createPayRefund(String userIp, AfterSaleDO afterSale) {
 //        TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
 

+ 53 - 0
feifan-module-mall/feifan-module-trade-biz/src/main/java/cn/newfeifan/mall/module/trade/service/jpushmessagelog/JPushMessageLogService.java

@@ -0,0 +1,53 @@
+package cn.newfeifan.mall.module.trade.service.jpushmessagelog;
+
+import javax.validation.*;
+import cn.newfeifan.mall.module.trade.controller.admin.jpushmessagelog.vo.*;
+import cn.newfeifan.mall.module.trade.dal.dataobject.jpushmessagelog.JPushMessageLogDO;
+import cn.newfeifan.mall.framework.common.pojo.PageResult;
+
+/**
+ * 极光推送记录 Service 接口
+ *
+ * @author 非繁人
+ */
+public interface JPushMessageLogService {
+
+    /**
+     * 创建极光推送记录
+     *
+     * @param createReqVO 创建信息
+     * @return 编号
+     */
+    Long createJPushMessageLog(@Valid JPushMessageLogSaveReqVO createReqVO);
+
+    /**
+     * 更新极光推送记录
+     *
+     * @param updateReqVO 更新信息
+     */
+    void updateJPushMessageLog(@Valid JPushMessageLogSaveReqVO updateReqVO);
+
+    /**
+     * 删除极光推送记录
+     *
+     * @param id 编号
+     */
+    void deleteJPushMessageLog(Long id);
+
+    /**
+     * 获得极光推送记录
+     *
+     * @param id 编号
+     * @return 极光推送记录
+     */
+    JPushMessageLogDO getJPushMessageLog(Long id);
+
+    /**
+     * 获得极光推送记录分页
+     *
+     * @param pageReqVO 分页查询
+     * @return 极光推送记录分页
+     */
+    PageResult<JPushMessageLogDO> getJPushMessageLogPage(JPushMessageLogPageReqVO pageReqVO);
+
+}

+ 70 - 0
feifan-module-mall/feifan-module-trade-biz/src/main/java/cn/newfeifan/mall/module/trade/service/jpushmessagelog/JPushMessageLogServiceImpl.java

@@ -0,0 +1,70 @@
+package cn.newfeifan.mall.module.trade.service.jpushmessagelog;
+
+import org.springframework.stereotype.Service;
+import javax.annotation.Resource;
+import org.springframework.validation.annotation.Validated;
+import cn.newfeifan.mall.module.trade.controller.admin.jpushmessagelog.vo.*;
+import cn.newfeifan.mall.module.trade.dal.dataobject.jpushmessagelog.JPushMessageLogDO;
+import cn.newfeifan.mall.framework.common.pojo.PageResult;
+import cn.newfeifan.mall.framework.common.util.object.BeanUtils;
+
+import cn.newfeifan.mall.module.trade.dal.mysql.jpushmessagelog.JPushMessageLogMapper;
+
+import static cn.newfeifan.mall.framework.common.exception.util.ServiceExceptionUtil.exception;
+import static cn.newfeifan.mall.module.trade.enums.ErrorCodeConstants.*;
+
+/**
+ * 极光推送记录 Service 实现类
+ *
+ * @author 非繁人
+ */
+@Service
+@Validated
+public class JPushMessageLogServiceImpl implements JPushMessageLogService {
+
+    @Resource
+    private JPushMessageLogMapper jPushMessageLogMapper;
+
+    @Override
+    public Long createJPushMessageLog(JPushMessageLogSaveReqVO createReqVO) {
+        // 插入
+        JPushMessageLogDO jPushMessageLog = BeanUtils.toBean(createReqVO, JPushMessageLogDO.class);
+        jPushMessageLogMapper.insert(jPushMessageLog);
+        // 返回
+        return jPushMessageLog.getId();
+    }
+
+    @Override
+    public void updateJPushMessageLog(JPushMessageLogSaveReqVO updateReqVO) {
+        // 校验存在
+        validateJPushMessageLogExists(updateReqVO.getId());
+        // 更新
+        JPushMessageLogDO updateObj = BeanUtils.toBean(updateReqVO, JPushMessageLogDO.class);
+        jPushMessageLogMapper.updateById(updateObj);
+    }
+
+    @Override
+    public void deleteJPushMessageLog(Long id) {
+        // 校验存在
+        validateJPushMessageLogExists(id);
+        // 删除
+        jPushMessageLogMapper.deleteById(id);
+    }
+
+    private void validateJPushMessageLogExists(Long id) {
+        if (jPushMessageLogMapper.selectById(id) == null) {
+            throw exception(J_PUSH_MESSAGE_LOG_NOT_EXISTS);
+        }
+    }
+
+    @Override
+    public JPushMessageLogDO getJPushMessageLog(Long id) {
+        return jPushMessageLogMapper.selectById(id);
+    }
+
+    @Override
+    public PageResult<JPushMessageLogDO> getJPushMessageLogPage(JPushMessageLogPageReqVO pageReqVO) {
+        return jPushMessageLogMapper.selectPage(pageReqVO);
+    }
+
+}

+ 47 - 2
feifan-module-mall/feifan-module-trade-biz/src/main/java/cn/newfeifan/mall/module/trade/service/order/TradeOrderUpdateServiceImpl.java

@@ -31,6 +31,7 @@ import cn.newfeifan.mall.module.distri.service.ptprofitlog.PtProfitLogService;
 import cn.newfeifan.mall.module.distri.service.sharepath.SharePathService;
 import cn.newfeifan.mall.module.member.api.address.MemberAddressApi;
 import cn.newfeifan.mall.module.member.api.address.dto.MemberAddressRespDTO;
+import cn.newfeifan.mall.module.member.service.user.MemberUserService;
 import cn.newfeifan.mall.module.pay.api.order.PayOrderApi;
 import cn.newfeifan.mall.module.pay.api.order.dto.PayOrderCreateReqDTO;
 import cn.newfeifan.mall.module.pay.api.order.dto.PayOrderRespDTO;
@@ -70,6 +71,8 @@ import cn.newfeifan.mall.module.trade.service.price.TradePriceService;
 import cn.newfeifan.mall.module.trade.service.price.bo.TradePriceCalculateReqBO;
 import cn.newfeifan.mall.module.trade.service.price.bo.TradePriceCalculateRespBO;
 import cn.newfeifan.mall.module.trade.service.price.calculator.TradePriceCalculatorHelper;
+import cn.newfeifan.mall.module.trade.utils.push.JPushUtils;
+import cn.newfeifan.mall.module.trade.utils.push.pojo.RequestBody;
 import cn.newfeifan.mall.module.trade.utils.wechat.WcChatMessageUtils;
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.google.gson.Gson;
@@ -164,6 +167,11 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
     @Resource
     private OrderPercentageMapper orderPercentageMapper;
 
+    @Resource
+    private JPushUtils jPushUtils;
+    @Resource
+    private MemberUserService memberUserService;
+
     // =================== Order ===================
 
     @Override
@@ -455,12 +463,25 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
         tradeMessageService.sendMessageWhenDeliveryOrder(new TradeOrderMessageWhenDeliveryOrderReqBO()
                 .setOrderId(order.getId()).setUserId(order.getUserId()).setMessage(null));
         //5.发送微信模板消息
+        sengWeChatMessage(deliveryReqVO, order);
+
+        //6. 发送极光推送
+        sendJPush(deliveryReqVO, order);
+    }
+
+    /**
+     * 发送微信模板消息
+     *
+     * @param deliveryReqVO 发货请求信息
+     * @param order         订单
+     */
+    private void sengWeChatMessage(TradeOrderDeliveryReqVO deliveryReqVO, TradeOrderDO order) {
         List<String> params = new ArrayList<>();
         params.add(order.getNo());
         if (deliveryReqVO.getAttachment() != null) {
             params.add("无");
             params.add("-");
-        }else{
+        } else {
             params.add(deliveryExpressService.getDeliveryNameById(order.getLogisticsId()));
             params.add(order.getLogisticsNo());
         }
@@ -471,6 +492,30 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
                 params, null, order.getUserId(), order.getId());
     }
 
+    private void sendJPush(TradeOrderDeliveryReqVO deliveryReqVO, TradeOrderDO order) {
+        String title = WcChatMessageTemplateIdEnum.ORDER_DELIVERY2.getName();
+
+        String alert = "订单号: " + order.getNo();
+        if (deliveryReqVO.getAttachment() != null) {
+            alert += "\n快递公司: 无";
+            alert += "\n快递编号: -";
+        } else {
+            alert += "\n快递公司: " + deliveryExpressService.getDeliveryNameById(order.getLogisticsId());
+            alert += "\n快递编号: " + order.getLogisticsNo();
+        }
+
+        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
+        alert += "\n时间: " + LocalDateTime.now().format(formatter);
+        alert += "\n产品名称: " + tradeOrderItemMapper.getSupNameByOrderId(order.getId());
+
+        List<String> userRsgIds = memberUserService.getUserRsgIds(null, order.getUserId());
+        if (userRsgIds != null) {
+            String url = jPushUtils.getUrl(title, order.getUserId(), order.getId());
+            RequestBody requestBody = jPushUtils.getRequestBody(userRsgIds, alert, title, url);
+            jPushUtils.jPush(requestBody, order.getUserId(), null, order.getId());
+        }
+    }
+
     /**
      * 订阅物流信息
      *
@@ -680,7 +725,7 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
                 if (payIntegral > 0) {
                     integralService.updateUserIntegral(userId, CaclEnum.ORDER_CANCEL_BY_SYSTEM_REFUND_INTEGRAL, payIntegral, tradeOrderId, orderNum);
                 }
-                if(order.getPayConsumptionPoints() > 0){
+                if (order.getPayConsumptionPoints() > 0) {
                     integralService.updateUserConsumptionPoints(userId, ConsumptionEnum.ORDER_CANCEL_BY_SYSTEM_REFUND_INTEGRAL, order.getPayConsumptionPoints(), tradeOrderId, orderNum);
                 }
 

+ 177 - 0
feifan-module-mall/feifan-module-trade-biz/src/main/java/cn/newfeifan/mall/module/trade/utils/push/JPushUtils.java

@@ -0,0 +1,177 @@
+package cn.newfeifan.mall.module.trade.utils.push;
+
+import cn.hutool.core.codec.Base64;
+import cn.hutool.json.JSONObject;
+import cn.hutool.json.JSONUtil;
+import cn.newfeifan.mall.module.member.service.user.MemberUserService;
+import cn.newfeifan.mall.module.system.dal.dataobject.config.SystemConfigDO;
+import cn.newfeifan.mall.module.system.service.config.SystemConfigService;
+import cn.newfeifan.mall.module.trade.controller.admin.jpushmessagelog.vo.JPushMessageLogSaveReqVO;
+import cn.newfeifan.mall.module.trade.enums.wxmessage.WcChatMessageTemplateIdEnum;
+import cn.newfeifan.mall.module.trade.service.jpushmessagelog.JPushMessageLogService;
+import cn.newfeifan.mall.module.trade.utils.push.pojo.NotificationData;
+import cn.newfeifan.mall.module.trade.utils.push.pojo.Options;
+import cn.newfeifan.mall.module.trade.utils.push.pojo.RequestBody;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.http.HttpEntity;
+import org.apache.http.client.config.RequestConfig;
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.entity.ContentType;
+import org.apache.http.entity.StringEntity;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClients;
+import org.apache.http.util.EntityUtils;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.Resource;
+import java.time.LocalDateTime;
+import java.util.List;
+
+@Component
+@Slf4j
+public class JPushUtils {
+    @Value("${jpush.app-key}")
+    private String appKey;
+    @Value("${jpush.secret}")
+    private String secret;
+
+    @Resource
+    private JPushMessageLogService jPushService;
+    @Resource
+    private SystemConfigService configService;
+    @Resource
+    private MemberUserService memberUserService;
+
+    /**
+     * 获取请求体
+     *
+     * @param rsgIds 极光注册id
+     * @param alert  推送内容
+     * @param title  推送标题
+     * @return 请求体
+     */
+    public RequestBody getRequestBody(List<String> rsgIds, String alert, String title, String url) {
+        RequestBody requestBody = RequestBody.builder().build();
+        requestBody.setPlatform("all");
+        requestBody.setAudience(new RequestBody.Audience().setRegistration_id(rsgIds));
+
+        NotificationData notificationData = new NotificationData();
+        notificationData.setAlert("Hello, content!");
+        NotificationData.Notification notification = new NotificationData.Notification();
+        notification.setAlert(alert);
+        notification.setTitle(title);
+        notification.setBuilder_id(1);
+        notification.setIntent(new NotificationData.Intent().setUrl("letcgo://gizmos?" + url));
+
+        notificationData.setAndroid(notification);
+        requestBody.setNotification(notificationData);
+
+        Options options = Options.builder()
+                .time_to_live(86400)
+                .classification(1)
+                .third_party_channel(Options.ThirdPartyChannel
+                        .builder()
+                        .huawei(Options.Channel
+                                .builder()
+                                .importance("NORMAL")
+                                .category("WORK")
+                                .build())
+                        .xiaomi(Options.Channel
+                                .builder()
+                                .channel_id("NORMAL")
+                                .build())
+                        .honor(Options.Channel
+                                .builder()
+                                .importance("NORMAL")
+                                .build())
+                        .oppo(Options.Channel
+                                .builder()
+                                .channel_id("message")
+                                .build())
+                        .vivo(Options.Channel
+                                .builder()
+                                .distribution("secondary_push")
+                                .category("IM")
+                                .build())
+                        .build())
+                .build();
+        requestBody.setOptions(options);
+        return requestBody;
+    }
+
+    /**
+     * 获取url
+     *
+     * @param title        标题
+     * @param memberUserId 用户id
+     * @param objectId     对象id
+     * @return url
+     */
+    public String getUrl(String title, Long memberUserId, Long objectId) {
+        SystemConfigDO redisConfig = configService.getRedisConfig();
+        String username = memberUserService.getUsernameByUserId(memberUserId);
+        if (title.equals(WcChatMessageTemplateIdEnum.ORDER_DELIVERY2.getName())) {
+            //订单已发货
+            return redisConfig.getMallDomain() + "/#/pages/order/detail?id=" + objectId + "&username=" + username;
+        } else if (title.equals(WcChatMessageTemplateIdEnum.AFTER_SALE_CHECK2.getName())) {
+            //售后订单审核完成
+            return redisConfig.getMallDomain() + "/#/pages/order/aftersale/detail?id=" + objectId + "&username=" + username;
+        } else if (title.equals(WcChatMessageTemplateIdEnum.AFTER_SALE_NO_CHECK2.getName())) {
+            //售后订单待审核
+            return redisConfig.getMerchantDomain() + "/after-sale";
+        } else if (title.equals(WcChatMessageTemplateIdEnum.ORDER_NO_DELIVERY2.getName())) {
+            //订单待发货
+            return redisConfig.getMerchantDomain() + "/order";
+        }
+        return null;
+    }
+
+    /**
+     * 推送消息
+     */
+    public void jPush(RequestBody requestBody, Long memberUserId, Long systemUserId, Long objectId) {
+        String url = "https://api.jpush.cn/v3/push";
+
+        String authString = appKey + ":" + secret;
+        String encodedAuthString = Base64.encode(authString);
+
+
+        try {
+            RequestConfig clientConfig = RequestConfig.custom().setConnectionRequestTimeout(30000).setSocketTimeout(30000).setConnectTimeout(30000).build();
+            CloseableHttpClient client = HttpClients.custom().setDefaultRequestConfig(clientConfig).build();
+
+            HttpPost httpPost = new HttpPost(url);
+            httpPost.setHeader("Authorization", "Basic " + encodedAuthString);
+            httpPost.setHeader("Content-Type", "application/json");
+            JSONObject message = JSONUtil.parseObj(JSONUtil.toJsonStr(requestBody));
+            httpPost.setEntity(new StringEntity(message.toString(), ContentType.APPLICATION_JSON));
+
+            CloseableHttpResponse execute = client.execute(httpPost);
+
+            int statusCode = execute.getStatusLine().getStatusCode();
+            log.info("请求状态:{}", statusCode);
+
+            HttpEntity entity = execute.getEntity();
+            String str = EntityUtils.toString(entity);
+            log.info("请求结果:{}", str);
+
+            // 记录推送日志
+            JPushMessageLogSaveReqVO jPushMessageLogSaveReqVO = JPushMessageLogSaveReqVO.builder()
+                    .regId(JSONUtil.toJsonStr(requestBody.getAudience().getRegistration_id()))
+                    .memberUserId(memberUserId)
+                    .systemUserId(systemUserId)
+                    .objectId(objectId)
+                    .jpushParams(message.toString())
+                    .sendTime(LocalDateTime.now())
+                    .responseResult(str)
+                    .build();
+            jPushService.createJPushMessageLog(jPushMessageLogSaveReqVO);
+        } catch (Exception e) {
+            log.info("用户rsgId:{}", JSONUtil.toJsonStr(requestBody.getAudience().getRegistration_id()));
+            log.info("推送异常:{}", e.getMessage());
+            throw new RuntimeException(e);
+        }
+    }
+}

+ 34 - 0
feifan-module-mall/feifan-module-trade-biz/src/main/java/cn/newfeifan/mall/module/trade/utils/push/pojo/Callback.java

@@ -0,0 +1,34 @@
+package cn.newfeifan.mall.module.trade.utils.push.pojo;
+
+import lombok.Data;
+
+import java.util.Map;
+
+/**
+ * 回调参数
+ */
+
+@Data
+public class Callback {
+    /**
+     * 数据临时回调地址
+     * 指定后以此处指定为准,仅针对这一次推送请求生效。
+     * 不指定,则以极光后台配置为准。
+     */
+    private String url;
+    /**
+     * 需要回调给用户的自定义参数
+     */
+    private Map<String, String> params;
+    /**
+     * 回调数据类型。
+     * 1: 送达回执
+     * 2: 点击回执
+     * 3: 送达和点击回执
+     * 8: 推送成功回执
+     * 9: 成功和送达回执
+     * 10: 成功和点击回执
+     * 11: 成功和送达以及点击回执
+     */
+    private int type;
+}

+ 33 - 0
feifan-module-mall/feifan-module-trade-biz/src/main/java/cn/newfeifan/mall/module/trade/utils/push/pojo/Message.java

@@ -0,0 +1,33 @@
+package cn.newfeifan.mall.module.trade.utils.push.pojo;
+
+import lombok.Data;
+
+import java.util.Map;
+
+/**
+ * 自定义消息
+ */
+
+@Data
+public class Message {
+    /**
+     * 消息内容本身
+     * 必填
+     */
+    private String msg_content;
+    /**
+     * 消息标题
+     * 可选
+     */
+    private String title;
+    /**
+     * 消息内容类型,开发者可根据自身业务定义具体类型。
+     * 可选
+     */
+    private String content_type;
+    /**
+     * JSON 格式的可选参数
+     * 可选
+     */
+    private Map<String, String> extras;
+}

+ 64 - 0
feifan-module-mall/feifan-module-trade-biz/src/main/java/cn/newfeifan/mall/module/trade/utils/push/pojo/NotificationData.java

@@ -0,0 +1,64 @@
+package cn.newfeifan.mall.module.trade.utils.push.pojo;
+
+import lombok.Data;
+
+/**
+ * 通知类
+ */
+
+@Data
+//@Builder
+public class NotificationData {
+    /**
+     * 通知的内容在各个平台上,都可能只有这一个最基本的属性 "alert"。
+     * 这个位置的 "alert" 属性(直接在 notification 对象下),是一个快捷定义,各平台的 alert 信息如果都一样,则以此定义为准。如果各平台有定义,则覆盖这里的定义。
+     */
+    private String alert;
+    /**
+     * android 平台
+     */
+    private Notification android;
+    /**
+     * ios 平台
+     */
+    private Notification ios;
+    /**
+     * hmos 平台
+     */
+    private Notification hmos;
+
+
+    @Data
+    public static class Intent {
+        private String url;
+    }
+
+    @Data
+    public static class Extras {
+        private int newsid;
+    }
+
+    @Data
+    public static class Notification {
+        /**
+         * 通知内容
+         */
+        private String alert;
+        /**
+         * 通知标题
+         */
+        private String title;
+        /**
+         * 通知样式ID
+         */
+        private int builder_id;
+        /**
+         * 指定跳转页面
+         */
+        private Intent intent;
+        /**
+         * 扩展字段(基本不用)
+         */
+        private Extras extras;
+    }
+}

+ 90 - 0
feifan-module-mall/feifan-module-trade-biz/src/main/java/cn/newfeifan/mall/module/trade/utils/push/pojo/Options.java

@@ -0,0 +1,90 @@
+package cn.newfeifan.mall.module.trade.utils.push.pojo;
+
+import lombok.Builder;
+import lombok.Data;
+
+/**
+ * 可选参数类
+ */
+
+@Data
+@Builder
+public class Options {
+    /**
+     * 离线消息保留时长 (秒)
+     * 推送当前用户不在线时,为该用户保留多长时间的离线消息,以便其上线时再次推送。
+     * 默认 86400 (1 天),普通用户最长 3 天, VIP 用户最长 10 天。设置为 0 表示不保留离线消息,只有推送当前在线的用户可以收到。
+     * 该字段对 iOS 的 Notification 消息无效。
+     */
+    private int time_to_live;
+
+    /**
+     * APNs 是否生产环境
+     * 该字段仅对 iOS 的 Notification 有效,如果不指定则为推送生产环境。注意:JPush 服务端 SDK 默认设置为推送 “开发环境”。
+     * true:表示推送生产环境。
+     * false:表示推送开发环境。
+     */
+    private boolean apns_production;
+
+    /**
+     * 消息类型分类
+     * 	极光不对指定的消息类型进行判断或校准,会以开发者自行指定的消息类型适配 Android 厂商通道。不填默认为 0。
+     * 0:代表运营消息。
+     * 1:代表系统消息。
+     * 此字段优先级最高,会覆盖 options.third_party_channel.vivo.classification 设置的值。
+     */
+    private int classification;
+
+    /**
+     * 推送请求下发通道
+     * 仅针对配置了厂商用户使用有效
+     */
+    private ThirdPartyChannel third_party_channel;
+
+    /**
+     * 厂商通道类
+     * key 只支持 xiaomi、huawei、honor、meizu、oppo、vivo、fcm 类型用户。 key 可以为上述 7 个类型中的其中一个或者多个同时存在,未传递的 key 其对应的厂商下发走默认下发逻辑。
+     */
+    @Data
+    @Builder
+    public static class ThirdPartyChannel {
+        private Channel huawei;
+        private Channel xiaomi;
+        private Channel honor;
+        private Channel oppo;
+        private Channel vivo;
+    }
+
+    @Data
+    @Builder
+    public static class Channel{
+        /**
+         * 华为、荣耀通知栏消息智能分类
+         * LOW:一般消息。
+         * NORMAL:重要消息。
+         * HIGH:非常重要消息(仅华为支持)。
+         */
+        private String importance;
+        /**
+         * 华为、vivo 厂商消息场景标识
+         * 华为的参数值参考文档: <a href="https://developer.huawei.com/consumer/cn/doc/HMSCore-Guides/message-classification-0000001149358835#section1085395991513">...</a>
+         */
+        private String category;
+
+        /**
+         * 小米推送平台登记的 channel id
+         * OPPO 官网登记的通道 ID
+         */
+        private String channel_id;
+
+        /**
+         * 通知栏消息下发逻辑
+         * first_ospush(VIP):成功注册厂商通道的设备走厂商通道,仅注册极光通道的设备走极光通道。
+         * ospush(VIP):表示推送强制走厂商通道下发。 需要特别注意,只要指定此值的厂商对应配额不够时,推送请求会失败,返回 1012 错误码。
+         * 举例:假设指定一个小米用户的 RegistrationID 推送,请求时针对小米、OPPO 等厂商通道都指定了“ospush”,且 OPPO 厂商通道都配额已经用完,则推送同样会返回 1012 错误,提示厂商配额不足。
+         * jpush:表示推送强制走极光通道下发。
+         * secondary_push:表示推送优先走极光,极光不在线再走厂商,厂商作为辅助(建议此种方式)。
+         */
+        private String distribution;
+    }
+}

+ 56 - 0
feifan-module-mall/feifan-module-trade-biz/src/main/java/cn/newfeifan/mall/module/trade/utils/push/pojo/RequestBody.java

@@ -0,0 +1,56 @@
+package cn.newfeifan.mall.module.trade.utils.push.pojo;
+
+import lombok.Builder;
+import lombok.Data;
+
+import java.util.List;
+
+/**
+ * 推送通知类
+ */
+
+@Data
+@Builder
+public class RequestBody {
+    /**
+     * 推送平台
+     * "all"就是所有
+     * {"platform" : ["android", "ios","quickapp","hmos"] },这是指定
+     */
+    private String platform;
+    /**
+     * 推送目标
+     * "all" 全部,"tag" 标签,"registration_id" 注册id,"alias" 别名
+     */
+    private Audience audience;
+    /**
+     * 通知
+     */
+    private NotificationData notification;
+    /**
+     * 通知的内容在各个平台上,都可能只有这一个最基本的属性 "alert"。
+     * 这个位置的 "alert" 属性(直接在 notification 对象下),是一个快捷定义,各平台的 alert 信息如果都一样,则以此定义为准。如果各平台有定义,则覆盖这里的定义。
+     */
+    private String alert;
+    /**
+     * 自定义消息
+     */
+    private Message message;
+    /**
+     * 可选参数
+     */
+    private Options options;
+    /**
+     * 回调参数
+     */
+    private Callback callback;
+
+    @Data
+    public static class Audience {
+        /**
+         * 用户注册的极光id
+         * 还有其他很多类型的选择,tag,alias,live_activity_id等等,不过我们项目就只用到这个
+         */
+        private List<String> registration_id;
+    }
+}

+ 12 - 0
feifan-module-mall/feifan-module-trade-biz/src/main/resources/mapper/jpushmessagelog/JPushMessageLogMapper.xml

@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="cn.newfeifan.mall.module.trade.dal.mysql.jpushmessagelog.JPushMessageLogMapper">
+
+    <!--
+        一般情况下,尽可能使用 Mapper 进行 CRUD 增删改查即可。
+        无法满足的场景,例如说多表关联查询,才使用 XML 编写 SQL。
+        代码生成器暂时只生成 Mapper XML 文件本身,更多推荐 MybatisX 快速开发插件来生成查询。
+        文档可见:https://www.zhongxing.cn/MyBatis/x-plugins/
+     -->
+
+</mapper>

+ 5 - 0
feifan-module-member/feifan-module-member-biz/src/main/java/cn/newfeifan/mall/module/member/dal/dataobject/user/MemberUserDO.java

@@ -189,4 +189,9 @@ public class MemberUserDO extends TenantBaseDO {
      * 登录用的用户名
      */
     private String username;
+
+    /**
+     * 安卓app注册id
+     */
+    private String androidRegisterId;
 }

+ 8 - 0
feifan-module-member/feifan-module-member-biz/src/main/java/cn/newfeifan/mall/module/member/service/user/MemberUserService.java

@@ -197,4 +197,12 @@ public interface MemberUserService {
      * @return 用户名
      */
     String getUsernameByUserId(Long userId);
+
+    /**
+     * 通过系统用户或者会员用户来获取对应的rsgId
+     * @param systemUserIds 系统用户
+     * @param userId 会员用固话
+     * @return rsgId列表
+     */
+    List<String> getUserRsgIds(Long systemUserIds, Long userId);
 }

+ 18 - 0
feifan-module-member/feifan-module-member-biz/src/main/java/cn/newfeifan/mall/module/member/service/user/MemberUserServiceImpl.java

@@ -21,6 +21,7 @@ import cn.newfeifan.mall.module.system.api.sms.dto.code.SmsCodeUseReqDTO;
 import cn.newfeifan.mall.module.system.api.social.SocialClientApi;
 import cn.newfeifan.mall.module.system.api.social.dto.SocialWxPhoneNumberInfoRespDTO;
 import cn.newfeifan.mall.module.system.enums.sms.SmsSceneEnum;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.google.common.annotations.VisibleForTesting;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.security.crypto.password.PasswordEncoder;
@@ -34,6 +35,7 @@ import javax.validation.Valid;
 import java.time.LocalDateTime;
 import java.util.Collection;
 import java.util.List;
+import java.util.stream.Collectors;
 
 import static cn.newfeifan.mall.framework.common.exception.util.ServiceExceptionUtil.exception;
 import static cn.newfeifan.mall.framework.common.util.servlet.ServletUtils.getClientIP;
@@ -343,4 +345,20 @@ public class MemberUserServiceImpl implements MemberUserService {
         return null;
     }
 
+    @Override
+    public List<String> getUserRsgIds(Long systemUserId, Long userId) {
+        List<MemberUserDO> memberUserDOS;
+        if (systemUserId != null) {
+            memberUserDOS = memberUserMapper.selectList(new LambdaQueryWrapper<MemberUserDO>()
+                    .eq(MemberUserDO::getSystemUsersId, systemUserId)
+                    .isNotNull(MemberUserDO::getAndroidRegisterId));
+        } else {
+            memberUserDOS = memberUserMapper.selectList(new LambdaQueryWrapper<MemberUserDO>()
+                    .eq(MemberUserDO::getId, userId)
+                    .isNotNull(MemberUserDO::getAndroidRegisterId));
+
+        }
+        return memberUserDOS.stream().map(MemberUserDO::getAndroidRegisterId).collect(Collectors.toList());
+    }
+
 }

+ 4 - 0
feifan-server/src/main/resources/application-local.yaml

@@ -265,4 +265,8 @@ justauth:
 #    file:
 #       disabled: true
 
+#极光推送配置
+jpush:
+  app-key: ae2e75f19ee1b240e744191b
+  secret: ae5e69155a3cb68ffd94158e
 

+ 4 - 1
feifan-server/src/main/resources/application-prod.yaml

@@ -268,4 +268,7 @@ justauth:
 #    file:
 #       disabled: true
 
-
+#极光推送配置
+jpush:
+  app-key: ae2e75f19ee1b240e744191b
+  secret: ae5e69155a3cb68ffd94158e