diff --git a/DB/version4.2.5toMASTER.sql b/DB/version4.2.5toMASTER.sql index 1a211ecd..2225ac59 100644 --- a/DB/version4.2.5toMASTER.sql +++ b/DB/version4.2.5toMASTER.sql @@ -79,4 +79,12 @@ ALTER TABLE li_store ADD `self_pick_flag` bit(1) DEFAULT NULL; /** 历史足迹增加店铺Id */ -ALTER TABLE li_foot_print ADD `store_id` varchar(255) DEFAULT NULL COMMENT '店铺ID'; \ No newline at end of file +ALTER TABLE li_foot_print ADD `store_id` varchar(255) DEFAULT NULL COMMENT '店铺ID'; + + + +/** + 订单增加自提信息 + */ +ALTER TABLE li_member_withdraw_apply ADD `real_name` varchar(255) DEFAULT NULL; +ALTER TABLE li_member_withdraw_apply ADD `connect_number` varchar(255) DEFAULT NULL; diff --git a/README.md b/README.md index 62fda1fb..f058df13 100644 --- a/README.md +++ b/README.md @@ -165,3 +165,11 @@ PS:手机验证码为 ‘111111’ ##### 交流 qq 2群 875294241(已满) ##### 交流 qq 3群 263785057(已满) ##### 交流 qq 4群 674617534 + + +### 附录 +有人有自己的学习视频、学习记录文档、希望宣传关联开源项目等均可以私聊仓库所有者。 + +类似: + +清晨敲代码同学的分析: https://blog.csdn.net/vaevaevae233/category_12103567.html \ No newline at end of file diff --git a/buyer-api/src/main/java/cn/lili/controller/order/OrderBuyerController.java b/buyer-api/src/main/java/cn/lili/controller/order/OrderBuyerController.java index 1dfcb000..3e62ecc4 100644 --- a/buyer-api/src/main/java/cn/lili/controller/order/OrderBuyerController.java +++ b/buyer-api/src/main/java/cn/lili/controller/order/OrderBuyerController.java @@ -116,6 +116,16 @@ public class OrderBuyerController { return ResultUtil.data(orderService.getTraces(orderSn)); } + @ApiOperation(value = "查询地图版物流踪迹") + @ApiImplicitParams({ + @ApiImplicitParam(name = "orderSn", value = "订单编号", required = true, dataType = "String", paramType = "path") + }) + @PostMapping(value = "/getMapTraces/{orderSn}") + public ResultMessage getMapTraces(@NotBlank(message = "订单编号不能为空") @PathVariable String orderSn) { + OperationalJudgment.judgment(orderService.getBySn(orderSn)); + return ResultUtil.data(orderService.getMapTraces(orderSn)); + } + @PreventDuplicateSubmissions @ApiOperation(value = "开票") diff --git a/buyer-api/src/main/java/cn/lili/controller/order/RechargeTradeBuyerController.java b/buyer-api/src/main/java/cn/lili/controller/order/RechargeTradeBuyerController.java index e85cddbf..7ea39171 100644 --- a/buyer-api/src/main/java/cn/lili/controller/order/RechargeTradeBuyerController.java +++ b/buyer-api/src/main/java/cn/lili/controller/order/RechargeTradeBuyerController.java @@ -10,6 +10,7 @@ import io.swagger.annotations.ApiImplicitParam; import io.swagger.annotations.ApiImplicitParams; import io.swagger.annotations.ApiOperation; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @@ -26,6 +27,7 @@ import javax.validation.constraints.Min; @RestController @Api(tags = "买家端,预存款充值记录接口") @RequestMapping("/buyer/trade/recharge") +@Validated public class RechargeTradeBuyerController { @Autowired @@ -37,7 +39,10 @@ public class RechargeTradeBuyerController { @ApiImplicitParams({ @ApiImplicitParam(name = "price", value = "充值金额", required = true, dataType = "double", paramType = "query") }) - public ResultMessage create(@Max(value = 10000, message = "充值金额单次最多允许充值10000元") @Min(value = 1, message = "充值金额单次最少充值金额为1元") Double price) { + public ResultMessage create( + @Max(value = 10000, message = "充值金额单次最多允许充值10000元") + @Min(value = 1, message = "充值金额单次最少充值金额为1元") + Double price) { Recharge recharge = this.rechargeService.recharge(price); return ResultUtil.data(recharge); } diff --git a/buyer-api/src/main/java/cn/lili/controller/wallet/MemberWalletBuyerController.java b/buyer-api/src/main/java/cn/lili/controller/wallet/MemberWalletBuyerController.java index 52328e6f..7ce8061b 100644 --- a/buyer-api/src/main/java/cn/lili/controller/wallet/MemberWalletBuyerController.java +++ b/buyer-api/src/main/java/cn/lili/controller/wallet/MemberWalletBuyerController.java @@ -21,6 +21,7 @@ import io.swagger.annotations.ApiImplicitParams; import io.swagger.annotations.ApiOperation; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; import javax.validation.constraints.Max; @@ -36,6 +37,7 @@ import javax.validation.constraints.Pattern; @RestController @Api(tags = "买家端,会员余额接口") @RequestMapping("/buyer/wallet/wallet") +@Validated public class MemberWalletBuyerController { /** @@ -127,7 +129,10 @@ public class MemberWalletBuyerController { @ApiImplicitParams({ @ApiImplicitParam(name = "price", value = "提现金额", required = true, dataType = "double", paramType = "query") }) - public ResultMessage withdrawal(@Max(value = 9999, message = "充值金额单次最多允许提现9999元") @Min(value = 1, message = "充值金额单次最少提现金额为1元") Double price) { + public ResultMessage withdrawal( + @Max(value = 9999, message = "提现金额单次最多允许提现9999元") + @Min(value = 1, message = "提现金额单次最少提现金额为1元") + Double price) { return ResultUtil.data(memberWalletService.applyWithdrawal(price)); } diff --git a/consumer/src/main/java/cn/lili/listener/GoodsMessageListener.java b/consumer/src/main/java/cn/lili/listener/GoodsMessageListener.java index 1ed1c737..692e3ade 100644 --- a/consumer/src/main/java/cn/lili/listener/GoodsMessageListener.java +++ b/consumer/src/main/java/cn/lili/listener/GoodsMessageListener.java @@ -8,6 +8,7 @@ import cn.hutool.json.JSONObject; import cn.hutool.json.JSONUtil; import cn.lili.common.aop.annotation.RetryOperation; import cn.lili.common.exception.RetryException; +import cn.lili.common.vo.PageVO; import cn.lili.event.GoodsCommentCompleteEvent; import cn.lili.modules.distribution.entity.dos.DistributionGoods; import cn.lili.modules.distribution.entity.dto.DistributionGoodsSearchParams; @@ -33,6 +34,8 @@ import cn.lili.modules.promotion.service.PromotionService; import cn.lili.modules.search.entity.dos.EsGoodsIndex; import cn.lili.modules.search.service.EsGoodsIndexService; import cn.lili.rocketmq.tags.GoodsTagsEnum; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import lombok.extern.slf4j.Slf4j; import org.apache.rocketmq.common.message.MessageExt; import org.apache.rocketmq.spring.annotation.RocketMQMessageListener; @@ -54,6 +57,8 @@ import java.util.stream.Collectors; @RocketMQMessageListener(topic = "${lili.data.rocketmq.goods-topic}", consumerGroup = "${lili.data.rocketmq.goods-group}") public class GoodsMessageListener implements RocketMQListener { + private static final int BATCH_SIZE = 10; + /** * ES商品 */ @@ -175,10 +180,8 @@ public class GoodsMessageListener implements RocketMQListener { try { String updateIndexFieldsJsonStr = new String(messageExt.getBody()); JSONObject updateIndexFields = JSONUtil.parseObj(updateIndexFieldsJsonStr); - @SuppressWarnings("unchecked") - Map queryFields = updateIndexFields.get("queryFields", Map.class); - @SuppressWarnings("unchecked") - Map updateFields = updateIndexFields.get("updateFields", Map.class); + @SuppressWarnings("unchecked") Map queryFields = updateIndexFields.get("queryFields", Map.class); + @SuppressWarnings("unchecked") Map updateFields = updateIndexFields.get("updateFields", Map.class); goodsIndexService.updateIndex(queryFields, updateFields); } catch (Exception e) { log.error("更新商品索引事件执行异常,商品信息: " + new String(messageExt.getBody()), e); @@ -238,10 +241,7 @@ public class GoodsMessageListener implements RocketMQListener { try { goodsCommentCompleteEvent.goodsComment(memberEvaluation); } catch (Exception e) { - log.error("评价{},在{}业务中,状态修改事件执行异常", - new String(messageExt.getBody()), - goodsCommentCompleteEvent.getClass().getName(), - e); + log.error("评价{},在{}业务中,状态修改事件执行异常", new String(messageExt.getBody()), goodsCommentCompleteEvent.getClass().getName(), e); } } break; @@ -260,26 +260,42 @@ public class GoodsMessageListener implements RocketMQListener { log.info("更新商品索引促销信息: {}", promotionsJsonStr); JSONObject jsonObject = JSONUtil.parseObj(promotionsJsonStr); // 转换为详细的促销信息(注:促销信息必须继承自 BasePromotions,且必须保证派生类存在与sdk包下) - BasePromotions promotions = (BasePromotions) jsonObject.get("promotions", - ClassLoaderUtil.loadClass(jsonObject.get("promotionsType").toString())); + BasePromotions promotions = (BasePromotions) jsonObject.get("promotions", ClassLoaderUtil.loadClass(jsonObject.get("promotionsType").toString())); // 获取促销唯一key,由 促销类型 + 促销id 组成 String esPromotionKey = jsonObject.get("esPromotionKey").toString(); if (PromotionsScopeTypeEnum.PORTION_GOODS.name().equals(promotions.getScopeType())) { - PromotionGoodsSearchParams searchParams = new PromotionGoodsSearchParams(); - searchParams.setPromotionId(promotions.getId()); - List promotionGoodsList = this.promotionGoodsService.listFindAll(searchParams); - List skuIds = promotionGoodsList.stream().map(PromotionGoods::getSkuId).collect(Collectors.toList()); - // 更新商品索引促销信息(删除原索引中相关的促销信息,更新索引中促销信息) - this.goodsIndexService.deleteEsGoodsPromotionByPromotionKey(skuIds, esPromotionKey); - this.goodsIndexService.updateEsGoodsIndexByList(promotionGoodsList, promotions, esPromotionKey); + for (int i = 0; ; i++) { + PromotionGoodsSearchParams searchParams = new PromotionGoodsSearchParams(); + searchParams.setPromotionId(promotions.getId()); + PageVO pageVO = new PageVO(); + pageVO.setPageNumber(i); + pageVO.setPageSize(BATCH_SIZE); + Page promotionGoodsPage = this.promotionGoodsService.pageFindAll(searchParams, pageVO); + if (promotionGoodsPage == null || promotionGoodsPage.getRecords().isEmpty()) { + break; + } + List skuIds = promotionGoodsPage.getRecords().stream().map(PromotionGoods::getSkuId).collect(Collectors.toList()); + // 更新商品索引促销信息(删除原索引中相关的促销信息,更新索引中促销信息) + this.goodsIndexService.deleteEsGoodsPromotionByPromotionKey(skuIds, esPromotionKey); + this.goodsIndexService.updateEsGoodsIndexByList(promotionGoodsPage.getRecords(), promotions, esPromotionKey); + } + } else if (PromotionsScopeTypeEnum.PORTION_GOODS_CATEGORY.name().equals(promotions.getScopeType())) { - GoodsSearchParams searchParams = new GoodsSearchParams(); - searchParams.setCategoryPath(promotions.getScopeId()); - List goodsSkuByList = this.goodsSkuService.getGoodsSkuByList(searchParams); - List skuIds = goodsSkuByList.stream().map(GoodsSku::getId).collect(Collectors.toList()); - // 更新商品索引促销信息(删除原索引中相关的促销信息,更新索引中促销信息) - this.goodsIndexService.deleteEsGoodsPromotionByPromotionKey(skuIds, esPromotionKey); - this.goodsIndexService.updateEsGoodsIndexPromotions(skuIds, promotions, esPromotionKey); + for (int i = 0; ; i++) { + GoodsSearchParams searchParams = new GoodsSearchParams(); + searchParams.setCategoryPath(promotions.getScopeId()); + searchParams.setPageNumber(i); + searchParams.setPageSize(BATCH_SIZE); + IPage goodsSkuByPage = this.goodsSkuService.getGoodsSkuByPage(searchParams); + if (goodsSkuByPage == null || goodsSkuByPage.getRecords().isEmpty()) { + break; + } + List skuIds = goodsSkuByPage.getRecords().stream().map(GoodsSku::getId).collect(Collectors.toList()); + // 更新商品索引促销信息(删除原索引中相关的促销信息,更新索引中促销信息) + this.goodsIndexService.deleteEsGoodsPromotionByPromotionKey(skuIds, esPromotionKey); + this.goodsIndexService.updateEsGoodsIndexPromotions(skuIds, promotions, esPromotionKey); + } + } else if (PromotionsScopeTypeEnum.ALL.name().equals(promotions.getScopeType())) { this.goodsIndexService.updateEsGoodsIndexAllByList(promotions, esPromotionKey); } @@ -321,26 +337,31 @@ public class GoodsMessageListener implements RocketMQListener { * @param goods 商品消息 */ private void updateGoodsIndex(Goods goods) { - //如果商品通过审核&&并且已上架 - GoodsSearchParams searchParams = new GoodsSearchParams(); - searchParams.setGoodsId(goods.getId()); - List goodsSkuList = this.goodsSkuService.getGoodsSkuByList(searchParams); - log.info("goods:{}", goods); - log.info("goodsSkuList:{}", goodsSkuList); - if (goods.getAuthFlag().equals(GoodsAuthEnum.PASS.name()) - && goods.getMarketEnable().equals(GoodsStatusEnum.UPPER.name()) - && Boolean.FALSE.equals(goods.getDeleteFlag())) { - this.generatorGoodsIndex(goods, goodsSkuList); - } - //如果商品状态值不支持es搜索,那么将商品信息做下架处理 - else { - for (GoodsSku goodsSku : goodsSkuList) { - EsGoodsIndex esGoodsOld = goodsIndexService.findById(goodsSku.getId()); - if (esGoodsOld != null) { - goodsIndexService.deleteIndexById(goodsSku.getId()); + for (int i = 1; ; i++) { + //如果商品通过审核&&并且已上架 + GoodsSearchParams searchParams = new GoodsSearchParams(); + searchParams.setGoodsId(goods.getId()); + searchParams.setPageNumber(i); + searchParams.setPageSize(BATCH_SIZE); + IPage goodsSkuByPage = this.goodsSkuService.getGoodsSkuByPage(searchParams); + if (goodsSkuByPage == null || goodsSkuByPage.getRecords().isEmpty()) { + break; + } + log.info("goods:{}", goods); + log.info("goodsSkuList:{}", goodsSkuByPage.getRecords()); + if (goods.getAuthFlag().equals(GoodsAuthEnum.PASS.name()) && goods.getMarketEnable().equals(GoodsStatusEnum.UPPER.name()) && Boolean.FALSE.equals(goods.getDeleteFlag())) { + this.generatorGoodsIndex(goods, goodsSkuByPage.getRecords()); + } else { + //如果商品状态值不支持es搜索,那么将商品信息做下架处理 + for (GoodsSku goodsSku : goodsSkuByPage.getRecords()) { + EsGoodsIndex esGoodsOld = goodsIndexService.findById(goodsSku.getId()); + if (esGoodsOld != null) { + goodsIndexService.deleteIndexById(goodsSku.getId()); + } } } } + } /** @@ -462,9 +483,7 @@ public class GoodsMessageListener implements RocketMQListener { goodsSku.setBuyCount(buyCount); goodsSkuService.update(goodsSku); - this.goodsIndexService.updateIndex( - MapUtil.builder(new HashMap()).put("id", goodsCompleteMessage.getSkuId()).build(), - MapUtil.builder(new HashMap()).put("buyCount", buyCount).build()); + this.goodsIndexService.updateIndex(MapUtil.builder(new HashMap()).put("id", goodsCompleteMessage.getSkuId()).build(), MapUtil.builder(new HashMap()).put("buyCount", buyCount).build()); } else { log.error("商品SkuId为[" + goodsCompleteMessage.getGoodsId() + "的商品不存在,更新商品失败!"); diff --git a/docs/diagram/OrderComplaintBuyerController.uml b/docs/diagram/OrderComplaintBuyerController.uml new file mode 100644 index 00000000..03d08218 --- /dev/null +++ b/docs/diagram/OrderComplaintBuyerController.uml @@ -0,0 +1,50 @@ + + + JAVA + cn.lili.controller.order.OrderComplaintBuyerController + + cn.lili.modules.order.order.service.OrderComplaintCommunicationService + cn.lili.controller.order.OrderComplaintStoreController + cn.lili.modules.order.order.service.OrderComplaintService + cn.lili.controller.order.OrderComplaintBuyerController + cn.lili.controller.order.OrderComplaintManagerController + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Inner Classes + Methods + Properties + + All + private + + diff --git a/docs/uml/AfterSaleRefundGodos.puml b/docs/uml/AfterSaleRefundGodos.puml new file mode 100644 index 00000000..17092a7e --- /dev/null +++ b/docs/uml/AfterSaleRefundGodos.puml @@ -0,0 +1,20 @@ +@startuml +'https://plantuml.com/sequence-diagram + +actor user +actor seller +actor manager + +user --> 平台: 申请售后 +seller -> 平台: 审核售后申请 +activate 平台 +平台 -> 平台: 更新售后状态 +return 售后审核响应 + +user -> 平台: 填写物流信息 +seller->平台: 确认收货 +activate 平台 +平台 -> 平台: 自动退款 +return 售后审核响应 +平台 -> user: 售后结束 +@enduml \ No newline at end of file diff --git a/docs/uml/AfterSaleRefundMoney.puml b/docs/uml/AfterSaleRefundMoney.puml new file mode 100644 index 00000000..2b75c663 --- /dev/null +++ b/docs/uml/AfterSaleRefundMoney.puml @@ -0,0 +1,16 @@ +@startuml +'https://plantuml.com/sequence-diagram + +actor user +actor seller +actor manager + +user --> 平台: 申请售后 +seller -> 平台: 审核售后申请 +activate 平台 +平台 -> 平台: 通过审核,自动退款 +manager --> 平台: 审核阶段可介入操作 +return 售后审核响应 + +user -> 平台: 查询售后结果 +@enduml \ No newline at end of file diff --git a/docs/uml/Complaint.puml b/docs/uml/Complaint.puml new file mode 100644 index 00000000..eb768b2e --- /dev/null +++ b/docs/uml/Complaint.puml @@ -0,0 +1,21 @@ +@startuml +'https://plantuml.com/sequence-diagram + +actor user +actor seller +actor manager + +activate 平台 +user -> 平台: 输入原因,发起投诉 +user --> 平台: 多次输出对话 +seller --> 平台: 多次输出对话 +manager --> 平台: 多次输出对话 +manager -> 平台: 根据各方意见,仲裁投诉结果 +activate 平台 +平台->平台: 仲裁结果,更新状态 + +平台 -> seller: 根据平台仲裁结果进行处理 +平台 -> user: 根据平台仲裁结果进行处理 +deactivate 平台 + +@enduml \ No newline at end of file diff --git a/framework/pom.xml b/framework/pom.xml index 5e0b4b74..3003fb45 100644 --- a/framework/pom.xml +++ b/framework/pom.xml @@ -437,8 +437,33 @@ minio ${minio.version} - - + + com.huaweicloud + esdk-obs-java + ${huaweicloud.version} + + + com.qcloud + cos_api + ${cos.version} + + + com.tencentcloudapi + tencentcloud-sdk-java + ${tencentcloud.version} + + + com.github.kuaidi100-api + sdk + ${kuaidi100-api.version} + + + com.qiyuesuo.sdk + SDK + 2.1.7 + system + ${project.basedir}/src/main/resources/maven-repository/SF-CSIM-EXPRESS-SDK-V2.1.7.jar + diff --git a/framework/src/main/java/cn/lili/common/enums/ResultCode.java b/framework/src/main/java/cn/lili/common/enums/ResultCode.java index 1693f24e..a81b4987 100644 --- a/framework/src/main/java/cn/lili/common/enums/ResultCode.java +++ b/framework/src/main/java/cn/lili/common/enums/ResultCode.java @@ -193,7 +193,7 @@ public enum ResultCode { ORDER_NOT_USER(31007, "非当前会员的订单"), ORDER_TAKE_ERROR(31008, "当前订单无法核销"), MEMBER_ADDRESS_NOT_EXIST(31009, "订单无收货地址,请先配置收货地址"), - STORE_ADDRESS_NOT_EXIST(31009,"订单没有自提地址,请先选择自提地址"), + STORE_ADDRESS_NOT_EXIST(31009, "订单没有自提地址,请先选择自提地址"), ORDER_DELIVER_NUM_ERROR(31010, "没有待发货的订单"), ORDER_NOT_SUPPORT_DISTRIBUTION(31011, "购物车中包含不支持配送的商品,请重新选择收货地址,或者重新选择商品"), ORDER_NOT_EXIST_VALID(31041, "购物车中无有效商品,请检查购物车内商品,或者重新选择商品"), @@ -202,6 +202,8 @@ public enum ResultCode { ORDER_ITEM_NOT_EXIST(31014, "当前订单项不存在!"), POINT_NOT_ENOUGH(31015, "当前会员积分不足购买当前积分商品!"), + ORDER_LABEL_ORDER_ERROR(31016, "订单不能打印电子面单"), + /** * 支付 @@ -219,7 +221,7 @@ public enum ResultCode { PAY_POINT_ENOUGH(32010, "积分不足,不能兑换"), PAY_NOT_EXIST_ORDER(32011, "支付订单不存在"), CAN_NOT_RECHARGE_WALLET(32012, "不能使用余额进行充值"), - + RECHARGE_PRICE_ERROR(32013, "充值金额错误"), /** * 售后 @@ -257,7 +259,7 @@ public enum ResultCode { WALLET_REMARK_ERROR(34004, "请填写审核备注!"), WALLET_EXIT_ERROR(34000, "钱包已存在,无法重复创建"), WALLET_APPLY_ERROR(34005, "提现申请异常!"), - + WALLET_WITHDRAWAL_AMOUNT_ERROR(34006, "申请提现金额异常!"), /** * 评价 */ @@ -411,8 +413,10 @@ public enum ResultCode { STORE_NOT_OPEN(50004, "该会员未开通店铺"), STORE_NOT_LOGIN_ERROR(50005, "未登录店铺"), STORE_CLOSE_ERROR(50006, "店铺关闭,请联系管理员"), + STORE_DELIVER_GOODS_ADDRESS(50007,"请填写商家发货地址"), FREIGHT_TEMPLATE_NOT_EXIST(50010, "当前模版不存在"), STORE_STATUS_ERROR(50011, "店铺状态异常,无法申请"), + STORE_DELIVER_ADDRESS_EXIST(50012,"请填写发货地址"), /** * 结算单 @@ -489,6 +493,12 @@ public enum ResultCode { WECHAT_ERROR(80307, "微信接口异常"), APP_VERSION_EXIST(80307, "APP版本已存在"), + /** + * IM + */ + IM_MESSAGE_ADD_ERROR(80400,"IM消息发送错误"), + IM_MESSAGE_EDIT_ERROR(80400,"IM消息更新错误"), + /** * 其他 */ @@ -500,6 +510,7 @@ public enum ResultCode { PURCHASE_ORDER_DEADLINE_ERROR(90004, "供求单,已超过报名截止时间"), INDEX_BUILDING(90005, "索引正在生成"); + private final Integer code; private final String message; diff --git a/framework/src/main/java/cn/lili/common/security/AuthUser.java b/framework/src/main/java/cn/lili/common/security/AuthUser.java index 819ae6e6..cc384d19 100644 --- a/framework/src/main/java/cn/lili/common/security/AuthUser.java +++ b/framework/src/main/java/cn/lili/common/security/AuthUser.java @@ -2,6 +2,7 @@ package cn.lili.common.security; import cn.lili.common.security.enums.UserEnums; import lombok.AllArgsConstructor; +import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; @@ -13,6 +14,7 @@ import java.io.Serializable; @Data @NoArgsConstructor @AllArgsConstructor +@Builder public class AuthUser implements Serializable { private static final long serialVersionUID = 582441893336003319L; @@ -84,22 +86,5 @@ public class AuthUser implements Serializable { this.nickName = nickName; } - public AuthUser(String username, String id, String face, UserEnums manager, String nickName, Boolean isSuper) { - this.username = username; - this.id = id; - this.face = face; - this.role = manager; - this.isSuper = isSuper; - this.nickName = nickName; - } - public AuthUser(String username, String id, UserEnums manager, String nickName, Boolean isSuper, String clerkId, String face) { - this.username = username; - this.id = id; - this.role = manager; - this.isSuper = isSuper; - this.nickName = nickName; - this.clerkId = clerkId; - this.face = face; - } } diff --git a/framework/src/main/java/cn/lili/common/security/token/TokenUtil.java b/framework/src/main/java/cn/lili/common/security/token/TokenUtil.java index 0f92dc6e..dc62def6 100644 --- a/framework/src/main/java/cn/lili/common/security/token/TokenUtil.java +++ b/framework/src/main/java/cn/lili/common/security/token/TokenUtil.java @@ -4,10 +4,10 @@ import cn.lili.cache.Cache; import cn.lili.cache.CachePrefix; import cn.lili.common.enums.ResultCode; import cn.lili.common.exception.ServiceException; +import cn.lili.common.properties.JWTTokenProperties; import cn.lili.common.security.AuthUser; import cn.lili.common.security.enums.SecurityEnum; import cn.lili.common.security.enums.UserEnums; -import cn.lili.common.properties.JWTTokenProperties; import com.google.gson.Gson; import io.jsonwebtoken.*; import io.jsonwebtoken.security.SignatureException; @@ -34,24 +34,21 @@ public class TokenUtil { /** * 构建token * - * @param username 主体 - * @param claim 私有声明 - * @param longTerm 长时间特殊token 如:移动端,微信小程序等 - * @param userEnums 用户枚举 + * @param authUser 私有声明 * @return TOKEN */ - public Token createToken(String username, Object claim, boolean longTerm, UserEnums userEnums) { + public Token createToken(AuthUser authUser) { Token token = new Token(); //访问token - String accessToken = createToken(username, claim, tokenProperties.getTokenExpireTime()); + String accessToken = createToken(authUser, tokenProperties.getTokenExpireTime()); - cache.put(CachePrefix.ACCESS_TOKEN.getPrefix(userEnums) + accessToken, 1, + cache.put(CachePrefix.ACCESS_TOKEN.getPrefix(authUser.getRole()) + accessToken, 1, tokenProperties.getTokenExpireTime(), TimeUnit.MINUTES); //刷新token生成策略:如果是长时间有效的token(用于app),则默认15天有效期刷新token。如果是普通用户登录,则刷新token为普通token2倍数 - Long expireTime = longTerm ? 15 * 24 * 60L : tokenProperties.getTokenExpireTime() * 2; - String refreshToken = createToken(username, claim, expireTime); + Long expireTime = authUser.getLongTerm() ? 15 * 24 * 60L : tokenProperties.getTokenExpireTime() * 2; + String refreshToken = createToken(authUser, expireTime); - cache.put(CachePrefix.REFRESH_TOKEN.getPrefix(userEnums) + refreshToken, 1, expireTime, TimeUnit.MINUTES); + cache.put(CachePrefix.REFRESH_TOKEN.getPrefix(authUser.getRole()) + refreshToken, 1, expireTime, TimeUnit.MINUTES); token.setAccessToken(accessToken); token.setRefreshToken(refreshToken); @@ -62,17 +59,17 @@ public class TokenUtil { * 刷新token * * @param oldRefreshToken 刷新token - * @param userEnums 用户枚举 * @return token */ - public Token refreshToken(String oldRefreshToken, UserEnums userEnums) { + public Token refreshToken(String oldRefreshToken) { Claims claims; try { claims = Jwts.parser() .setSigningKey(SecretKeyUtil.generalKeyByDecoders()) .parseClaimsJws(oldRefreshToken).getBody(); - } catch (ExpiredJwtException | UnsupportedJwtException | MalformedJwtException | SignatureException | IllegalArgumentException e) { + } catch (ExpiredJwtException | UnsupportedJwtException | MalformedJwtException | SignatureException | + IllegalArgumentException e) { //token 过期 认证失败等 throw new ServiceException(ResultCode.USER_AUTH_EXPIRED); } @@ -80,7 +77,7 @@ public class TokenUtil { //获取存储在claims中的用户信息 String json = claims.get(SecurityEnum.USER_CONTEXT.getValue()).toString(); AuthUser authUser = new Gson().fromJson(json, AuthUser.class); - + UserEnums userEnums = authUser.getRole(); String username = authUser.getUsername(); //获取是否长期有效的token @@ -91,17 +88,18 @@ public class TokenUtil { if (cache.hasKey(CachePrefix.REFRESH_TOKEN.getPrefix(userEnums) + oldRefreshToken)) { Token token = new Token(); //访问token - String accessToken = createToken(username, authUser, tokenProperties.getTokenExpireTime()); + String accessToken = createToken(authUser, tokenProperties.getTokenExpireTime()); cache.put(CachePrefix.ACCESS_TOKEN.getPrefix(userEnums) + accessToken, 1, tokenProperties.getTokenExpireTime(), TimeUnit.MINUTES); //如果是信任登录设备,则刷新token长度继续延长 Long expirationTime = tokenProperties.getTokenExpireTime() * 2; if (longTerm) { expirationTime = 60 * 24 * 15L; + authUser.setLongTerm(true); } //刷新token生成策略:如果是长时间有效的token(用于app),则默认15天有效期刷新token。如果是普通用户登录,则刷新token为普通token2倍数 - String refreshToken = createToken(username, authUser, expirationTime); + String refreshToken = createToken(authUser, expirationTime); cache.put(CachePrefix.REFRESH_TOKEN.getPrefix(userEnums) + refreshToken, 1, expirationTime, TimeUnit.MINUTES); token.setAccessToken(accessToken); @@ -117,18 +115,17 @@ public class TokenUtil { /** * 生成token * - * @param username 主体 - * @param claim 私有神明内容 + * @param authUser jwt主体对象 * @param expirationTime 过期时间(分钟) * @return token字符串 */ - private String createToken(String username, Object claim, Long expirationTime) { + private String createToken(AuthUser authUser, Long expirationTime) { //JWT 生成 return Jwts.builder() //jwt 私有声明 - .claim(SecurityEnum.USER_CONTEXT.getValue(), new Gson().toJson(claim)) + .claim(SecurityEnum.USER_CONTEXT.getValue(), new Gson().toJson(authUser)) //JWT的主体 - .setSubject(username) + .setSubject(authUser.getUsername()) //失效时间 当前时间+过期分钟 .setExpiration(new Date(System.currentTimeMillis() + expirationTime * 60 * 1000)) //签名算法和密钥 diff --git a/framework/src/main/java/cn/lili/modules/distribution/entity/dos/Distribution.java b/framework/src/main/java/cn/lili/modules/distribution/entity/dos/Distribution.java index 182c18c5..3a24e867 100644 --- a/framework/src/main/java/cn/lili/modules/distribution/entity/dos/Distribution.java +++ b/framework/src/main/java/cn/lili/modules/distribution/entity/dos/Distribution.java @@ -34,6 +34,8 @@ public class Distribution extends BaseEntity { this.memberName = memberName; distributionOrderCount=0; this.distributionStatus = DistributionStatusEnum.APPLY.name(); + commissionFrozen=0D; + canRebate=0D; BeanUtil.copyProperties(distributionApplyDTO, this); } diff --git a/framework/src/main/java/cn/lili/modules/file/entity/enums/OssEnum.java b/framework/src/main/java/cn/lili/modules/file/entity/enums/OssEnum.java index 748faeca..585b04da 100644 --- a/framework/src/main/java/cn/lili/modules/file/entity/enums/OssEnum.java +++ b/framework/src/main/java/cn/lili/modules/file/entity/enums/OssEnum.java @@ -1,7 +1,5 @@ package cn.lili.modules.file.entity.enums; -import com.aliyun.oss.OSS; - /** * OssEnum * @@ -11,7 +9,7 @@ import com.aliyun.oss.OSS; */ public enum OssEnum { /** - * + * 上传渠道 */ - ALI_OSS, MINIO; + ALI_OSS, MINIO, HUAWEI_OBS, TENCENT_COS; } diff --git a/framework/src/main/java/cn/lili/modules/file/plugin/FilePluginFactory.java b/framework/src/main/java/cn/lili/modules/file/plugin/FilePluginFactory.java index 63e4da08..929116f1 100644 --- a/framework/src/main/java/cn/lili/modules/file/plugin/FilePluginFactory.java +++ b/framework/src/main/java/cn/lili/modules/file/plugin/FilePluginFactory.java @@ -4,7 +4,9 @@ import cn.hutool.json.JSONUtil; import cn.lili.common.exception.ServiceException; import cn.lili.modules.file.entity.enums.OssEnum; import cn.lili.modules.file.plugin.impl.AliFilePlugin; +import cn.lili.modules.file.plugin.impl.HuaweiFilePlugin; import cn.lili.modules.file.plugin.impl.MinioFilePlugin; +import cn.lili.modules.file.plugin.impl.TencentFilePlugin; import cn.lili.modules.system.entity.dos.Setting; import cn.lili.modules.system.entity.dto.OssSetting; import cn.lili.modules.system.entity.enums.SettingEnum; @@ -47,6 +49,10 @@ public class FilePluginFactory { return new MinioFilePlugin(ossSetting); case ALI_OSS: return new AliFilePlugin(ossSetting); + case HUAWEI_OBS: + return new HuaweiFilePlugin(ossSetting); + case TENCENT_COS: + return new TencentFilePlugin(ossSetting); default: throw new ServiceException(); } diff --git a/framework/src/main/java/cn/lili/modules/file/plugin/impl/AliFilePlugin.java b/framework/src/main/java/cn/lili/modules/file/plugin/impl/AliFilePlugin.java index e195a2a0..31ae2c7d 100644 --- a/framework/src/main/java/cn/lili/modules/file/plugin/impl/AliFilePlugin.java +++ b/framework/src/main/java/cn/lili/modules/file/plugin/impl/AliFilePlugin.java @@ -44,9 +44,9 @@ public class AliFilePlugin implements FilePlugin { */ private OSS getOssClient() { return new OSSClientBuilder().build( - ossSetting.getEndPoint(), - ossSetting.getAccessKeyId(), - ossSetting.getAccessKeySecret()); + ossSetting.getAliyunOSSEndPoint(), + ossSetting.getAliyunOSSAccessKeyId(), + ossSetting.getAliyunOSSAccessKeySecret()); } @@ -56,14 +56,14 @@ public class AliFilePlugin implements FilePlugin { * @return */ private String getUrlPrefix() { - return "https://" + ossSetting.getBucketName() + "." + ossSetting.getEndPoint() + "/"; + return "https://" + ossSetting.getAliyunOSSBucketName() + "." + ossSetting.getAliyunOSSEndPoint() + "/"; } @Override public String pathUpload(String filePath, String key) { OSS ossClient = getOssClient(); try { - ossClient.putObject(ossSetting.getBucketName(), key, new File(filePath)); + ossClient.putObject(ossSetting.getAliyunOSSBucketName(), key, new File(filePath)); } catch (OSSException oe) { log.error("Caught an OSSException, which means your request made it to OSS, " + "but was rejected with an error response for some reason."); @@ -94,7 +94,7 @@ public class AliFilePlugin implements FilePlugin { try { ObjectMetadata meta = new ObjectMetadata(); meta.setContentType("image/jpg"); - ossClient.putObject(ossSetting.getBucketName(), key, inputStream, meta); + ossClient.putObject(ossSetting.getAliyunOSSBucketName(), key, inputStream, meta); } catch (OSSException oe) { log.error("Caught an OSSException, which means your request made it to OSS, " + "but was rejected with an error response for some reason."); @@ -125,7 +125,7 @@ public class AliFilePlugin implements FilePlugin { try { ossClient.deleteObjects( - new DeleteObjectsRequest(ossSetting.getBucketName()).withKeys(key)); + new DeleteObjectsRequest(ossSetting.getAliyunOSSBucketName()).withKeys(key)); } catch (OSSException oe) { log.error("Caught an OSSException, which means your request made it to OSS, " + "but was rejected with an error response for some reason."); diff --git a/framework/src/main/java/cn/lili/modules/file/plugin/impl/HuaweiFilePlugin.java b/framework/src/main/java/cn/lili/modules/file/plugin/impl/HuaweiFilePlugin.java new file mode 100644 index 00000000..62e2c64d --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/file/plugin/impl/HuaweiFilePlugin.java @@ -0,0 +1,128 @@ +package cn.lili.modules.file.plugin.impl; + +import cn.lili.common.enums.ResultCode; +import cn.lili.common.exception.ServiceException; +import cn.lili.modules.file.entity.enums.OssEnum; +import cn.lili.modules.file.plugin.FilePlugin; +import cn.lili.modules.system.entity.dto.OssSetting; + +import com.obs.services.ObsClient; +import com.obs.services.exception.ObsException; +import com.obs.services.model.*; +import lombok.extern.slf4j.Slf4j; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.util.List; + +/** + * 华为obs 文件操作 + * + * @author Bulbasaur + * + * + */ + +@Slf4j +public class HuaweiFilePlugin implements FilePlugin { + + private OssSetting ossSetting; + + public HuaweiFilePlugin(OssSetting ossSetting) { + this.ossSetting = ossSetting; + } + + @Override + public OssEnum pluginName() { + return OssEnum.HUAWEI_OBS; + } + + /** + * 获取oss client + * + * @return + */ + private ObsClient getObsClient() { + return new ObsClient(ossSetting.getHuaweicloudOBSAccessKey(), ossSetting.getHuaweicloudOBSSecretKey(), ossSetting.getAliyunOSSEndPoint()); + } + + + @Override + public String pathUpload(String filePath, String key) { + ObsClient obsClient = getObsClient(); + try { + obsClient.putObject(ossSetting.getHuaweicloudOBSBucketName(), key, new File(filePath)); + } catch (ObsException ce) { + log.error("Error Message: " + ce.getMessage()); + throw new ServiceException(ResultCode.OSS_EXCEPTION_ERROR); + } finally { + try { + // 关闭OBS连接 + obsClient.close(); + } catch (IOException e) { + log.error("OBS关闭连接报错!" + e.getMessage()); + throw new ServiceException(ResultCode.OSS_EXCEPTION_ERROR); + } + } + return getUrlPrefix() + key; + } + + @Override + public String inputStreamUpload(InputStream inputStream, String key) { + ObsClient obsClient = getObsClient(); + try { + obsClient.putObject(new PutObjectRequest(ossSetting.getHuaweicloudOBSBucketName(), key, inputStream)); + } catch (ObsException obsException) { + throw new ServiceException(ResultCode.OSS_EXCEPTION_ERROR); + } finally { + try { + // 关闭OBS连接 + obsClient.close(); + } catch (IOException e) { + log.error("OBS关闭连接报错!" + e.getMessage()); + throw new ServiceException(ResultCode.OSS_EXCEPTION_ERROR); + } + } + + return getUrlPrefix() + key; + } + + @Override + public void deleteFile(List keys) { + ObsClient obsClient = getObsClient(); + ListVersionsResult result; + try { + DeleteObjectsRequest deleteRequest = new DeleteObjectsRequest(ossSetting.getHuaweicloudOBSBucketName()); + //deleteRequest.setQuiet(true); // 注意此demo默认是详细模式,如果要使用简单模式,请添加本行代码 + for (String key : keys) { + deleteRequest.addKeyAndVersion(key); + } + DeleteObjectsResult deleteResult = obsClient.deleteObjects(deleteRequest); + // 获取删除成功的对象 + log.info("删除成功:" + deleteResult.getDeletedObjectResults()); + // 获取删除失败的对象 + log.info("删除失败:" + deleteResult.getErrorResults()); + } catch (ObsException obsException) { + throw new ServiceException(ResultCode.OSS_DELETE_ERROR); + } finally { + try { + // 关闭OBS连接 + obsClient.close(); + } catch (IOException e) { + log.error("OBS关闭连接报错!" + e.getMessage()); + throw new ServiceException(ResultCode.OSS_EXCEPTION_ERROR); + } + } + } + + + /** + * 获取配置前缀 + * + * @return + */ + private String getUrlPrefix() { + return "https://" + ossSetting.getHuaweicloudOBSBucketName() + "." + ossSetting.getHuaweicloudOBSEndPoint() + "/"; + } +} diff --git a/framework/src/main/java/cn/lili/modules/file/plugin/impl/TencentFilePlugin.java b/framework/src/main/java/cn/lili/modules/file/plugin/impl/TencentFilePlugin.java new file mode 100644 index 00000000..61dda55d --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/file/plugin/impl/TencentFilePlugin.java @@ -0,0 +1,122 @@ +package cn.lili.modules.file.plugin.impl; + +import cn.lili.common.enums.ResultCode; +import cn.lili.common.exception.ServiceException; +import cn.lili.modules.file.entity.enums.OssEnum; +import cn.lili.modules.file.plugin.FilePlugin; +import cn.lili.modules.system.entity.dto.OssSetting; +import com.qcloud.cos.COSClient; +import com.qcloud.cos.ClientConfig; +import com.qcloud.cos.auth.BasicCOSCredentials; +import com.qcloud.cos.auth.COSCredentials; +import com.qcloud.cos.exception.CosClientException; +import com.qcloud.cos.exception.CosServiceException; +import com.qcloud.cos.http.HttpProtocol; +import com.qcloud.cos.model.DeleteObjectsRequest; +import com.qcloud.cos.model.ObjectMetadata; +import com.qcloud.cos.model.PutObjectRequest; +import com.qcloud.cos.region.Region; +import lombok.extern.slf4j.Slf4j; + +import java.io.File; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.List; + +/** + * 腾讯cos 文件操作 + * + * @author Bulbasaur + */ + +@Slf4j +public class TencentFilePlugin implements FilePlugin { + + private OssSetting ossSetting; + + public TencentFilePlugin(OssSetting ossSetting) { + this.ossSetting = ossSetting; + } + + @Override + public OssEnum pluginName() { + return OssEnum.TENCENT_COS; + } + + /** + * 获取oss client + * + * @return + */ + private COSClient getCOSClient() { + // 1 初始化用户身份信息(secretId, secretKey)。 + COSCredentials cred = new BasicCOSCredentials(ossSetting.getTencentCOSSecretId(), ossSetting.getTencentCOSSecretKey()); + // 2 设置 bucket 的地域, COS 地域的简称请参见 https://cloud.tencent.com/document/product/436/6224 + ClientConfig clientConfig = new ClientConfig(new Region("COS_REGION")); + // 这里建议设置使用 https 协议 + clientConfig.setHttpProtocol(HttpProtocol.https); + // 3 生成 cos 客户端。 + return new COSClient(cred, clientConfig); + } + + + /** + * 获取配置前缀 + * + * @return + */ + private String getUrlPrefix() { + return "https://" + ossSetting.getTencentCOSBucket() + "." + ossSetting.getTencentCOSEndPoint() + "/"; + } + + @Override + public String pathUpload(String filePath, String key) { + COSClient cosClient = getCOSClient(); + try { + cosClient.putObject(ossSetting.getTencentCOSBucket(), key, new File(filePath)); + } catch (CosServiceException oe) { + throw new ServiceException(ResultCode.OSS_EXCEPTION_ERROR); + } catch (CosClientException ce) { + throw new ServiceException(ResultCode.OSS_EXCEPTION_ERROR); + } finally { + cosClient.shutdown(); + } + return getUrlPrefix() + key; + } + + @Override + public String inputStreamUpload(InputStream inputStream, String key) { + COSClient cosClient = getCOSClient(); + try { + ObjectMetadata meta = new ObjectMetadata(); + meta.setContentType("image/jpg"); + cosClient.putObject(ossSetting.getTencentCOSBucket(), key, inputStream, meta); + } catch (CosServiceException oe) { + throw new ServiceException(ResultCode.OSS_EXCEPTION_ERROR); + } catch (CosClientException ce) { + throw new ServiceException(ResultCode.OSS_EXCEPTION_ERROR); + } finally { + cosClient.shutdown(); + } + return getUrlPrefix() + key; + } + + @Override + public void deleteFile(List keys) { + COSClient cosClient = getCOSClient(); + + try { + List delObjects = new ArrayList<>(); + for (String key:keys) { + delObjects.add(new DeleteObjectsRequest.KeyVersion(key)); + } + cosClient.deleteObjects(new DeleteObjectsRequest(ossSetting.getTencentCOSBucket()).withKeys(delObjects)); + } catch (CosServiceException oe) { + throw new ServiceException(ResultCode.OSS_EXCEPTION_ERROR); + } catch (CosClientException ce) { + throw new ServiceException(ResultCode.OSS_EXCEPTION_ERROR); + } finally { + cosClient.shutdown(); + } + } +} diff --git a/framework/src/main/java/cn/lili/modules/goods/serviceimpl/GoodsServiceImpl.java b/framework/src/main/java/cn/lili/modules/goods/serviceimpl/GoodsServiceImpl.java index 29f50414..e56898cb 100644 --- a/framework/src/main/java/cn/lili/modules/goods/serviceimpl/GoodsServiceImpl.java +++ b/framework/src/main/java/cn/lili/modules/goods/serviceimpl/GoodsServiceImpl.java @@ -427,6 +427,7 @@ public class GoodsServiceImpl extends ServiceImpl implements LambdaUpdateWrapper lambdaUpdateWrapper = Wrappers.lambdaUpdate(); lambdaUpdateWrapper.set(Goods::getTemplateId, templateId); lambdaUpdateWrapper.in(Goods::getId, goodsIds); + cache.multiDel(goodsIds); return this.update(lambdaUpdateWrapper); } diff --git a/framework/src/main/java/cn/lili/modules/im/entity/dos/ImTalk.java b/framework/src/main/java/cn/lili/modules/im/entity/dos/ImTalk.java index 56ffe258..7452756a 100644 --- a/framework/src/main/java/cn/lili/modules/im/entity/dos/ImTalk.java +++ b/framework/src/main/java/cn/lili/modules/im/entity/dos/ImTalk.java @@ -2,6 +2,8 @@ package cn.lili.modules.im.entity.dos; import cn.lili.common.utils.SnowFlake; +import cn.lili.modules.member.entity.dos.Member; +import cn.lili.modules.store.entity.dos.Store; import cn.lili.mybatis.BaseTenantEntity; import com.baomidou.mybatisplus.annotation.TableName; import com.fasterxml.jackson.annotation.JsonFormat; @@ -99,4 +101,71 @@ public class ImTalk extends BaseTenantEntity { this.name1 = name1; this.name2 = name2; } + + public ImTalk(Member member1,Member member2){ + if(Long.parseLong(member1.getId()) > Long.parseLong(member2.getId())){ + this.userId1 = member2.getId(); + this.userId2 = member1.getId(); + this.top1 = false; + this.top2 = false; + this.disable1 = false; + this.disable2 = false; + this.storeFlag1 = false; + this.storeFlag2 = false; + this.setId(SnowFlake.getIdStr()); + this.lastTalkTime = new Date(); + this.face1 = member2.getFace(); + this.face2 = member1.getFace(); + this.name1 = member2.getNickName(); + this.name2 = member1.getNickName(); + }else{ + this.userId1 = member1.getId(); + this.userId2 = member2.getId(); + this.top1 = false; + this.top2 = false; + this.disable1 = false; + this.disable2 = false; + this.storeFlag1 = false; + this.storeFlag2 = false; + this.setId(SnowFlake.getIdStr()); + this.lastTalkTime = new Date(); + this.face1 = member1.getFace(); + this.face2 = member2.getFace(); + this.name1 = member1.getNickName(); + this.name2 = member2.getNickName(); + } + } + public ImTalk(Member member, Store store){ + if(Long.parseLong(member.getId()) > Long.parseLong(store.getId())){ + this.userId1 = store.getId(); + this.userId2 = member.getId(); + this.top1 = false; + this.top2 = false; + this.disable1 = false; + this.disable2 = false; + this.storeFlag1 = true; + this.storeFlag2 = false; + this.setId(SnowFlake.getIdStr()); + this.lastTalkTime = new Date(); + this.face1 = store.getStoreLogo(); + this.face2 = member.getFace(); + this.name1 = store.getStoreName(); + this.name2 = member.getNickName(); + }else{ + this.userId1 = member.getId(); + this.userId2 = store.getId(); + this.top1 = false; + this.top2 = false; + this.disable1 = false; + this.disable2 = false; + this.storeFlag1 = false; + this.storeFlag2 = true; + this.setId(SnowFlake.getIdStr()); + this.lastTalkTime = new Date(); + this.face1 = member.getFace(); + this.face2 = store.getStoreLogo(); + this.name1 = member.getNickName(); + this.name2 = store.getStoreName(); + } + } } \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/im/service/ImMessageService.java b/framework/src/main/java/cn/lili/modules/im/service/ImMessageService.java index 57fee7cc..823369c0 100644 --- a/framework/src/main/java/cn/lili/modules/im/service/ImMessageService.java +++ b/framework/src/main/java/cn/lili/modules/im/service/ImMessageService.java @@ -16,29 +16,32 @@ public interface ImMessageService extends IService { /** * 阅读消息 * - * @param talkId - * @param accessToken + * @param talkId 对话Id + * @param accessToken 验证token */ void read(String talkId, String accessToken); /** * 未读消息列表 * - * @param accessToken + * @param accessToken 验证token + * @return 未读消息列表 */ List unReadMessages(String accessToken); /** * 历史消息 * - * @param accessToken - * @param to + * @param accessToken 验证token + * @param to 接收人 + * + * @return 历史消息列表 */ List historyMessage(String accessToken, String to); /** * 是否有新消息 - * @param accessToken + * @param accessToken 验证token * @return */ Boolean hasNewMessage(String accessToken); @@ -52,7 +55,7 @@ public interface ImMessageService extends IService { /** * 获取所有未读消息 - * @return + * @return 未读数量 */ Long unreadMessageCount(); diff --git a/framework/src/main/java/cn/lili/modules/im/service/ImTalkService.java b/framework/src/main/java/cn/lili/modules/im/service/ImTalkService.java index 427750fd..3b6f92ea 100644 --- a/framework/src/main/java/cn/lili/modules/im/service/ImTalkService.java +++ b/framework/src/main/java/cn/lili/modules/im/service/ImTalkService.java @@ -17,10 +17,9 @@ public interface ImTalkService extends IService { * 获取与某人的聊天框 * * @param userId1 - * @param userId2 * @return */ - ImTalk getTalkByUser(String userId1, String userId2); + ImTalk getTalkByUser(String userId1); /** * 置顶消息 @@ -41,7 +40,7 @@ public interface ImTalkService extends IService { * 获取用户聊天列表 * @return */ - List getUserTalkList(String userName); + List getUserTalkList(); /** * 获取商家聊天列表 diff --git a/framework/src/main/java/cn/lili/modules/im/serviceimpl/ImMessageServiceImpl.java b/framework/src/main/java/cn/lili/modules/im/serviceimpl/ImMessageServiceImpl.java index c216c282..6e75529d 100644 --- a/framework/src/main/java/cn/lili/modules/im/serviceimpl/ImMessageServiceImpl.java +++ b/framework/src/main/java/cn/lili/modules/im/serviceimpl/ImMessageServiceImpl.java @@ -4,6 +4,7 @@ import cn.lili.common.enums.ResultCode; import cn.lili.common.exception.ServiceException; import cn.lili.common.security.AuthUser; import cn.lili.common.security.context.UserContext; +import cn.lili.common.security.enums.UserEnums; import cn.lili.modules.im.entity.dos.ImMessage; import cn.lili.modules.im.entity.dto.MessageQueryParams; import cn.lili.modules.im.mapper.ImMessageMapper; @@ -18,9 +19,9 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import java.util.ArrayList; import java.util.Comparator; import java.util.List; +import java.util.Objects; /** * Im消息 业务实现 @@ -130,9 +131,18 @@ public class ImMessageServiceImpl extends ServiceImpl messageList) { - if (messageList.size() > 0) { + if (!messageList.isEmpty()) { + //判断用户类型 + AuthUser authUser = Objects.requireNonNull(UserContext.getCurrentUser()); + String toUserId = ""; + if(UserEnums.MEMBER.equals(authUser.getRole())){ + toUserId = authUser.getId(); + }else if(UserEnums.STORE.equals(authUser.getRole())){ + toUserId = authUser.getStoreId(); + } + //发送给自己的未读信息进行已读操作 for (ImMessage imMessage : messageList) { - if(Boolean.FALSE.equals(imMessage.getIsRead())){ + if(Boolean.FALSE.equals(imMessage.getIsRead()) && imMessage.getToUser().equals(toUserId)){ imMessage.setIsRead(true); } } diff --git a/framework/src/main/java/cn/lili/modules/im/serviceimpl/ImTalkServiceImpl.java b/framework/src/main/java/cn/lili/modules/im/serviceimpl/ImTalkServiceImpl.java index 2b24c8bb..bb59c584 100644 --- a/framework/src/main/java/cn/lili/modules/im/serviceimpl/ImTalkServiceImpl.java +++ b/framework/src/main/java/cn/lili/modules/im/serviceimpl/ImTalkServiceImpl.java @@ -21,9 +21,9 @@ import lombok.RequiredArgsConstructor; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import org.stringtemplate.v4.ST; import java.util.List; +import java.util.Objects; import java.util.stream.Collectors; /** @@ -45,47 +45,44 @@ public class ImTalkServiceImpl extends ServiceImpl impleme @Autowired private ImMessageService imMessageService; - @Override - public ImTalk getTalkByUser(String userId1, String userId2) { + public ImTalk getTalkByUser(String userId) { LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); - queryWrapper.eq(ImTalk::getUserId2, userId2); - queryWrapper.eq(ImTalk::getUserId1, userId1); + AuthUser currentUser = Objects.requireNonNull(UserContext.getCurrentUser()); + //登录用户的Id + String selfId = ""; + //查看当前用户角色对Id进行赋值 + if(UserEnums.STORE.equals(currentUser.getRole())){ + selfId = currentUser.getStoreId(); + }else if(UserEnums.MEMBER.equals(currentUser.getRole())){ + selfId = currentUser.getId(); + } + //小数在前保证永远是同一个对话 + String finalSelfId = selfId; + queryWrapper.and(wq-> wq.eq(ImTalk::getUserId2, userId).eq(ImTalk::getUserId1, finalSelfId).or().eq(ImTalk::getUserId2, finalSelfId).eq(ImTalk::getUserId1, userId)); ImTalk imTalk = this.getOne(queryWrapper); - AuthUser currentUser = UserContext.getCurrentUser(); //如果没有聊天,则创建聊天 if (imTalk == null) { - // 没有登录的这个账户信息 - if (currentUser == null) { - return null; - } //当自己为店铺时 if(UserEnums.STORE.equals(currentUser.getRole())){ - Store selfStore = storeService.getById(userId1); + Store selfStore = storeService.getById(selfId); //没有这个用户信息 - Member other = memberService.getById(userId2); + Member other = memberService.getById(userId); if(other == null){ return null; } //自己为店铺其他人必定为用户 - imTalk = new ImTalk(userId1, userId2, selfStore.getStoreLogo(), other.getFace(), selfStore.getStoreName(), other.getNickName()); - imTalk.setStoreFlag1(true); + imTalk = new ImTalk(other,selfStore); }else if(UserEnums.MEMBER.equals(currentUser.getRole())){ //没有这个店铺信息 - Member self = memberService.getById(userId1); - Member otherMember = memberService.getById(userId2); - Store otherStore = storeService.getById(userId2); - if(otherStore != null){ - imTalk = new ImTalk(userId1, userId2, self.getFace(), otherStore.getStoreLogo(), self.getNickName(), otherStore.getStoreName()); - imTalk.setStoreFlag2(true); - }else if (otherMember != null){ - imTalk = new ImTalk(userId1, userId2, self.getFace(), otherMember.getFace(), self.getNickName(), otherMember.getNickName()); - }else{ - return null; - } + Member self = memberService.getById(selfId); + Member otherMember = memberService.getById(userId); + Store otherStore = storeService.getById(userId); + if(otherStore != null){ + imTalk = new ImTalk(self, otherStore); + }else if (otherMember != null){ + imTalk = new ImTalk(self, otherMember); + } } - this.save(imTalk); - } else { - imTalk = check(imTalk); } return imTalk; } @@ -93,7 +90,7 @@ public class ImTalkServiceImpl extends ServiceImpl impleme /** * 发起聊天后,如果聊天不可见为true,则需要修正 * - * @param imTalk + * @param imTalk 对话信息 */ private ImTalk check(ImTalk imTalk) { if (imTalk.getDisable1() || imTalk.getDisable2()) { @@ -131,23 +128,16 @@ public class ImTalkServiceImpl extends ServiceImpl impleme } @Override - public List getUserTalkList(String userName) { + public List getUserTalkList() { AuthUser authUser = UserContext.getCurrentUser(); if(authUser == null){ throw new ServiceException(ResultCode.USER_NOT_LOGIN); } LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); - queryWrapper.and(wq->{ - wq.like(ImTalk::getName1, userName).or().like(ImTalk::getName2,userName); - }); - queryWrapper.and(wq->{ - wq.like(ImTalk::getUserId1, authUser.getId()).or().like(ImTalk::getUserId2,authUser.getId()); - }); + queryWrapper.and(wq-> wq.eq(ImTalk::getUserId1, authUser.getId()).or().eq(ImTalk::getUserId2,authUser.getId())); queryWrapper.orderByDesc(ImTalk::getLastTalkTime); List imTalks = this.list(queryWrapper); - List imTalkVOList = imTalks.stream().map(imTalk -> { - return new ImTalkVO(imTalk, authUser.getId()); - }).collect(Collectors.toList()); + List imTalkVOList = imTalks.stream().map(imTalk -> new ImTalkVO(imTalk, authUser.getId())).collect(Collectors.toList()); getUnread(imTalkVOList); return imTalkVOList; } @@ -160,7 +150,7 @@ public class ImTalkServiceImpl extends ServiceImpl impleme } LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); queryWrapper.and(wq->{ - wq.like(ImTalk::getUserId1, authUser.getStoreId()).or().like(ImTalk::getUserId2,authUser.getStoreId()); + wq.eq(ImTalk::getUserId1, authUser.getStoreId()).or().eq(ImTalk::getUserId2,authUser.getStoreId()); }); queryWrapper.orderByDesc(ImTalk::getLastTalkTime); List imTalks = this.list(queryWrapper); @@ -174,12 +164,12 @@ public class ImTalkServiceImpl extends ServiceImpl impleme /** * 获取未读消息数量 - * @param imTalkVOList + * @param imTalkVOList 消息列表 */ private void getUnread(List imTalkVOList){ - if(imTalkVOList.size() > 0){ + if(!imTalkVOList.isEmpty()){ for (ImTalkVO imTalkVO : imTalkVOList) { - long count = imMessageService.count(new LambdaQueryWrapper().eq(ImMessage::getFromUser, imTalkVO.getUserId()).eq(ImMessage::getIsRead, false)); + long count = imMessageService.count(new LambdaQueryWrapper().eq(ImMessage::getFromUser, imTalkVO.getUserId()).eq(ImMessage::getTalkId,imTalkVO.getId()).eq(ImMessage::getIsRead, false)); imTalkVO.setUnread(count); } } diff --git a/framework/src/main/java/cn/lili/modules/im/token/SeatTokenGenerate.java b/framework/src/main/java/cn/lili/modules/im/token/SeatTokenGenerate.java index 482bcded..caa0a153 100644 --- a/framework/src/main/java/cn/lili/modules/im/token/SeatTokenGenerate.java +++ b/framework/src/main/java/cn/lili/modules/im/token/SeatTokenGenerate.java @@ -23,20 +23,23 @@ public class SeatTokenGenerate extends AbstractTokenGenerate { @Override public Token createToken(Seat seat, Boolean longTerm) { - AuthUser authUser = new AuthUser( - seat.getUsername(), - seat.getId(), - seat.getNickName(), - seat.getFace(), - UserEnums.SEAT); - authUser.setTenantId(seat.getTenantId()); + AuthUser authUser = AuthUser.builder() + .username(seat.getUsername()) + .id(seat.getId()) + .nickName(seat.getNickName()) + .face(seat.getFace()) + .role(UserEnums.SEAT) + .longTerm(longTerm) + .tenantId(seat.getTenantId()) + .build(); + //登陆成功生成token - return tokenUtil.createToken(seat.getUsername(), authUser, longTerm, UserEnums.SEAT); + return tokenUtil.createToken(authUser); } @Override public Token refreshToken(String refreshToken) { - return tokenUtil.refreshToken(refreshToken, UserEnums.SEAT); + return tokenUtil.refreshToken(refreshToken); } } diff --git a/framework/src/main/java/cn/lili/modules/kdBrid/service/KdNiaoService.java b/framework/src/main/java/cn/lili/modules/kdBrid/service/KdNiaoService.java deleted file mode 100644 index 0bb776b8..00000000 --- a/framework/src/main/java/cn/lili/modules/kdBrid/service/KdNiaoService.java +++ /dev/null @@ -1,17 +0,0 @@ -package cn.lili.modules.kdBrid.service; - -/** - * 快递鸟电子面单业务层实现 - * - * @author chc - * @since 2022-4-12 10:12:43 - */ -public interface KdNiaoService { - /** - * 生成电子面单 - * @param orderSn 订单编号 - * @param logisticsId 物流公司 - * @return 电子面单模板 - */ - String createElectronicsFaceSheet(String orderSn, String logisticsId) throws Exception; -} diff --git a/framework/src/main/java/cn/lili/modules/logistics/LogisticsPlugin.java b/framework/src/main/java/cn/lili/modules/logistics/LogisticsPlugin.java new file mode 100644 index 00000000..5a0ab4e7 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/logistics/LogisticsPlugin.java @@ -0,0 +1,58 @@ +package cn.lili.modules.logistics; + +import cn.lili.modules.logistics.entity.dto.LabelOrderDTO; +import cn.lili.modules.logistics.entity.enums.LogisticsEnum; +import cn.lili.modules.order.order.entity.vo.OrderDetailVO; +import cn.lili.modules.system.entity.dos.Logistics; +import cn.lili.modules.system.entity.vo.Traces; + +import java.util.Map; + +/** + * 物流插件接口 + * + * @author Bulbasaur + * @author Bulbasaur + * @since 2023-02-16 + */ +public interface LogisticsPlugin { + + + /** + * 插件名称 + */ + LogisticsEnum pluginName(); + + /** + * 实时查询快递 + * + * @param logistics 物流公司 + * @param expNo + * @param phone + * @return 物流信息 + */ + Traces pollQuery(Logistics logistics, String expNo, String phone); + + /** + * 实时查询地图轨迹 + * + * @param logistics 物流公司 + * @param expNo 单号 + * @param phone 收件人手机号 + * @param from 出发地信息,最小颗粒到市级,例如:广东省深圳市 + * @param to 目的地信息,最小颗粒到市级,例如:广东省深圳市 + * @return 物流信息 + */ + Traces pollMapTrack(Logistics logistics, String expNo, String phone, String from, String to); + + /** + * 电子面单打印 + * + * @param labelOrderDTO 电子面单DTO + * @return + */ + Map labelOrder(LabelOrderDTO labelOrderDTO); + + String createOrder(OrderDetailVO orderDetailVO); + +} diff --git a/framework/src/main/java/cn/lili/modules/logistics/LogisticsPluginFactory.java b/framework/src/main/java/cn/lili/modules/logistics/LogisticsPluginFactory.java new file mode 100644 index 00000000..4ecc4bfe --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/logistics/LogisticsPluginFactory.java @@ -0,0 +1,59 @@ +package cn.lili.modules.logistics; + +import cn.hutool.json.JSONUtil; +import cn.lili.common.exception.ServiceException; +import cn.lili.modules.logistics.entity.enums.LogisticsEnum; +import cn.lili.modules.logistics.plugin.kdniao.KdniaoPlugin; +import cn.lili.modules.logistics.plugin.kuaidi100.Kuaidi100Plugin; +import cn.lili.modules.logistics.plugin.shunfeng.ShunfengPlugin; +import cn.lili.modules.system.entity.dos.Setting; +import cn.lili.modules.system.entity.dto.LogisticsSetting; +import cn.lili.modules.system.entity.enums.SettingEnum; +import cn.lili.modules.system.service.SettingService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +/** + * 文件服务抽象工厂 直接返回操作类 + * + * @author Bulbasaur + * @version v1.0 + * 2022-06-06 11:35 + */ + +@Component +public class LogisticsPluginFactory { + + + @Autowired + private SettingService settingService; + + + /** + * 获取logistics client + * + * @return + */ + public LogisticsPlugin filePlugin() { + + LogisticsSetting logisticsSetting = null; + try { + Setting setting = settingService.get(SettingEnum.LOGISTICS_SETTING.name()); + logisticsSetting = JSONUtil.toBean(setting.getSettingValue(), LogisticsSetting.class); + switch (LogisticsEnum.valueOf(logisticsSetting.getType())) { + case KDNIAO: + return new KdniaoPlugin(logisticsSetting); + case KUAIDI100: + return new Kuaidi100Plugin(logisticsSetting); + case SHUNFENG: + return new ShunfengPlugin(logisticsSetting); + default: + throw new ServiceException(); + } + } catch (Exception e) { + throw new ServiceException(); + } + } + + +} diff --git a/framework/src/main/java/cn/lili/modules/logistics/entity/dto/LabelOrderDTO.java b/framework/src/main/java/cn/lili/modules/logistics/entity/dto/LabelOrderDTO.java new file mode 100644 index 00000000..c1072d65 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/logistics/entity/dto/LabelOrderDTO.java @@ -0,0 +1,31 @@ +package cn.lili.modules.logistics.entity.dto; + +import cn.lili.modules.order.order.entity.dos.Order; +import cn.lili.modules.order.order.entity.dos.OrderItem; +import cn.lili.modules.store.entity.dos.StoreLogistics; +import cn.lili.modules.store.entity.dto.StoreDeliverGoodsAddressDTO; +import cn.lili.modules.system.entity.dos.Logistics; +import lombok.Data; + +import java.util.List; + +/** + * 电子面单DTO + * + * @author Bulbasaur + * @since 2023-02-16 + */ +@Data +public class LabelOrderDTO { + + //订单 + Order order; + //订单货物 + List orderItems; + //物流公司 + Logistics logistics; + //店铺物流公司配置 + StoreLogistics storeLogistics; + //店铺发件地址 + StoreDeliverGoodsAddressDTO storeDeliverGoodsAddressDTO; +} diff --git a/framework/src/main/java/cn/lili/modules/logistics/entity/enums/LogisticsEnum.java b/framework/src/main/java/cn/lili/modules/logistics/entity/enums/LogisticsEnum.java new file mode 100644 index 00000000..20c9bba8 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/logistics/entity/enums/LogisticsEnum.java @@ -0,0 +1,15 @@ +package cn.lili.modules.logistics.entity.enums; + +/** + * LogisticsEnum + * + * @author Bulbasaur + * @version v1.0 + * 2022-06-06 11:23 + */ +public enum LogisticsEnum { + /** + * 快递查询渠道 + */ + KDNIAO, KUAIDI100,SHUNFENG; +} diff --git a/framework/src/main/java/cn/lili/modules/kdBrid/serviceImpl/KdNiaoServiceImpl.java b/framework/src/main/java/cn/lili/modules/logistics/plugin/kdniao/KdniaoPlugin.java similarity index 60% rename from framework/src/main/java/cn/lili/modules/kdBrid/serviceImpl/KdNiaoServiceImpl.java rename to framework/src/main/java/cn/lili/modules/logistics/plugin/kdniao/KdniaoPlugin.java index 482fcf2d..4b567388 100644 --- a/framework/src/main/java/cn/lili/modules/kdBrid/serviceImpl/KdNiaoServiceImpl.java +++ b/framework/src/main/java/cn/lili/modules/logistics/plugin/kdniao/KdniaoPlugin.java @@ -1,35 +1,23 @@ -package cn.lili.modules.kdBrid.serviceImpl; +package cn.lili.modules.logistics.plugin.kdniao; import cn.hutool.core.text.CharSequenceUtil; import cn.lili.common.enums.ResultCode; import cn.lili.common.exception.ServiceException; -import cn.lili.common.security.OperationalJudgment; -import cn.lili.modules.kdBrid.service.KdNiaoService; -import cn.lili.modules.member.service.StoreLogisticsService; -import cn.lili.modules.order.order.aop.OrderLogPoint; +import cn.lili.modules.logistics.LogisticsPlugin; +import cn.lili.modules.logistics.entity.dto.LabelOrderDTO; +import cn.lili.modules.logistics.entity.enums.LogisticsEnum; import cn.lili.modules.order.order.entity.dos.Order; import cn.lili.modules.order.order.entity.dos.OrderItem; -import cn.lili.modules.order.order.entity.enums.DeliverStatusEnum; -import cn.lili.modules.order.order.entity.enums.OrderStatusEnum; -import cn.lili.modules.order.order.service.OrderItemService; -import cn.lili.modules.order.order.service.OrderService; +import cn.lili.modules.order.order.entity.vo.OrderDetailVO; import cn.lili.modules.store.entity.dos.StoreLogistics; import cn.lili.modules.store.entity.dto.StoreDeliverGoodsAddressDTO; -import cn.lili.modules.store.service.StoreDetailService; import cn.lili.modules.system.entity.dos.Logistics; -import cn.lili.modules.system.entity.dos.Setting; -import cn.lili.modules.system.entity.dto.KuaidiSetting; -import cn.lili.modules.system.entity.enums.SettingEnum; -import cn.lili.modules.system.service.LogisticsService; -import cn.lili.modules.system.service.SettingService; +import cn.lili.modules.system.entity.dto.LogisticsSetting; +import cn.lili.modules.system.entity.vo.Traces; +import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; -import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; -import com.baomidou.mybatisplus.core.toolkit.Wrappers; -import com.google.gson.Gson; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; import java.io.*; import java.net.HttpURLConnection; @@ -42,111 +30,117 @@ import java.util.List; import java.util.Map; /** - * 快递鸟电子面单业务层实现 + * 快递鸟插件 * - * @author chc - * @since 2022-4-12 10:12:43 + * @author Bulbasaur */ -@Service @Slf4j -public class KdNiaoServiceImpl implements KdNiaoService { - /** - * 订单货物 - */ - @Autowired - OrderItemService orderItemService; +public class KdniaoPlugin implements LogisticsPlugin { - /** - * 订单 - */ @Autowired - OrderService orderService; - - /** - * 物流公司 - */ - @Autowired - LogisticsService logisticsService; - - /** - * 商家店铺 - */ - @Autowired - StoreDetailService storeDetailService; - - /** - * 配置 - */ - @Autowired - SettingService settingService; - - /** - * 店铺-物流 - */ - @Autowired - StoreLogisticsService storeLogisticsService; + private LogisticsSetting logisticsSetting; + public KdniaoPlugin(LogisticsSetting logisticsSetting) { + this.logisticsSetting = logisticsSetting; + } @Override - @OrderLogPoint(description = "'订单['+#orderSn+']发货,打印电子面单'", orderSn = "#orderSn") - @Transactional(rollbackFor = Exception.class) - public String createElectronicsFaceSheet(String orderSn, String logisticsId) throws Exception { - //电子面单模板 - String printTemplate = null; - //获取订单及子订单 - Order order = OperationalJudgment.judgment(orderService.getBySn(orderSn)); - List orderItems = orderItemService.getByOrderSn(orderSn); + public LogisticsEnum pluginName() { + return LogisticsEnum.KDNIAO; + } - Setting setting = settingService.get(SettingEnum.KUAIDI_SETTING.name()); - if (CharSequenceUtil.isBlank(setting.getSettingValue())) { - throw new ServiceException(ResultCode.LOGISTICS_NOT_SETTING); + @Override + public Traces pollQuery(Logistics logistics, String expNo, String phone) { + try { + String requestData = "{'OrderCode':'','ShipperCode':'" + logistics.getCode() + + "','LogisticCode':'" + expNo + "'" + + ",'CustomerName':'" + phone.substring(phone.length() - 4) + "'" + + "}"; + //请求地址-测试地址 + String testReqURL = "http://sandboxapi.kdniao.com:8080/kdniaosandbox/gateway/exterfaceInvoke.json"; + //请求地址-正式地址 + String reqURL = "https://api.kdniao.com/Ebusiness/EbusinessOrderHandle.aspx"; + Map params = new HashMap<>(8); + params.put("RequestData", urlEncoder(requestData, "UTF-8")); + params.put("EBusinessID", logisticsSetting.getKdniaoEbusinessID()); + params.put("RequestType", "1002"); + String dataSign = encrypt(requestData, logisticsSetting.getKdniaoAppKey(), "UTF-8"); + params.put("DataSign", urlEncoder(dataSign, "UTF-8")); + params.put("DataType", "2"); + + String result = sendPost(reqURL, params); + Map map = (Map) JSON.parse(result); + return new Traces(logistics.getName(), expNo, (List) map.get("Traces")); + } catch (Exception e) { + e.printStackTrace(); } - KuaidiSetting kuaidiSetting = new Gson().fromJson(setting.getSettingValue(), KuaidiSetting.class); + return null; + } - //ID - String EBusinessID = kuaidiSetting.getEbusinessID(); - - //KEY - String AppKey = kuaidiSetting.getAppKey(); - - //请求url - String ReqURL = kuaidiSetting.getSheetReqURL(); - - //如果订单未发货,并且订单状态值等于待发货 - if (order.getDeliverStatus().equals(DeliverStatusEnum.UNDELIVERED.name()) && order.getOrderStatus().equals(OrderStatusEnum.UNDELIVERED.name())) { + @Override + public Traces pollMapTrack(Logistics logistics, String expNo, String phone, String from, String to) { + try { + //请求地址-测试地址 + String testReqURL = "http://sandboxapi.kdniao.com:8080/kdniaosandbox/gateway/exterfaceInvoke.json"; + //请求地址-正式地址 + String reqURL = "https://api.kdniao.com/Ebusiness/EbusinessOrderHandle.aspx"; + String RequestData = "{" + + "'OrderCode': ''," + + "'CustomerName': '" + phone.substring(phone.length() - 4) + "'," + + "'ShipperCode': '" + logistics.getCode() + "'," + + "'LogisticCode': '" + expNo + "'," + + "'SenderCityName': '" + from + "'," + + "'ReceiverCityName': '" + to + "'," + + "'IsReturnCoordinates': 1," + + "'IsReturnRouteMap': 1," + + "}"; + // 组装系统级参数 + Map params = new HashMap(); + params.put("RequestData", urlEncoder(RequestData, "UTF-8")); + params.put("EBusinessID", logisticsSetting.getKdniaoEbusinessID()); + params.put("RequestType", "8003"); + String dataSign = encrypt(RequestData, logisticsSetting.getKdniaoAppKey(), "UTF-8"); + params.put("DataSign", urlEncoder(dataSign, "UTF-8")); + // params.put("DataType", "2"); + // 以form表单形式提交post请求,post请求体中包含了应用级参数和系统级参数 + String result = sendPost(reqURL, params); + log.error(result); + } catch (Exception e) { + e.printStackTrace(); + } + return null; + } + @Override + public Map labelOrder(LabelOrderDTO labelOrderDTO) { + try { + Map resultMap = new HashMap(); + //订单 + Order order = labelOrderDTO.getOrder(); + //订单货物 + List orderItems = labelOrderDTO.getOrderItems(); //获取对应物流 - Logistics logistics = logisticsService.getById(logisticsId); - - //物流为空,抛出异常 - if (logistics == null) { - throw new ServiceException(ResultCode.ORDER_LOGISTICS_ERROR); - } - - //获取店家的物流信息 - LambdaQueryWrapper lambdaQueryWrapper = Wrappers.lambdaQuery(); - lambdaQueryWrapper.eq(StoreLogistics::getLogisticsId, logisticsId); - lambdaQueryWrapper.eq(StoreLogistics::getStoreId, order.getStoreId()); - StoreLogistics storeLogistics = storeLogisticsService.getOne(lambdaQueryWrapper); - - //获取店家信息 - StoreDeliverGoodsAddressDTO storeDeliverGoodsAddressDTO = storeDetailService.getStoreDeliverGoodsAddressDto(order.getStoreId()); + Logistics logistics = labelOrderDTO.getLogistics(); //收件人地址 String[] ConsigneeAddress = order.getConsigneeAddressPath().split(","); + //获取店家信息 + StoreDeliverGoodsAddressDTO storeDeliverGoodsAddressDTO = labelOrderDTO.getStoreDeliverGoodsAddressDTO(); //发件人地址 String[] consignorAddress = storeDeliverGoodsAddressDTO.getSalesConsignorAddressPath().split(","); + //店铺-物流公司设置 + StoreLogistics storeLogistics = labelOrderDTO.getStoreLogistics(); //组装快递鸟应用级参数 String resultDate = "{" + - "'OrderCode': '" + orderSn + "'," + //订单编码 + "'OrderCode': '" + order.getSn() + "'," + //订单编码 "'ShipperCode': '" + logistics.getCode() + "'," + //快递公司编码 - "'CustomerName': '"+storeLogistics.getCustomerName()+"'," +//客户编码 - "'CustomerPwd': '"+storeLogistics.getCustomerPwd()+"'," + //客户密码 - "'MonthCode': '"+storeLogistics.getMonthCode()+"'," + //密钥 - "'SendSite': '"+storeLogistics.getSendSite()+"'," + //归属网点 - "'SendStaff': '"+storeLogistics.getSendStaff()+"'," + //收件快递员 - "'PayType': "+storeLogistics.getPayType()+"," + - "'ExpType': "+storeLogistics.getExpType()+"," + + "'CustomerName': '" + storeLogistics.getCustomerName() + "'," +//客户编码 + "'CustomerPwd': '" + storeLogistics.getCustomerPwd() + "'," + //客户密码 + "'MonthCode': '" + storeLogistics.getMonthCode() + "'," + //密钥 + "'SendSite': '" + storeLogistics.getSendSite() + "'," + //归属网点 + "'SendStaff': '" + storeLogistics.getSendStaff() + "'," + //收件快递员 + "'PayType': " + storeLogistics.getPayType() + "," + + "'ExpType': " + storeLogistics.getExpType() + "," + //发件人信息 "'Sender': {" + "'Name': '" + storeDeliverGoodsAddressDTO.getSalesConsignorName() + "'," + @@ -176,45 +170,55 @@ public class KdNiaoServiceImpl implements KdNiaoService { "},"; } resultDate = resultDate + "]," + - "'Quantity': "+orderItems.size()+"," + //包裹数 - "'IsReturnPrintTemplate':1,"+ //生成电子面单模板 - "'Remark': '" + order.getRemark() + "'"+//商家备注 + "'Quantity': " + orderItems.size() + "," + //包裹数 + "'IsReturnPrintTemplate':1," + //生成电子面单模板 + "'Remark': '" + order.getRemark() + "'" +//商家备注 "}"; //组织系统级参数 Map params = new HashMap<>(); + //请求地址-测试地址 + String testReqURL = "http://sandboxapi.kdniao.com:8080/kdniaosandbox/gateway/exterfaceInvoke.json"; + //请求地址-正式地址 + String reqURL = "https://api.kdniao.com/api/EOrderService"; + //进行格式加密 params.put("RequestData", urlEncoder(resultDate, "UTF-8")); - params.put("EBusinessID", EBusinessID); + params.put("EBusinessID", logisticsSetting.getKdniaoEbusinessID()); params.put("RequestType", "1007"); - String dataSign = encrypt(resultDate, AppKey, "UTF-8"); + String dataSign = encrypt(resultDate, logisticsSetting.getKdniaoAppKey(), "UTF-8"); params.put("DataSign", dataSign); params.put("DataType", "2"); // 以form表单形式提交post请求,post请求体中包含了应用级参数和系统级参数 - String result = sendPost(ReqURL, params); - if(CharSequenceUtil.isEmpty(result) || CharSequenceUtil.isBlank(result)){ + String result = sendPost(reqURL, params); + if (CharSequenceUtil.isEmpty(result) || CharSequenceUtil.isBlank(result)) { throw new ServiceException(ResultCode.LOGISTICS_CHECK_SETTING); } //根据公司业务处理返回的信息...... JSONObject obj = JSONObject.parseObject(result); - log.info("电子面单响应:{}",result); - if(!"100".equals(obj.getString("ResultCode"))){ - return obj.getString("Reason"); + log.info("电子面单响应:{}", result); + if (!"100".equals(obj.getString("ResultCode"))) { + resultMap.put("Reason",obj.getString("Reason")); + return resultMap; } JSONObject orderJson = JSONObject.parseObject(obj.getString("Order")); - - //电子面单模板 - printTemplate = obj.getString("PrintTemplate"); - - //进行发货 - orderService.delivery(orderSn, orderJson.getString("LogisticCode"), logisticsId); + resultMap.put("printTemplate",obj.getString("PrintTemplate")); + return resultMap; + } catch (Exception e) { + e.printStackTrace(); } - return printTemplate; + return null; } + @Override + public String createOrder(OrderDetailVO orderDetailVO) { + return null; + } + + /** * MD5加密 * @@ -323,7 +327,7 @@ public class KdNiaoServiceImpl implements KdNiaoService { result.append(line); } } catch (Exception e) { - e.printStackTrace(); + log.error("向指定 URL 发送POST方法的请求错误", e); } //使用finally块来关闭输出流、输入流 finally { @@ -381,5 +385,4 @@ public class KdNiaoServiceImpl implements KdNiaoService { } return sb.toString(); } - } diff --git a/framework/src/main/java/cn/lili/modules/logistics/plugin/kuaidi100/Kuaidi100Plugin.java b/framework/src/main/java/cn/lili/modules/logistics/plugin/kuaidi100/Kuaidi100Plugin.java new file mode 100644 index 00000000..b66980f8 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/logistics/plugin/kuaidi100/Kuaidi100Plugin.java @@ -0,0 +1,191 @@ +package cn.lili.modules.logistics.plugin.kuaidi100; + +import cn.lili.modules.logistics.LogisticsPlugin; +import cn.lili.modules.logistics.entity.dto.LabelOrderDTO; +import cn.lili.modules.logistics.entity.enums.LogisticsEnum; +import cn.lili.modules.logistics.plugin.kuaidi100.utils.Kuaidi100SignUtils; +import cn.lili.modules.order.order.entity.dos.Order; +import cn.lili.modules.order.order.entity.dos.OrderItem; +import cn.lili.modules.order.order.entity.vo.OrderDetailVO; +import cn.lili.modules.store.entity.dos.StoreLogistics; +import cn.lili.modules.store.entity.dto.StoreDeliverGoodsAddressDTO; +import cn.lili.modules.system.entity.dos.Logistics; +import cn.lili.modules.system.entity.dto.LogisticsSetting; +import cn.lili.modules.system.entity.vo.Traces; +import com.google.gson.Gson; +import com.kuaidi100.sdk.api.LabelV2; +import com.kuaidi100.sdk.api.QueryTrack; +import com.kuaidi100.sdk.api.QueryTrackMap; +import com.kuaidi100.sdk.contant.ApiInfoConstant; +import com.kuaidi100.sdk.contant.PrintType; +import com.kuaidi100.sdk.core.IBaseClient; +import com.kuaidi100.sdk.pojo.HttpResult; +import com.kuaidi100.sdk.request.ManInfo; +import com.kuaidi100.sdk.request.PrintReq; +import com.kuaidi100.sdk.request.QueryTrackParam; +import com.kuaidi100.sdk.request.QueryTrackReq; +import com.kuaidi100.sdk.request.labelV2.OrderReq; +import com.kuaidi100.sdk.response.QueryTrackData; +import com.kuaidi100.sdk.response.QueryTrackMapResp; +import com.kuaidi100.sdk.response.QueryTrackResp; +import com.kuaidi100.sdk.response.samecity.OrderResp; +import com.kuaidi100.sdk.utils.SignUtils; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * 快递100插件 + * + * @author Bulbasaur + */ +@Slf4j +public class Kuaidi100Plugin implements LogisticsPlugin { + + + @Autowired + private LogisticsSetting logisticsSetting; + + public Kuaidi100Plugin(LogisticsSetting logisticsSetting) { + this.logisticsSetting = logisticsSetting; + } + + @Override + public LogisticsEnum pluginName() { + return LogisticsEnum.KUAIDI100; + } + + @Override + public Traces pollQuery(Logistics logistics, String expNo, String phone) { + try { + QueryTrackReq queryTrackReq = new QueryTrackReq(); + QueryTrackParam queryTrackParam = new QueryTrackParam(); + queryTrackParam.setCom(logistics.getCode()); + queryTrackParam.setNum(expNo); + queryTrackParam.setPhone(phone); + String param = new Gson().toJson(queryTrackParam); + + queryTrackReq.setParam(param); + queryTrackReq.setCustomer(logisticsSetting.getKuaidi100Customer()); + queryTrackReq.setSign(Kuaidi100SignUtils.querySign(param, logisticsSetting.getKuaidi100Key(), logisticsSetting.getKuaidi100Customer())); + + IBaseClient baseClient = new QueryTrack(); + HttpResult httpResult = baseClient.execute(queryTrackReq); + QueryTrackResp queryTrackResp = new Gson().fromJson(httpResult.getBody(), QueryTrackResp.class); + log.info(String.valueOf(queryTrackResp.getData())); + + List traces = new ArrayList<>(); + for (QueryTrackData queryTrackData : queryTrackResp.getData()) { + Map map = new HashMap(); + map.put("AcceptTime", queryTrackData.getTime()); + map.put("AcceptStation", queryTrackData.getContext()); + map.put("Remark", null); + traces.add(map); + } + return new Traces(logistics.getName(), expNo, traces); + } catch (Exception e) { + e.printStackTrace(); + } + return null; + } + + @Override + public Traces pollMapTrack(Logistics logistics, String expNo, String phone, String from, String to) { + try { + QueryTrackReq queryTrackReq = new QueryTrackReq(); + QueryTrackParam queryTrackParam = new QueryTrackParam(); + queryTrackParam.setCom(logistics.getCode()); + queryTrackParam.setNum(expNo); + queryTrackParam.setPhone(phone); + queryTrackParam.setFrom(from); + queryTrackParam.setTo(to); + queryTrackParam.setResultv2("5"); + String param = new Gson().toJson(queryTrackParam); + + queryTrackReq.setParam(param); + queryTrackReq.setCustomer(logisticsSetting.getKuaidi100Customer()); + queryTrackReq.setSign(SignUtils.querySign(param, logisticsSetting.getKuaidi100Key(), logisticsSetting.getKuaidi100Customer())); + + IBaseClient baseClient = new QueryTrackMap(); + HttpResult result = baseClient.execute(queryTrackReq); + + QueryTrackMapResp queryTrackMapResp = new Gson().fromJson(result.getBody(), QueryTrackMapResp.class); + System.out.println(queryTrackMapResp); + } catch (Exception e) { + e.printStackTrace(); + } + return null; + } + + @Override + public Map labelOrder(LabelOrderDTO labelOrderDTO) { + try { + //订单 + Order order = labelOrderDTO.getOrder(); + //订单货物 + List orderItems = labelOrderDTO.getOrderItems(); + //获取对应物流 + Logistics logistics = labelOrderDTO.getLogistics(); + //收件人地址 + String[] consigneeAddress = order.getConsigneeAddressPath().split(","); + //获取店家信息 + StoreDeliverGoodsAddressDTO storeDeliverGoodsAddressDTO = labelOrderDTO.getStoreDeliverGoodsAddressDTO(); + //发件人地址 + String[] consignorAddress = storeDeliverGoodsAddressDTO.getSalesConsignorAddressPath().split(","); + //店铺-物流公司设置 + StoreLogistics storeLogistics = labelOrderDTO.getStoreLogistics(); + + + ManInfo recManInfo = new ManInfo(); + recManInfo.setName(order.getConsigneeName()); + recManInfo.setMobile(order.getConsigneeMobile()); + recManInfo.setPrintAddr(consigneeAddress[0] + consigneeAddress[1] + consigneeAddress[2] + consigneeAddress[3] + order.getConsigneeDetail()); + + ManInfo sendManInfo = new ManInfo(); + sendManInfo.setName(storeDeliverGoodsAddressDTO.getSalesConsignorName()); + sendManInfo.setMobile(storeDeliverGoodsAddressDTO.getSalesConsignorMobile()); + sendManInfo.setPrintAddr(consignorAddress[0] + consignorAddress[1] + consignorAddress[2] + consignorAddress[3] + storeDeliverGoodsAddressDTO.getSalesConsignorDetail()); + + OrderReq orderReq = new OrderReq(); + orderReq.setKuaidicom(logistics.getCode()); + orderReq.setCount(1); + // orderReq.setSiid(siid); + //orderReq.setTempId("60f6c17c7c223700131d8bc3"); + orderReq.setSendMan(sendManInfo); + orderReq.setRecMan(recManInfo); + + orderReq.setPrintType(PrintType.CLOUD); + + String param = new Gson().toJson(orderReq); + String t = System.currentTimeMillis() + ""; + + PrintReq printReq = new PrintReq(); + printReq.setT(t); + printReq.setKey(logisticsSetting.getKuaidi100Key()); + printReq.setSign(SignUtils.printSign(param, t, logisticsSetting.getKuaidi100Key(), logisticsSetting.getKuaidi100Customer())); + printReq.setMethod(ApiInfoConstant.ORDER); + printReq.setParam(param); + + IBaseClient baseClient = new LabelV2(); + HttpResult result = baseClient.execute(printReq); + System.out.println(result.getBody()); + QueryTrackMapResp queryTrackMapResp = new Gson().fromJson(result.getBody(), QueryTrackMapResp.class); + OrderResp orderResp = new Gson().fromJson(result.getBody(), OrderResp.class); + + } catch (Exception e) { + e.printStackTrace(); + } + return null; + } + + @Override + public String createOrder(OrderDetailVO orderDetailVO) { + return null; + } + + +} diff --git a/framework/src/main/java/cn/lili/modules/logistics/plugin/kuaidi100/utils/Kuaidi100SignUtils.java b/framework/src/main/java/cn/lili/modules/logistics/plugin/kuaidi100/utils/Kuaidi100SignUtils.java new file mode 100644 index 00000000..b90470ec --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/logistics/plugin/kuaidi100/utils/Kuaidi100SignUtils.java @@ -0,0 +1,73 @@ +package cn.lili.modules.logistics.plugin.kuaidi100.utils; + +import org.apache.commons.codec.digest.DigestUtils; + +/** + * @Author: api.kuaidi100.com + * @Date: 2020-07-14 16:54 + */ +public class Kuaidi100SignUtils { + + /** + * 快递100加密方式统一为MD5后转大写 + * + * @param msg + * @return + */ + public static String sign(String msg) { + return DigestUtils.md5Hex(msg).toUpperCase(); + } + + /** + * 查询加密 + * + * @param param + * @param key + * @param customer + * @return + */ + public static String querySign(String param, String key, String customer) { + return sign(param + key + customer); + } + + /** + * 打印/下单 加密 + * + * @param param + * @param t + * @param key + * @param secret + * @return: java.lang.String + * @author: api.kuaidi100.com + * @time: 2020/12/3 9:23 + */ + public static String printSign(String param, String t, String key, String secret) { + return sign(param + t + key + secret); + } + + /** + * 云平台 加密 + * + * @param key + * @param secret + * @return: java.lang.String + * @author: api.kuaidi100.com + * @time: 2020/12/3 9:23 + */ + public static String cloudSign(String key, String secret) { + return sign(key + secret); + } + + /** + * 短信加密 + * + * @param key + * @param userId + * @return: java.lang.String + * @author: api.kuaidi100.com + * @time: 2020/12/3 9:32 + */ + public static String smsSign(String key, String userId) { + return sign(key + userId); + } +} diff --git a/framework/src/main/java/cn/lili/modules/logistics/plugin/shunfeng/ShunfengPlugin.java b/framework/src/main/java/cn/lili/modules/logistics/plugin/shunfeng/ShunfengPlugin.java new file mode 100644 index 00000000..f813baad --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/logistics/plugin/shunfeng/ShunfengPlugin.java @@ -0,0 +1,242 @@ +package cn.lili.modules.logistics.plugin.shunfeng; + +import cn.hutool.json.JSONArray; +import cn.hutool.json.JSONObject; +import cn.hutool.json.JSONUtil; +import cn.lili.common.enums.ResultCode; +import cn.lili.common.exception.ServiceException; +import cn.lili.common.utils.SpringContextUtil; +import cn.lili.modules.logistics.LogisticsPlugin; +import cn.lili.modules.logistics.entity.dto.LabelOrderDTO; +import cn.lili.modules.logistics.entity.enums.LogisticsEnum; +import cn.lili.modules.order.order.entity.dos.Order; +import cn.lili.modules.order.order.entity.dos.OrderItem; +import cn.lili.modules.order.order.entity.vo.OrderDetailVO; +import cn.lili.modules.store.entity.dto.StoreDeliverGoodsAddressDTO; +import cn.lili.modules.store.service.StoreDetailService; +import cn.lili.modules.system.entity.dos.Logistics; +import cn.lili.modules.system.entity.dto.LogisticsSetting; +import cn.lili.modules.system.entity.vo.Traces; +import com.sf.csim.express.service.CallExpressServiceTools; +import com.sf.csim.express.service.HttpClientUtil; +import com.sf.csim.express.service.IServiceCodeStandard; +import com.sf.csim.express.service.code.ExpressServiceCodeEnum; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +import java.io.UnsupportedEncodingException; +import java.util.*; + +/** + * 顺丰插件 + * @author admin + */ +@Slf4j +public class ShunfengPlugin implements LogisticsPlugin { + + /** + * ExpressServiceCodeEnum 对应速运类-快递APIs + * POSTServiceCodeEnum 对应速运类-驿站APIs + * YJTServiceCodeEnum 对应解决方案-医寄通APIs + * EPSServiceCodeEnum 对应解决方案-快递管家APIs + * 详情见code目录下枚举类,客户可自行修改引用的该类 + **/ + private LogisticsSetting logisticsSetting; + + public ShunfengPlugin(){} + + public ShunfengPlugin(LogisticsSetting logisticsSetting) { + this.logisticsSetting = logisticsSetting; + } + + @Override + public LogisticsEnum pluginName() { + return LogisticsEnum.SHUNFENG; + } + + /** + * 文档地址:https://open.sf-express.com/Api/ApiDetails?level3=393&interName=%E4%B8%8B%E8%AE%A2%E5%8D%95%E6%8E%A5%E5%8F%A3-EXP_RECE_CREATE_ORDER + * + * @param orderDetailVO + */ + public String createOrder(OrderDetailVO orderDetailVO) { + StoreDetailService storeService = SpringContextUtil.getBean(StoreDetailService.class); + StoreDeliverGoodsAddressDTO storeDeliverGoodsAddressDTO = storeService.getStoreDeliverGoodsAddressDto(orderDetailVO.getOrder().getStoreId()); + if(storeDeliverGoodsAddressDTO == null){ + throw new ServiceException(ResultCode.STORE_DELIVER_ADDRESS_EXIST); + } + try { + Order order = orderDetailVO.getOrder(); + Map msgDataMap = new HashMap(); + msgDataMap.put("language", "zh-CN"); + msgDataMap.put("orderId", order.getSn()); + //托寄物信息 + List> cargoDetails = new ArrayList<>(); + for (OrderItem orderItem : orderDetailVO.getOrderItems()) { + Map map = new HashMap<>(); + map.put("name", orderItem.getGoodsName()); + cargoDetails.add(map); + } + msgDataMap.put("cargoDetails", cargoDetails); + + //收寄双方信息 + List> contactInfoList = new ArrayList<>(); + Map storeContactInfoMap = new HashMap<>(); + storeContactInfoMap.put("contactType", 1); + storeContactInfoMap.put("contact", storeDeliverGoodsAddressDTO.getSalesConsignorName()); + storeContactInfoMap.put("mobile", storeDeliverGoodsAddressDTO.getSalesConsignorMobile()); + //国家或地区2位代码 参照附录 + storeContactInfoMap.put("country", "CN"); + //详细地址,若有四级行政区划,如镇/街道等信息可拼接至此字段,格式样例:镇/街道+详细地址。若province/city 字段的值不传,此字段必须包含省市信息,避免影响原寄地代码识别,如:广东省深圳市福田区新洲十一街万基商务大厦10楼;此字段地址必须详细,否则会影响目的地中转识别; + storeContactInfoMap.put("address", storeDeliverGoodsAddressDTO.getSalesConsignorAddressPath() + storeDeliverGoodsAddressDTO.getSalesConsignorDetail()); + contactInfoList.add(storeContactInfoMap); + + Map memberContactInfoMap = new HashMap<>(); + memberContactInfoMap.put("contactType", 2); + memberContactInfoMap.put("contact", order.getConsigneeName()); + memberContactInfoMap.put("mobile", order.getConsigneeMobile()); + //国家或地区2位代码 参照附录 + memberContactInfoMap.put("country", "CN"); + //详细地址,若有四级行政区划,如镇/街道等信息可拼接至此字段,格式样例:镇/街道+详细地址。若province/city 字段的值不传,此字段必须包含省市信息,避免影响原寄地代码识别,如:广东省深圳市福田区新洲十一街万基商务大厦10楼;此字段地址必须详细,否则会影响目的地中转识别; + memberContactInfoMap.put("address", order.getConsigneeAddressPath() + order.getConsigneeDetail()); + contactInfoList.add(memberContactInfoMap); + msgDataMap.put("contactInfoList", contactInfoList); + + msgDataMap.put("expressTypeId", 1); + msgDataMap.put("isReturnRoutelabel", 1); + + + String result = sendPost(ExpressServiceCodeEnum.EXP_RECE_CREATE_ORDER, msgDataMap); + JSONObject resultData = JSONUtil.parseObj(result).getJSONObject("apiResultData"); + if(Boolean.TRUE.toString().equals(resultData.get("success").toString())){ + return resultData.getJSONObject("msgData").getJSONArray("waybillNoInfoList").getJSONObject(0).get("waybillNo").toString(); + } + throw new ServiceException(resultData.get("errorMsg").toString()); + } catch (UnsupportedEncodingException e) { + throw new RuntimeException(e); + } + } + + /** + * 文档地址:https://open.sf-express.com/Api/ApiDetails?apiServiceCode=EXP_RECE_SEARCH_ROUTES&category=1&apiClassify=1&interName=%E8%B7%AF%E7%94%B1%E6%9F%A5%E8%AF%A2%E6%8E%A5%E5%8F%A3-EXP_RECE_SEARCH_ROUTES + * + * @param logistics 物流公司 + * @param expNo + * @param phone + * @return + */ + @Override + public Traces pollQuery(Logistics logistics, String expNo, String phone) { + try { + Map msgDataMap = new HashMap(); + msgDataMap.put("language", "zh-CN"); + /** + * 查询号类别: + * 1:根据顺丰运单号查询,trackingNumber将被当作顺丰运单号处理 + * 2:根据客户订单号查询,trackingNumber将被当作客户订单号处理 + */ + msgDataMap.put("trackingType", 1); + List trackingNumber = new ArrayList<>(); + trackingNumber.add(expNo); + msgDataMap.put("trackingNumber", trackingNumber); + JSONObject result = JSONUtil.parseObj(sendPost(ExpressServiceCodeEnum.EXP_RECE_SEARCH_ROUTES, msgDataMap)); + JSONObject resultData = result.getJSONObject("apiResultData"); + if(Boolean.TRUE.toString().equals(resultData.get("success").toString())){ + JSONArray routesJson = resultData.getJSONObject("msgData").getJSONArray("routeResps").getJSONObject(0).getJSONArray("routes"); + List routes = routesJson.toList(Map.class); + return new Traces(logistics.getName(),expNo,routes); + } + } catch (UnsupportedEncodingException e) { + throw new RuntimeException(e); + } + return null; + } + + @Override + public Traces pollMapTrack(Logistics logistics, String expNo, String phone, String from, String to) { + return null; + } + + /** + * 文档地址:http://open.sf-express.com/Api/ApiDetails?level3=317&interName=%E4%BA%91%E6%89%93%E5%8D%B0%E9%9D%A2%E5%8D%952.0%E6%8E%A5%E5%8F%A3-COM_RECE_CLOUD_PRINT_WAYBILLS + * + * @param labelOrderDTO 电子面单DTO + * @return + */ + @Override + public Map labelOrder(LabelOrderDTO labelOrderDTO) { + try { + Map msgDataMap = new HashMap<>(); + //模板编码 + //关联云打印接口后,点击查看,可在接口详情页获取模板编码,类似:fm_76130_standard_{partnerId} + msgDataMap.put("templateCode", logisticsSetting.getTemplateCode()); + //业务数据 + Map documents = new HashMap<>(); + documents.put("masterWaybillNo", labelOrderDTO.getOrder().getLogisticsNo()); + msgDataMap.put("documents",documents); + msgDataMap.put("sync",true); + /** + * 版本号,传固定值:2.0 + */ + msgDataMap.put("version", "2.0"); + JSONObject result = JSONUtil.parseObj(sendPost(ExpressServiceCodeEnum.COM_RECE_CLOUD_PRINT_WAYBILLS, msgDataMap)); + JSONObject resultData = result.getJSONObject("apiResultData"); + if(Boolean.TRUE.toString().equals(resultData.get("success").toString())){ + return resultData.getJSONObject("obj").getJSONArray("files").toList(Map.class).get(0); + } + throw new ServiceException(resultData.getJSONArray("errorMessage").get(0).toString()); + } catch (UnsupportedEncodingException e) { + throw new RuntimeException(e); + } + } + + /** + * 文档地址:https://open.sf-express.com/Api/ApiDetails?level3=409&interName=%E9%A2%84%E8%AE%A1%E6%B4%BE%E9%80%81%E6%97%B6%E9%97%B4%E6%8E%A5%E5%8F%A3-EXP_RECE_SEARCH_PROMITM + * + * @param searchNo + * @param checkNos + */ + public String searchPromitm(String searchNo, String checkNos) { + try { + Map msgDataMap = new HashMap(); + //顺丰运单号 + msgDataMap.put("searchNo", searchNo); + //校验类型 1,电话号码校验 2,月结卡号校验 + msgDataMap.put("checkType", 1); + //校验值 当校验类型为1时传电话号码 当校验类型为2时传月结卡号 + List mobileList= new ArrayList<>(); + mobileList.add(checkNos); + msgDataMap.put("checkNos", mobileList); + JSONObject result = JSONUtil.parseObj(sendPost(ExpressServiceCodeEnum.EXP_RECE_SEARCH_PROMITM, msgDataMap)); + JSONObject resultData = result.getJSONObject("apiResultData"); + if(Boolean.TRUE.toString().equals(resultData.get("success").toString())){ + return resultData.getJSONObject("msgData").get("promiseTm").toString(); + } + throw new ServiceException(resultData.get("errorMsg").toString()); + } catch (UnsupportedEncodingException e) { + throw new RuntimeException(e); + } + } + + private String sendPost(IServiceCodeStandard standardService, Map msgDataMap) throws UnsupportedEncodingException { + CallExpressServiceTools tools = CallExpressServiceTools.getInstance(); + Map params = new HashMap(); + String timeStamp = String.valueOf(System.currentTimeMillis()); + // 顾客编码 + params.put("partnerID", logisticsSetting.getClientCode()); + params.put("requestID", UUID.randomUUID().toString().replace("-", "")); + // 接口服务码 + params.put("serviceCode", standardService.getCode()); + params.put("timestamp", timeStamp); + params.put("msgData", JSONUtil.toJsonStr(msgDataMap)); + + params.put("msgDigest", tools.getMsgDigest(params.get("msgData"), timeStamp, logisticsSetting.getCheckWord())); + String result = HttpClientUtil.post(logisticsSetting.getCallUrl(), params); + + log.info("===调用地址 ===" + logisticsSetting.getCallUrl()); + log.info("===顾客编码 ===" + logisticsSetting.getClientCode()); + log.info("===返回结果:" + result); + + return result; + } +} diff --git a/framework/src/main/java/cn/lili/modules/member/service/MemberService.java b/framework/src/main/java/cn/lili/modules/member/service/MemberService.java index 1b1d8be8..27aab633 100644 --- a/framework/src/main/java/cn/lili/modules/member/service/MemberService.java +++ b/framework/src/main/java/cn/lili/modules/member/service/MemberService.java @@ -74,6 +74,14 @@ public interface MemberService extends IService { */ Token usernameStoreLogin(String username, String password); + /** + * 商家登录:用户名、密码登录 + * + * @param mobilePhone 用户名 + * @return token + */ + Token mobilePhoneStoreLogin(String mobilePhone); + /** * 注册:手机号、验证码登录 * diff --git a/framework/src/main/java/cn/lili/modules/member/serviceimpl/MemberServiceImpl.java b/framework/src/main/java/cn/lili/modules/member/serviceimpl/MemberServiceImpl.java index 4060e39c..db29096d 100644 --- a/framework/src/main/java/cn/lili/modules/member/serviceimpl/MemberServiceImpl.java +++ b/framework/src/main/java/cn/lili/modules/member/serviceimpl/MemberServiceImpl.java @@ -198,6 +198,22 @@ public class MemberServiceImpl extends ServiceImpl impleme throw new ServiceException(ResultCode.USER_PASSWORD_ERROR); } //对店铺状态的判定处理 + return checkMemberStore(member); + } + + @Override + public Token mobilePhoneStoreLogin(String mobilePhone) { + Member member = this.findMember(mobilePhone); + //如果手机号不存在则自动注册用户 + if (member == null) { + throw new ServiceException(ResultCode.USER_NOT_EXIST); + } + loginBindUser(member); + //对店铺状态的判定处理 + return checkMemberStore(member); + } + + private Token checkMemberStore(Member member) { if (Boolean.TRUE.equals(member.getHaveStore())) { Store store = storeService.getById(member.getStoreId()); if (!store.getStoreDisable().equals(StoreStatusEnum.OPEN.name())) { @@ -206,7 +222,6 @@ public class MemberServiceImpl extends ServiceImpl impleme } else { throw new ServiceException(ResultCode.USER_NOT_EXIST); } - return storeTokenGenerate.createToken(member, false); } diff --git a/framework/src/main/java/cn/lili/modules/member/token/MemberTokenGenerate.java b/framework/src/main/java/cn/lili/modules/member/token/MemberTokenGenerate.java index 7b70b5fb..ad5e0089 100644 --- a/framework/src/main/java/cn/lili/modules/member/token/MemberTokenGenerate.java +++ b/framework/src/main/java/cn/lili/modules/member/token/MemberTokenGenerate.java @@ -57,14 +57,21 @@ public class MemberTokenGenerate extends AbstractTokenGenerate { String destination = rocketmqCustomProperties.getMemberTopic() + ":" + MemberTagsEnum.MEMBER_LOGIN.name(); rocketMQTemplate.asyncSend(destination, member, RocketmqSendCallbackBuilder.commonCallback()); - AuthUser authUser = new AuthUser(member.getUsername(), member.getId(), member.getNickName(), member.getFace(), UserEnums.MEMBER); + AuthUser authUser = AuthUser.builder() + .username(member.getUsername()) + .face(member.getFace()) + .id(member.getId()) + .role(UserEnums.MEMBER) + .nickName(member.getNickName()) + .longTerm(longTerm) + .build(); //登陆成功生成token - return tokenUtil.createToken(member.getUsername(), authUser, longTerm, UserEnums.MEMBER); + return tokenUtil.createToken(authUser); } @Override public Token refreshToken(String refreshToken) { - return tokenUtil.refreshToken(refreshToken, UserEnums.MEMBER); + return tokenUtil.refreshToken(refreshToken); } } diff --git a/framework/src/main/java/cn/lili/modules/member/token/StoreTokenGenerate.java b/framework/src/main/java/cn/lili/modules/member/token/StoreTokenGenerate.java index 091537e2..9d4b08fc 100644 --- a/framework/src/main/java/cn/lili/modules/member/token/StoreTokenGenerate.java +++ b/framework/src/main/java/cn/lili/modules/member/token/StoreTokenGenerate.java @@ -11,16 +11,16 @@ import cn.lili.common.security.enums.UserEnums; import cn.lili.common.security.token.Token; import cn.lili.common.security.token.TokenUtil; import cn.lili.common.security.token.base.AbstractTokenGenerate; +import cn.lili.modules.member.entity.dos.Clerk; import cn.lili.modules.member.entity.dos.Member; +import cn.lili.modules.member.entity.vo.StoreUserMenuVO; +import cn.lili.modules.member.service.ClerkService; +import cn.lili.modules.member.service.StoreMenuRoleService; import cn.lili.modules.store.entity.dos.Store; import cn.lili.modules.store.service.StoreService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; -import cn.lili.modules.member.entity.dos.Clerk; -import cn.lili.modules.member.entity.vo.StoreUserMenuVO; -import cn.lili.modules.member.service.ClerkService; -import cn.lili.modules.member.service.StoreMenuRoleService; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -61,7 +61,7 @@ public class StoreTokenGenerate extends AbstractTokenGenerate { throw new ServiceException(ResultCode.CLERK_DISABLED_ERROR); } //获取当前用户权限 - List storeUserMenuVOS = storeMenuRoleService.findAllMenu(clerk.getId(),member.getId()); + List storeUserMenuVOS = storeMenuRoleService.findAllMenu(clerk.getId(), member.getId()); //缓存权限列表 cache.put(CachePrefix.PERMISSION_LIST.getPrefix(UserEnums.STORE) + member.getId(), this.permissionList(storeUserMenuVOS)); //查询店铺信息 @@ -69,16 +69,25 @@ public class StoreTokenGenerate extends AbstractTokenGenerate { if (store == null) { throw new ServiceException(ResultCode.STORE_NOT_OPEN); } - AuthUser authUser = new AuthUser(member.getUsername(), member.getId(), UserEnums.STORE, member.getNickName(), clerk.getIsSuper(), clerk.getId(),store.getStoreLogo()); - - authUser.setStoreId(store.getId()); - authUser.setStoreName(store.getStoreName()); - return tokenUtil.createToken(member.getUsername(), authUser, longTerm, UserEnums.STORE); + //构建对象 + AuthUser authUser = AuthUser.builder() + .username(member.getUsername()) + .id(member.getId()) + .role(UserEnums.STORE) + .nickName(member.getNickName()) + .isSuper(clerk.getIsSuper()) + .clerkId(clerk.getId()) + .face(store.getStoreLogo()) + .storeId(store.getId()) + .storeName(store.getStoreName()) + .longTerm(longTerm) + .build(); + return tokenUtil.createToken(authUser); } @Override public Token refreshToken(String refreshToken) { - return tokenUtil.refreshToken(refreshToken, UserEnums.STORE); + return tokenUtil.refreshToken(refreshToken); } /** @@ -145,7 +154,6 @@ public class StoreTokenGenerate extends AbstractTokenGenerate { superPermissions.add("/store/passport/login*"); - //店铺设置 queryPermissions.add("/store/settings/storeSettings*"); //文章接口 @@ -154,6 +162,5 @@ public class StoreTokenGenerate extends AbstractTokenGenerate { queryPermissions.add("/store/statistics/index*"); - } } diff --git a/framework/src/main/java/cn/lili/modules/order/aftersale/serviceimpl/AfterSaleServiceImpl.java b/framework/src/main/java/cn/lili/modules/order/aftersale/serviceimpl/AfterSaleServiceImpl.java index c6724bc4..c6f9507c 100644 --- a/framework/src/main/java/cn/lili/modules/order/aftersale/serviceimpl/AfterSaleServiceImpl.java +++ b/framework/src/main/java/cn/lili/modules/order/aftersale/serviceimpl/AfterSaleServiceImpl.java @@ -269,8 +269,7 @@ public class AfterSaleServiceImpl extends ServiceImpl { * 根据促销查询订单 * * @param orderPromotionType 订单类型 - * @param payStatus 支付状态 - * @param parentOrderSn 依赖订单编号 - * @param orderSn 订单编号 + * @param payStatus 支付状态 + * @param parentOrderSn 依赖订单编号 + * @param orderSn 订单编号 * @return 订单信息 */ List queryListByPromotion(String orderPromotionType, String payStatus, String parentOrderSn, String orderSn); @@ -75,9 +75,9 @@ public interface OrderService extends IService { * 根据促销查询订单 * * @param orderPromotionType 订单类型 - * @param payStatus 支付状态 - * @param parentOrderSn 依赖订单编号 - * @param orderSn 订单编号 + * @param payStatus 支付状态 + * @param parentOrderSn 依赖订单编号 + * @param orderSn 订单编号 * @return 订单信息 */ long queryCountByPromotion(String orderPromotionType, String payStatus, String parentOrderSn, String orderSn); @@ -91,7 +91,6 @@ public interface OrderService extends IService { List queryListByPromotion(String pintuanId); - /** * 查询导出订单列表 * @@ -166,6 +165,14 @@ public interface OrderService extends IService { */ Order delivery(String orderSn, String invoiceNumber, String logisticsId); + /** + * 订单发货 + * + * @param orderSn 订单编号 + * @return 订单 + */ + Order shunFengDelivery(String orderSn); + /** * 获取物流踪迹 * @@ -174,6 +181,14 @@ public interface OrderService extends IService { */ Traces getTraces(String orderSn); + /** + * 获取地图版 物流踪迹 + * + * @param orderSn 订单编号 + * @return 物流踪迹 + */ + Traces getMapTraces(String orderSn); + /** * 订单核验 * @@ -288,9 +303,9 @@ public interface OrderService extends IService { /** * 检查是否开始虚拟成团 * - * @param pintuanId 拼团活动id + * @param pintuanId 拼团活动id * @param requiredNum 成团人数 - * @param fictitious 是否开启成团 + * @param fictitious 是否开启成团 * @return 是否成功 */ boolean checkFictitiousOrder(String pintuanId, Integer requiredNum, Boolean fictitious); diff --git a/framework/src/main/java/cn/lili/modules/order/order/serviceimpl/OrderServiceImpl.java b/framework/src/main/java/cn/lili/modules/order/order/serviceimpl/OrderServiceImpl.java index 260cf4ff..9af389f3 100644 --- a/framework/src/main/java/cn/lili/modules/order/order/serviceimpl/OrderServiceImpl.java +++ b/framework/src/main/java/cn/lili/modules/order/order/serviceimpl/OrderServiceImpl.java @@ -16,10 +16,11 @@ import cn.lili.common.properties.RocketmqCustomProperties; import cn.lili.common.security.OperationalJudgment; import cn.lili.common.security.context.UserContext; import cn.lili.common.security.enums.UserEnums; -import cn.lili.common.utils.ObjectUtil; import cn.lili.common.utils.SnowFlake; import cn.lili.modules.goods.entity.dto.GoodsCompleteMessage; +import cn.lili.modules.logistics.entity.enums.LogisticsEnum; import cn.lili.modules.member.entity.dto.MemberAddressDTO; +import cn.lili.modules.member.service.StoreLogisticsService; import cn.lili.modules.order.cart.entity.dto.TradeDTO; import cn.lili.modules.order.cart.entity.enums.DeliveryMethodEnum; import cn.lili.modules.order.order.aop.OrderLogPoint; @@ -41,8 +42,11 @@ import cn.lili.modules.order.trade.service.OrderLogService; import cn.lili.modules.payment.entity.enums.PaymentMethodEnum; import cn.lili.modules.promotion.entity.dos.Pintuan; import cn.lili.modules.promotion.service.PintuanService; +import cn.lili.modules.store.entity.dto.StoreDeliverGoodsAddressDTO; +import cn.lili.modules.store.service.StoreDetailService; import cn.lili.modules.system.aspect.annotation.SystemLogPoint; import cn.lili.modules.system.entity.dos.Logistics; +import cn.lili.modules.system.entity.dto.LogisticsSetting; import cn.lili.modules.system.entity.vo.Traces; import cn.lili.modules.system.service.LogisticsService; import cn.lili.mybatis.util.PageUtil; @@ -149,6 +153,9 @@ public class OrderServiceImpl extends ServiceImpl implements @Autowired private ApplicationEventPublisher applicationEventPublisher; + @Autowired + private StoreDetailService storeDetailService; + @Override @Transactional(rollbackFor = Exception.class) public void intoDB(TradeDTO tradeDTO) { @@ -164,6 +171,8 @@ public class OrderServiceImpl extends ServiceImpl implements List orderVOS = new ArrayList<>(); //循环购物车 tradeDTO.getCartList().forEach(item -> { + //当前购物车订单子项 + List currentOrderItems = new ArrayList<>(); Order order = new Order(item, tradeDTO); //构建orderVO对象 OrderVO orderVO = new OrderVO(); @@ -174,10 +183,13 @@ public class OrderServiceImpl extends ServiceImpl implements //记录日志 orderLogs.add(new OrderLog(item.getSn(), UserContext.getCurrentUser().getId(), UserContext.getCurrentUser().getRole().getRole(), UserContext.getCurrentUser().getUsername(), message)); item.getCheckedSkuList().forEach( - sku -> orderItems.add(new OrderItem(sku, item, tradeDTO)) + sku -> { + orderItems.add(new OrderItem(sku, item, tradeDTO)); + currentOrderItems.add(new OrderItem(sku, item, tradeDTO)); + } ); //写入子订单信息 - orderVO.setOrderItems(orderItems); + orderVO.setOrderItems(currentOrderItems); //orderVO 记录 orderVOS.add(orderVO); }); @@ -292,7 +304,7 @@ public class OrderServiceImpl extends ServiceImpl implements Order order = OperationalJudgment.judgment(this.getBySn(orderSn)); //如果订单促销类型不为空&&订单是拼团订单,并且订单未成团,则抛出异常 if (OrderPromotionTypeEnum.PINTUAN.name().equals(order.getOrderPromotionType()) - && !CharSequenceUtil.equalsAny(order.getOrderStatus(),OrderStatusEnum.UNDELIVERED.name(),OrderStatusEnum.STAY_PICKED_UP.name())) { + && !CharSequenceUtil.equalsAny(order.getOrderStatus(), OrderStatusEnum.UNDELIVERED.name(), OrderStatusEnum.STAY_PICKED_UP.name())) { throw new ServiceException(ResultCode.ORDER_CAN_NOT_CANCEL); } if (CharSequenceUtil.equalsAny(order.getOrderStatus(), @@ -451,13 +463,33 @@ public class OrderServiceImpl extends ServiceImpl implements return order; } + @Override + @Transactional(rollbackFor = Exception.class) + public Order shunFengDelivery(String orderSn) { + OrderDetailVO orderDetailVO = this.queryDetail(orderSn); + String logisticsNo = logisticsService.sfCreateOrder(orderDetailVO); + Logistics logistics = logisticsService.getOne(new LambdaQueryWrapper().eq(Logistics::getCode,"SF")); + return delivery(orderSn,logisticsNo,logistics.getId()); + } + @Override public Traces getTraces(String orderSn) { //获取订单信息 Order order = this.getBySn(orderSn); //获取踪迹信息 - String str = order.getConsigneeMobile(); - return logisticsService.getLogistic(order.getLogisticsCode(), order.getLogisticsNo(), str.substring(str.length() - 4)); + return logisticsService.getLogisticTrack(order.getLogisticsCode(), order.getLogisticsNo(), order.getConsigneeMobile()); + } + + @Override + public Traces getMapTraces(String orderSn) { + //获取订单信息 + Order order = this.getBySn(orderSn); + //获取店家信息 + StoreDeliverGoodsAddressDTO storeDeliverGoodsAddressDTO = storeDetailService.getStoreDeliverGoodsAddressDto(order.getStoreId()); + String from = storeDeliverGoodsAddressDTO.getSalesConsignorAddressPath().substring(0, storeDeliverGoodsAddressDTO.getSalesConsignorAddressPath().indexOf(",") - 1); + String to = order.getConsigneeAddressPath().substring(0, order.getConsigneeAddressPath().indexOf(",") - 1); + //获取踪迹信息 + return logisticsService.getLogisticMapTrack(order.getLogisticsCode(), order.getLogisticsNo(), order.getConsigneeMobile(), from, to); } @Override @@ -479,7 +511,7 @@ public class OrderServiceImpl extends ServiceImpl implements public Order take(String verificationCode) { String storeId = OperationalJudgment.judgment(UserContext.getCurrentUser()).getStoreId(); Order order = this.getOne(new LambdaQueryWrapper().eq(Order::getVerificationCode, verificationCode).eq(Order::getStoreId, storeId)); - if(order == null){ + if (order == null) { throw new ServiceException(ResultCode.ORDER_NOT_EXIST); } order.setOrderStatus(OrderStatusEnum.COMPLETED.name()); @@ -492,7 +524,7 @@ public class OrderServiceImpl extends ServiceImpl implements public Order getOrderByVerificationCode(String verificationCode) { String storeId = Objects.requireNonNull(UserContext.getCurrentUser()).getStoreId(); return this.getOne(new LambdaQueryWrapper() - .in(Order::getOrderStatus, OrderStatusEnum.TAKE.name(),OrderStatusEnum.STAY_PICKED_UP.name()) + .in(Order::getOrderStatus, OrderStatusEnum.TAKE.name(), OrderStatusEnum.STAY_PICKED_UP.name()) .eq(Order::getStoreId, storeId) .eq(Order::getVerificationCode, verificationCode)); } @@ -956,9 +988,9 @@ public class OrderServiceImpl extends ServiceImpl implements public void normalOrderConfirm(String orderSn) { OrderStatusEnum orderStatusEnum = null; Order order = this.getBySn(orderSn); - if(DeliveryMethodEnum.SELF_PICK_UP.name().equals(order.getDeliveryMethod())){ + if (DeliveryMethodEnum.SELF_PICK_UP.name().equals(order.getDeliveryMethod())) { orderStatusEnum = OrderStatusEnum.STAY_PICKED_UP; - }else if (DeliveryMethodEnum.LOGISTICS.name().equals(order.getDeliveryMethod())){ + } else if (DeliveryMethodEnum.LOGISTICS.name().equals(order.getDeliveryMethod())) { orderStatusEnum = OrderStatusEnum.UNDELIVERED; } //修改订单 diff --git a/framework/src/main/java/cn/lili/modules/payment/entity/enums/PaymentMethodEnum.java b/framework/src/main/java/cn/lili/modules/payment/entity/enums/PaymentMethodEnum.java index d06b6121..fe8b6973 100644 --- a/framework/src/main/java/cn/lili/modules/payment/entity/enums/PaymentMethodEnum.java +++ b/framework/src/main/java/cn/lili/modules/payment/entity/enums/PaymentMethodEnum.java @@ -16,6 +16,11 @@ public enum PaymentMethodEnum { * 支付宝 */ ALIPAY("aliPayPlugin", "支付宝"), + + /** + * 银联-云闪付 + */ + UNIONPAY("unionPayPlugin", "银联-云闪付"), /** * 余额支付 */ diff --git a/framework/src/main/java/cn/lili/modules/payment/kit/CashierSupport.java b/framework/src/main/java/cn/lili/modules/payment/kit/CashierSupport.java index cec468fb..fb8874cc 100644 --- a/framework/src/main/java/cn/lili/modules/payment/kit/CashierSupport.java +++ b/framework/src/main/java/cn/lili/modules/payment/kit/CashierSupport.java @@ -18,6 +18,7 @@ import cn.lili.modules.system.entity.dto.payment.PaymentSupportSetting; import cn.lili.modules.system.entity.dto.payment.dto.PaymentSupportItem; import cn.lili.modules.system.entity.enums.SettingEnum; import cn.lili.modules.system.service.SettingService; +import cn.lili.modules.wallet.entity.dos.MemberWithdrawApply; import cn.lili.modules.wallet.service.MemberWalletService; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; @@ -142,6 +143,15 @@ public class CashierSupport { payment.notify(request); } + /** + * 用户提现 + * @param paymentMethodEnum 支付渠道 + * @param memberWithdrawApply 用户提现申请 + */ + public void transfer(PaymentMethodEnum paymentMethodEnum, MemberWithdrawApply memberWithdrawApply){ + Payment payment = (Payment) SpringContextUtil.getBean(paymentMethodEnum.getPlugin()); + payment.transfer(memberWithdrawApply); + } /** * 获取收银台参数 * diff --git a/framework/src/main/java/cn/lili/modules/payment/kit/Payment.java b/framework/src/main/java/cn/lili/modules/payment/kit/Payment.java index f67c2619..fba4010e 100644 --- a/framework/src/main/java/cn/lili/modules/payment/kit/Payment.java +++ b/framework/src/main/java/cn/lili/modules/payment/kit/Payment.java @@ -6,6 +6,7 @@ import cn.lili.common.vo.ResultMessage; import cn.lili.modules.payment.entity.RefundLog; import cn.lili.modules.payment.entity.enums.PaymentMethodEnum; import cn.lili.modules.payment.kit.dto.PayParam; +import cn.lili.modules.wallet.entity.dos.MemberWithdrawApply; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -110,6 +111,13 @@ public interface Payment { throw new ServiceException(ResultCode.PAY_ERROR); } + /** + * 提现 + */ + default void transfer(MemberWithdrawApply memberWithdrawApply) { + throw new ServiceException(ResultCode.PAY_ERROR); + } + /** * 支付回调地址 * diff --git a/framework/src/main/java/cn/lili/modules/payment/kit/plugin/alipay/AliPayPlugin.java b/framework/src/main/java/cn/lili/modules/payment/kit/plugin/alipay/AliPayPlugin.java index 9641d751..2d72e845 100644 --- a/framework/src/main/java/cn/lili/modules/payment/kit/plugin/alipay/AliPayPlugin.java +++ b/framework/src/main/java/cn/lili/modules/payment/kit/plugin/alipay/AliPayPlugin.java @@ -26,11 +26,13 @@ import cn.lili.modules.system.entity.dos.Setting; import cn.lili.modules.system.entity.dto.payment.AlipayPaymentSetting; import cn.lili.modules.system.entity.enums.SettingEnum; import cn.lili.modules.system.service.SettingService; +import cn.lili.modules.wallet.entity.dos.MemberWithdrawApply; import com.alibaba.fastjson.JSONObject; import com.alipay.api.AlipayApiException; import com.alipay.api.domain.*; import com.alipay.api.internal.util.AlipaySignature; -import com.alipay.api.response.AlipayTradeCancelResponse; +import com.alipay.api.request.AlipayFundTransUniTransferRequest; +import com.alipay.api.response.AlipayFundTransUniTransferResponse; import com.alipay.api.response.AlipayTradeRefundResponse; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; @@ -229,6 +231,43 @@ public class AliPayPlugin implements Payment { log.info("支付异步通知:"); } + /** + * 支付宝提现 + * 文档地址:https://opendocs.alipay.com/open/02byuo?scene=ca56bca529e64125a2786703c6192d41&ref=api + * + * @param memberWithdrawApply 会员提现申请 + */ + @Override + public void transfer(MemberWithdrawApply memberWithdrawApply) { + AlipayFundTransUniTransferModel model = new AlipayFundTransUniTransferModel(); + model.setOutBizNo(SnowFlake.createStr("T")); + model.setRemark("用户提现"); + model.setBusinessParams("{\"payer_show_name_use_alias\":\"true\"}"); + model.setBizScene("DIRECT_TRANSFER"); + Participant payeeInfo = new Participant(); + payeeInfo.setIdentity(memberWithdrawApply.getConnectNumber()); + payeeInfo.setIdentityType("ALIPAY_LOGON_ID"); + payeeInfo.setName(memberWithdrawApply.getRealName()); + model.setPayeeInfo(payeeInfo); + + model.setTransAmount(memberWithdrawApply.getApplyMoney().toString()); + model.setProductCode("TRANS_ACCOUNT_NO_PWD"); + model.setOrderTitle("用户提现"); + //交互退款 + try { + AlipayFundTransUniTransferResponse alipayFundTransUniTransferResponse = AliPayApi.uniTransferToResponse(model,null); + log.error("支付宝退款,参数:{},支付宝响应:{}", JSONUtil.toJsonStr(model), JSONUtil.toJsonStr(alipayFundTransUniTransferResponse)); + if (alipayFundTransUniTransferResponse.isSuccess()) { + + } else { + log.error(alipayFundTransUniTransferResponse.getSubMsg()); + } + } catch (Exception e) { + log.error("用户提现异常:", e); + throw new ServiceException(ResultCode.PAY_ERROR); + } + } + /** * 验证支付结果 * diff --git a/framework/src/main/java/cn/lili/modules/payment/kit/plugin/unionpay/UnionPayApi.java b/framework/src/main/java/cn/lili/modules/payment/kit/plugin/unionpay/UnionPayApi.java new file mode 100644 index 00000000..7f10c265 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/payment/kit/plugin/unionpay/UnionPayApi.java @@ -0,0 +1,38 @@ +/** + *

IJPay 让支付触手可及,封装了微信支付、支付宝支付、银联支付常用的支付方式以及各种常用的接口。

+ * + *

不依赖任何第三方 mvc 框架,仅仅作为工具使用简单快速完成支付模块的开发,可轻松嵌入到任何系统里。

+ * + *

IJPay 交流群: 723992875

+ * + *

Node.js 版: https://gitee.com/javen205/TNWX

+ * + *

云闪付接口

+ * + * @author Javen + */ +package cn.lili.modules.payment.kit.plugin.unionpay; + +import cn.lili.modules.payment.kit.core.kit.HttpKit; +import cn.lili.modules.payment.kit.core.kit.WxPayKit; + +import java.util.Map; + +public class UnionPayApi { + public static String authUrl = "https://qr.95516.com/qrcGtwWeb-web/api/userAuth?version=1.0.0&redirectUrl=%s"; + + public static String execution(String url, Map params) { + return HttpKit.getDelegate().post(url, WxPayKit.toXml(params)); + } + + /** + * 获取用户授权 API + * + * @param url 回调地址,可以自定义参数 https://pay.javen.com/callback?sdk=ijpay + * @return 银联重定向 Url + */ + public static String buildAuthUrl(String url) { + return String.format(authUrl, url); + } + +} diff --git a/framework/src/main/java/cn/lili/modules/payment/kit/plugin/unionpay/UnionPayApiConfig.java b/framework/src/main/java/cn/lili/modules/payment/kit/plugin/unionpay/UnionPayApiConfig.java new file mode 100644 index 00000000..66ea9f1c --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/payment/kit/plugin/unionpay/UnionPayApiConfig.java @@ -0,0 +1,57 @@ + +package cn.lili.modules.payment.kit.plugin.unionpay; + +import lombok.*; + +import java.io.Serializable; + +/** + *

IJPay 让支付触手可及,封装了微信支付、支付宝支付、银联支付等常用的支付方式以及各种常用的接口。

+ * + *

不依赖任何第三方 mvc 框架,仅仅作为工具使用简单快速完成支付模块的开发,可轻松嵌入到任何系统里。

+ * + *

IJPay 交流群: 723992875

+ * + *

Node.js 版: https://gitee.com/javen205/TNWX

+ * + *

云闪付常用配置

+ * + * @author Javen + */ +@Getter +@Setter +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class UnionPayApiConfig implements Serializable { + private static final long serialVersionUID = -9025648880068727445L; + + /** + * 商户平台分配的账号 + */ + private String mchId; + /** + * 连锁商户号 + */ + private String groupMchId; + /** + * 授权交易机构代码 + */ + private String agentMchId; + /** + * 商户平台分配的密钥 + */ + private String apiKey; + /** + * 商户平台网关 + */ + private String serverUrl; + /** + * 应用域名,回调中会使用此参数 + */ + private String domain; + /** + * 其他附加参数 + */ + private Object exParams; +} diff --git a/framework/src/main/java/cn/lili/modules/payment/kit/plugin/unionpay/UnionPayApiConfigKit.java b/framework/src/main/java/cn/lili/modules/payment/kit/plugin/unionpay/UnionPayApiConfigKit.java new file mode 100644 index 00000000..9f5dc3f0 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/payment/kit/plugin/unionpay/UnionPayApiConfigKit.java @@ -0,0 +1,87 @@ + +package cn.lili.modules.payment.kit.plugin.unionpay; + +import cn.hutool.core.util.StrUtil; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + *

IJPay 让支付触手可及,封装了微信支付、支付宝支付、银联支付等常用的支付方式以及各种常用的接口。

+ * + *

不依赖任何第三方 mvc 框架,仅仅作为工具使用简单快速完成支付模块的开发,可轻松嵌入到任何系统里。

+ * + *

IJPay 交流群: 723992875

+ * + *

Node.js 版: https://gitee.com/javen205/TNWX

+ * + *

云闪付常用配置 Kit

+ * + * @author Javen + */ +public class UnionPayApiConfigKit { + private static final ThreadLocal TL = new ThreadLocal(); + + private static final Map CFG_MAP = new ConcurrentHashMap(); + private static final String DEFAULT_CFG_KEY = "_default_key_"; + + /** + * 添加云闪付配置,每个 mchId 只需添加一次,相同 mchId 将被覆盖 + * + * @param UnionPayApiConfig 云闪付配置 + * @return {@link UnionPayApiConfig} 云闪付配置 + */ + public static UnionPayApiConfig putApiConfig(UnionPayApiConfig UnionPayApiConfig) { + if (CFG_MAP.size() == 0) { + CFG_MAP.put(DEFAULT_CFG_KEY, UnionPayApiConfig); + } + return CFG_MAP.put(UnionPayApiConfig.getMchId(), UnionPayApiConfig); + } + + public static UnionPayApiConfig setThreadLocalApiConfig(UnionPayApiConfig UnionPayApiConfig) { + if (StrUtil.isNotEmpty(UnionPayApiConfig.getMchId())) { + setThreadLocalMchId(UnionPayApiConfig.getMchId()); + } + return putApiConfig(UnionPayApiConfig); + } + + public static UnionPayApiConfig removeApiConfig(UnionPayApiConfig UnionPayApiConfig) { + return removeApiConfig(UnionPayApiConfig.getMchId()); + } + + public static UnionPayApiConfig removeApiConfig(String mchId) { + return CFG_MAP.remove(mchId); + } + + public static void setThreadLocalMchId(String mchId) { + if (StrUtil.isEmpty(mchId)) { + mchId = CFG_MAP.get(DEFAULT_CFG_KEY).getMchId(); + } + TL.set(mchId); + } + + public static void removeThreadLocalMchId() { + TL.remove(); + } + + public static String getMchId() { + String appId = TL.get(); + if (StrUtil.isEmpty(appId)) { + appId = CFG_MAP.get(DEFAULT_CFG_KEY).getMchId(); + } + return appId; + } + + public static UnionPayApiConfig getApiConfig() { + String appId = getMchId(); + return getApiConfig(appId); + } + + public static UnionPayApiConfig getApiConfig(String appId) { + UnionPayApiConfig cfg = CFG_MAP.get(appId); + if (cfg == null) { + throw new IllegalStateException("需事先调用 UnionPayApiConfigKit.putApiConfig(UnionPayApiConfig) 将 mchId 对应的 UnionPayApiConfig 对象存入,才可以使用 UnionPayApiConfigKit.getUnionPayApiConfig() 的系列方法"); + } + return cfg; + } +} diff --git a/framework/src/main/java/cn/lili/modules/payment/kit/plugin/unionpay/UnionPayPlugin.java b/framework/src/main/java/cn/lili/modules/payment/kit/plugin/unionpay/UnionPayPlugin.java new file mode 100644 index 00000000..ebcd9d59 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/payment/kit/plugin/unionpay/UnionPayPlugin.java @@ -0,0 +1,247 @@ +package cn.lili.modules.payment.kit.plugin.unionpay; + +import cn.hutool.core.net.URLEncoder; +import cn.hutool.core.util.StrUtil; +import cn.hutool.json.JSONUtil; +import cn.lili.common.enums.ResultCode; +import cn.lili.common.exception.ServiceException; +import cn.lili.common.utils.SnowFlake; +import cn.lili.common.vo.ResultMessage; +import cn.lili.modules.payment.kit.CashierSupport; +import cn.lili.modules.payment.kit.Payment; +import cn.lili.modules.payment.kit.core.enums.SignType; +import cn.lili.modules.payment.kit.core.kit.HttpKit; +import cn.lili.modules.payment.kit.core.kit.IpKit; +import cn.lili.modules.payment.kit.core.kit.WxPayKit; +import cn.lili.modules.payment.kit.dto.PayParam; +import cn.lili.modules.payment.kit.params.dto.CashierParam; +import cn.lili.modules.payment.kit.plugin.unionpay.enums.ServiceEnum; +import cn.lili.modules.payment.kit.plugin.unionpay.model.UnifiedOrderModel; +import cn.lili.modules.payment.service.PaymentService; +import cn.lili.modules.system.entity.dos.Setting; +import cn.lili.modules.system.entity.dto.payment.UnionPaymentSetting; +import cn.lili.modules.system.entity.enums.SettingEnum; +import cn.lili.modules.system.service.SettingService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import javax.servlet.http.HttpServletRequest; +import java.nio.charset.StandardCharsets; +import java.util.Map; + +/** + * 银联云闪付 支付 + * + * @author Bulbasaur + * @since 2023/02/14 17:44 + */ +@Slf4j +@Component +public class UnionPayPlugin implements Payment { + + + /** + * 收银台 + */ + @Autowired + private CashierSupport cashierSupport; + /** + * 支付日志 + */ + @Autowired + private PaymentService paymentService; + /** + * 配置 + */ + @Autowired + private SettingService settingService; + + @Override + public ResultMessage nativePay(HttpServletRequest request, PayParam payParam) { + try { + CashierParam cashierParam = cashierSupport.cashierParam(payParam); + UnionPaymentSetting unionPaymentSetting = this.unionPaymentSetting(); + String notifyUrl = unionPaymentSetting.getUnionPayDomain().concat("/unionPay/payNotify"); + //用户ip + String ip = IpKit.getRealIp(request); + //第三方付款订单 + String outOrderNo = SnowFlake.getIdStr(); + String attach = URLEncoder.createDefault().encode(JSONUtil.toJsonStr(payParam), StandardCharsets.UTF_8); + Map params = UnifiedOrderModel.builder() + .service(ServiceEnum.NATIVE.toString()) + .mch_id(unionPaymentSetting.getUnionPayMachId()) + .out_trade_no(outOrderNo) + .body(cashierParam.getDetail()) + .attach(attach) + .total_fee("0") + .mch_create_ip(ip) + .notify_url(notifyUrl) + .nonce_str(WxPayKit.generateStr()) + .build().createSign(unionPaymentSetting.getUnionPayKey(), SignType.MD5); + + String xmlResult = UnionPayApi.execution(unionPaymentSetting.getUnionPayServerUrl(), params); + log.info("xmlResult:" + xmlResult); + Map result = WxPayKit.xmlToMap(xmlResult); + log.info(result.toString()); + return null; + } catch (Exception e) { + e.printStackTrace(); + return null; + } + } + + @Override + public ResultMessage appPay(HttpServletRequest request, PayParam payParam) { + try { + + CashierParam cashierParam = cashierSupport.cashierParam(payParam); + UnionPaymentSetting unionPaymentSetting = this.unionPaymentSetting(); + String notifyUrl = unionPaymentSetting.getUnionPayDomain().concat("/unionPay/payNotify"); + String attach = URLEncoder.createDefault().encode(JSONUtil.toJsonStr(payParam), StandardCharsets.UTF_8); + + //用户ip + String ip = IpKit.getRealIp(request); + Map params = UnifiedOrderModel.builder() + .service(ServiceEnum.WEI_XIN_APP_PAY.toString()) + .mch_id(unionPaymentSetting.getUnionPayMachId()) + .appid(unionPaymentSetting.getUnionPayAppId()) + .out_trade_no(WxPayKit.generateStr()) + .body(cashierParam.getDetail()) + .attach(attach) + .total_fee("0") + .mch_create_ip(ip) + .notify_url(notifyUrl) + .nonce_str(WxPayKit.generateStr()) + .build() + .createSign(unionPaymentSetting.getUnionPayKey(), SignType.MD5); + + System.out.println(params); + + String xmlResult = UnionPayApi.execution(unionPaymentSetting.getUnionPayServerUrl(), params); + log.info("xmlResult:" + xmlResult); + Map result = WxPayKit.xmlToMap(xmlResult); + if (!WxPayKit.verifyNotify(result, unionPaymentSetting.getUnionPayKey(), SignType.MD5)) { + log.error("签名异常"); + } + String status = result.get("status"); + String resultCode = result.get("result_code"); + if (!"0".equals(status) && !"0".equals(resultCode)) { + log.error(result.get("err_msg")); + return null; + } + log.error(result.get("pay_info")); + return null; + } catch (Exception e) { + log.error(e.getMessage()); + e.printStackTrace(); + + return null; + + } + } + + @Override + public ResultMessage jsApiPay(HttpServletRequest request, PayParam payParam) { + String buyerLogonId=""; + String buyerId=""; + CashierParam cashierParam = cashierSupport.cashierParam(payParam); + UnionPaymentSetting unionPaymentSetting = this.unionPaymentSetting(); + String attach = URLEncoder.createDefault().encode(JSONUtil.toJsonStr(payParam), StandardCharsets.UTF_8); + //用户ip + String ip = IpKit.getRealIp(request); + try { + if (StrUtil.isEmpty(buyerLogonId) && StrUtil.isEmpty(buyerId)) { + log.error("buyer_logon_id buyer_id 不能同时为空"); + return null; + } + + String notifyUrl = unionPaymentSetting.getUnionPayDomain().concat("/unionPay/payNotify"); + + + Map params = UnifiedOrderModel.builder() + .service(ServiceEnum.ALI_PAY_JS_PAY.toString()) + .mch_id(unionPaymentSetting.getUnionPayMachId()) + .out_trade_no(WxPayKit.generateStr()) + .body(cashierParam.getDetail()) + .attach(attach) + .total_fee("0") + .mch_create_ip(ip) + .notify_url(notifyUrl) + .nonce_str(WxPayKit.generateStr()) + .buyer_id(buyerId) + .buyer_logon_id(buyerLogonId) + .build() + .createSign(unionPaymentSetting.getUnionPayKey(), SignType.MD5); + + System.out.println(params); + + String xmlResult = UnionPayApi.execution(unionPaymentSetting.getUnionPayServerUrl(), params); + log.info("xmlResult:" + xmlResult); + Map result = WxPayKit.xmlToMap(xmlResult); + log.info(result.toString()); + return null; + } catch (Exception e) { + e.printStackTrace(); + return null; + } + } + + + @Override + public void callBack(HttpServletRequest request) { + try { + verifyNotify(request); + } catch (Exception e) { + log.error("支付异常", e); + } + } + + @Override + public void notify(HttpServletRequest request) { + try { + verifyNotify(request); + } catch (Exception e) { + log.error("支付异常", e); + } + } + + private void verifyNotify(HttpServletRequest request) throws Exception { + String xmlMsg = HttpKit.readData(request); + log.info("支付通知=" + xmlMsg); + Map params = WxPayKit.xmlToMap(xmlMsg); + + String status = params.get("status"); + String returnCode = params.get("result_code"); + + log.info(status + " " + returnCode); + + if ("0".equals(status) && "0".equals(returnCode)) { + UnionPaymentSetting unionPaymentSetting = this.unionPaymentSetting(); + // 注意重复通知的情况,同一订单号可能收到多次通知,请注意一定先判断订单状态 + // 注意此处签名方式需与统一下单的签名类型一致 + if (WxPayKit.verifyNotify(params, unionPaymentSetting.getUnionPayKey(), SignType.MD5)) { + log.info("支付成功...."); + // 更新订单信息 + // 发送通知等 + + } + } + + } + /** + * 获取微信支付配置 + * + * @return + */ + private UnionPaymentSetting unionPaymentSetting() { + try { + Setting systemSetting = settingService.get(SettingEnum.UNIONPAY_PAYMENT.name()); + UnionPaymentSetting unionPaymentSetting = JSONUtil.toBean(systemSetting.getSettingValue(), UnionPaymentSetting.class); + return unionPaymentSetting; + } catch (Exception e) { + log.error("微信支付暂不支持", e); + throw new ServiceException(ResultCode.PAY_NOT_SUPPORT); + } + } +} diff --git a/framework/src/main/java/cn/lili/modules/payment/kit/plugin/unionpay/enums/ServiceEnum.java b/framework/src/main/java/cn/lili/modules/payment/kit/plugin/unionpay/enums/ServiceEnum.java new file mode 100644 index 00000000..856dc00a --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/payment/kit/plugin/unionpay/enums/ServiceEnum.java @@ -0,0 +1,95 @@ +package cn.lili.modules.payment.kit.plugin.unionpay.enums; + +/** + *

IJPay 让支付触手可及,封装了微信支付、支付宝支付、银联支付常用的支付方式以及各种常用的接口。

+ * + *

不依赖任何第三方 mvc 框架,仅仅作为工具使用简单快速完成支付模块的开发,可轻松嵌入到任何系统里。

+ * + *

IJPay 交流群: 723992875

+ * + *

Node.js 版: https://gitee.com/javen205/TNWX

+ * + *

云闪付接口类型枚举

+ * + * @author Javen + */ +public enum ServiceEnum { + /** + * 刷卡支付 + */ + MICRO_PAY("unified.trade.micropay"), + /** + * 扫码支付 + */ + NATIVE("unified.trade.native"), + /** + * 微信公众号、小程序支付统一下单 + */ + WEI_XIN_JS_PAY("pay.weixin.jspay"), + /** + * 微信 App 支付 + */ + WEI_XIN_APP_PAY("pay.weixin.raw.app"), + /** + * 查询订单 + */ + QUERY("unified.trade.query"), + /** + * 申请退款 + */ + REFUND("unified.trade.refund"), + /** + * 退款查询 + */ + REFUND_QUERY("unified.trade.refundquery"), + /** + * 关闭订单 + */ + CLOSE("unified.trade.close"), + /** + * 撤销订单 + */ + MICRO_PAY_REVERSE("unified.micropay.reverse"), + /** + * 授权码查询 openid + */ + AUTH_CODE_TO_OPENID("unified.tools.authcodetoopenid"), + /** + * 银联 JS 支付获取 userId + */ + UNION_PAY_USER_ID("pay.unionpay.userid"), + /** + * 银联 JS 支付下单 + */ + UNION_JS_PAY("pay.unionpay.jspay"), + /** + * 支付宝服务窗口支付 + */ + ALI_PAY_JS_PAY("pay.alipay.jspay"), + /** + * 下载单个商户时的对账单 + */ + BILL_MERCHANT("pay.bill.merchant"), + /** + * 下载连锁商户下所有门店的对账单 + */ + BILL_BIG_MERCHANT("pay.bill.bigMerchant"), + /** + * 下载某内部机构/外包服务机构下所有商户的对账单 + */ + BILL_AGENT("pay.bill.agent"); + + /** + * 接口类型 + */ + private final String service; + + ServiceEnum(String domain) { + this.service = domain; + } + + @Override + public String toString() { + return service; + } +} diff --git a/framework/src/main/java/cn/lili/modules/payment/kit/plugin/unionpay/model/ApplyModel.java b/framework/src/main/java/cn/lili/modules/payment/kit/plugin/unionpay/model/ApplyModel.java new file mode 100644 index 00000000..c440a65c --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/payment/kit/plugin/unionpay/model/ApplyModel.java @@ -0,0 +1,55 @@ +/** + *

IJPay 让支付触手可及,封装了微信支付、支付宝支付、银联支付常用的支付方式以及各种常用的接口。

+ * + *

不依赖任何第三方 mvc 框架,仅仅作为工具使用简单快速完成支付模块的开发,可轻松嵌入到任何系统里。

+ * + *

IJPay 交流群: 723992875

+ * + *

Node.js 版: https://gitee.com/javen205/TNWX

+ * + *

云闪付-商户进件

+ * + * @author Javen + */ +package cn.lili.modules.payment.kit.plugin.unionpay.model; + + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.Setter; + +@Builder +@AllArgsConstructor +@Getter +@Setter +public class ApplyModel extends BaseModel{ + /** + * 合作伙伴 ID 即机构号 + */ + private String partner; + /** + * 服务名称 + */ + private String serviceName; + /** + * 支持 MD5 和RSA,默认为MD5 + */ + private String signType; + /** + * 字符集,默认为UTF-8 + */ + private String charset; + /** + * 请求数据 + */ + private String data; + /** + * 数据类型 + */ + private String dataType; + /** + * 数据签名 + */ + private String dataSign; +} diff --git a/framework/src/main/java/cn/lili/modules/payment/kit/plugin/unionpay/model/AuthCodeToOpenIdModel.java b/framework/src/main/java/cn/lili/modules/payment/kit/plugin/unionpay/model/AuthCodeToOpenIdModel.java new file mode 100644 index 00000000..ef0cab1e --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/payment/kit/plugin/unionpay/model/AuthCodeToOpenIdModel.java @@ -0,0 +1,37 @@ +/** + *

IJPay 让支付触手可及,封装了微信支付、支付宝支付、银联支付常用的支付方式以及各种常用的接口。

+ * + *

不依赖任何第三方 mvc 框架,仅仅作为工具使用简单快速完成支付模块的开发,可轻松嵌入到任何系统里。

+ * + *

IJPay 交流群: 723992875

+ * + *

Node.js 版: https://gitee.com/javen205/TNWX

+ * + *

云闪付-授权码查询 openId

+ * + * @author Javen + */ +package cn.lili.modules.payment.kit.plugin.unionpay.model; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.Setter; + +@Builder +@AllArgsConstructor +@Getter +@Setter +public class AuthCodeToOpenIdModel extends BaseModel{ + private String service; + private String version; + private String charset; + private String sign_type; + private String mch_id; + private String sub_appid; + private String auth_code; + private String nonce_str; + private String sign; + private String sign_agentno; + private String groupno; +} diff --git a/framework/src/main/java/cn/lili/modules/payment/kit/plugin/unionpay/model/BaseModel.java b/framework/src/main/java/cn/lili/modules/payment/kit/plugin/unionpay/model/BaseModel.java new file mode 100644 index 00000000..1935bb59 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/payment/kit/plugin/unionpay/model/BaseModel.java @@ -0,0 +1,105 @@ +/** + *

IJPay 让支付触手可及,封装了微信支付、支付宝支付、银联支付常用的支付方式以及各种常用的接口。

+ * + *

不依赖任何第三方 mvc 框架,仅仅作为工具使用简单快速完成支付模块的开发,可轻松嵌入到任何系统里。

+ * + *

IJPay 交流群: 723992875

+ * + *

Node.js 版: https://gitee.com/javen205/TNWX

+ * + *

Model 公用方法

+ * + * @author Javen + */ +package cn.lili.modules.payment.kit.plugin.unionpay.model; + +import cn.hutool.core.util.StrUtil; +import cn.lili.modules.payment.kit.core.enums.SignType; +import cn.lili.modules.payment.kit.core.kit.WxPayKit; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.HashMap; +import java.util.Map; + +public class BaseModel { + + /** + * 将建构的 builder 转为 Map + * + * @return 转化后的 Map + */ + public Map toMap() { + String[] fieldNames = getFiledNames(this); + HashMap map = new HashMap(fieldNames.length); + for (String name : fieldNames) { + String value = (String) getFieldValueByName(name, this); + if (StrUtil.isNotEmpty(value)) { + map.put(name, value); + } + } + return map; + } + + /** + * 构建签名 Map + * + * @param partnerKey API KEY + * @param signType {@link SignType} 签名类型 + * @return 构建签名后的 Map + */ + public Map createSign(String partnerKey, SignType signType) { + return createSign(partnerKey, signType, true); + } + + /** + * 构建签名 Map + * + * @param partnerKey API KEY + * @param signType {@link SignType} 签名类型 + * @param haveSignType 签名是否包含 sign_type 字段 + * @return 构建签名后的 Map + */ + public Map createSign(String partnerKey, SignType signType, boolean haveSignType) { + return WxPayKit.buildSign(toMap(), partnerKey, signType, haveSignType); + } + + + + /** + * 获取属性名数组 + * + * @param obj 对象 + * @return 返回对象属性名数组 + */ + public String[] getFiledNames(Object obj) { + Field[] fields = obj.getClass().getDeclaredFields(); + String[] fieldNames = new String[fields.length]; + for (int i = 0; i < fields.length; i++) { + fieldNames[i] = fields[i].getName(); + } + return fieldNames; + } + + /** + * 根据属性名获取属性值 + * + * @param fieldName 属性名称 + * @param obj 对象 + * @return 返回对应属性的值 + */ + public Object getFieldValueByName(String fieldName, Object obj) { + try { + String firstLetter = fieldName.substring(0, 1).toUpperCase(); + String getter = new StringBuffer().append("get") + .append(firstLetter) + .append(fieldName.substring(1)) + .toString(); + Method method = obj.getClass().getMethod(getter); + return method.invoke(obj); + } catch (Exception e) { + return null; + } + } + +} diff --git a/framework/src/main/java/cn/lili/modules/payment/kit/plugin/unionpay/model/BillDownloadModel.java b/framework/src/main/java/cn/lili/modules/payment/kit/plugin/unionpay/model/BillDownloadModel.java new file mode 100644 index 00000000..90049165 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/payment/kit/plugin/unionpay/model/BillDownloadModel.java @@ -0,0 +1,35 @@ +/** + *

IJPay 让支付触手可及,封装了微信支付、支付宝支付、银联支付常用的支付方式以及各种常用的接口。

+ * + *

不依赖任何第三方 mvc 框架,仅仅作为工具使用简单快速完成支付模块的开发,可轻松嵌入到任何系统里。

+ * + *

IJPay 交流群: 723992875

+ * + *

Node.js 版: https://gitee.com/javen205/TNWX

+ * + *

云闪付-下单对账单

+ * + * @author Javen + */ +package cn.lili.modules.payment.kit.plugin.unionpay.model; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.Setter; + +@Builder +@AllArgsConstructor +@Getter +@Setter +public class BillDownloadModel extends BaseModel{ + private String service; + private String version; + private String charset; + private String bill_date; + private String bill_type; + private String sign_type; + private String mch_id; + private String nonce_str; + private String sign; +} diff --git a/framework/src/main/java/cn/lili/modules/payment/kit/plugin/unionpay/model/CloseOrderModel.java b/framework/src/main/java/cn/lili/modules/payment/kit/plugin/unionpay/model/CloseOrderModel.java new file mode 100644 index 00000000..c9d3ca25 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/payment/kit/plugin/unionpay/model/CloseOrderModel.java @@ -0,0 +1,36 @@ +/** + *

IJPay 让支付触手可及,封装了微信支付、支付宝支付、银联支付常用的支付方式以及各种常用的接口。

+ * + *

不依赖任何第三方 mvc 框架,仅仅作为工具使用简单快速完成支付模块的开发,可轻松嵌入到任何系统里。

+ * + *

IJPay 交流群: 723992875

+ * + *

Node.js 版: https://gitee.com/javen205/TNWX

+ * + *

云闪付-关闭订单

+ * + * @author Javen + */ +package cn.lili.modules.payment.kit.plugin.unionpay.model; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.Setter; + +@Builder +@AllArgsConstructor +@Getter +@Setter +public class CloseOrderModel extends BaseModel{ + private String service; + private String version; + private String charset; + private String sign_type; + private String mch_id; + private String out_trade_no; + private String nonce_str; + private String sign; + private String sign_agentno; + private String groupno; +} diff --git a/framework/src/main/java/cn/lili/modules/payment/kit/plugin/unionpay/model/MicroPayModel.java b/framework/src/main/java/cn/lili/modules/payment/kit/plugin/unionpay/model/MicroPayModel.java new file mode 100644 index 00000000..e48180a6 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/payment/kit/plugin/unionpay/model/MicroPayModel.java @@ -0,0 +1,51 @@ +/** + *

IJPay 让支付触手可及,封装了微信支付、支付宝支付、银联支付常用的支付方式以及各种常用的接口。

+ * + *

不依赖任何第三方 mvc 框架,仅仅作为工具使用简单快速完成支付模块的开发,可轻松嵌入到任何系统里。

+ * + *

IJPay 交流群: 723992875

+ * + *

Node.js 版: https://gitee.com/javen205/TNWX

+ * + *

云闪付-付款码支付

+ * + * @author Javen + */ +package cn.lili.modules.payment.kit.plugin.unionpay.model; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.Setter; + +@Builder +@AllArgsConstructor +@Getter +@Setter +public class MicroPayModel extends BaseModel { + private String service; + private String version; + private String charset; + private String sign_type; + private String mch_id; + private String out_trade_no; + private String device_info; + private String body; + private String goods_detail; + private String sub_appid; + private String attach; + private String need_receipt; + private String total_fee; + private String mch_create_ip; + private String auth_code; + private String time_start; + private String time_expire; + private String op_user_id; + private String op_shop_id; + private String op_device_id; + private String goods_tag; + private String nonce_str; + private String sign; + private String sign_agentno; + private String groupno; +} diff --git a/framework/src/main/java/cn/lili/modules/payment/kit/plugin/unionpay/model/OrderQueryModel.java b/framework/src/main/java/cn/lili/modules/payment/kit/plugin/unionpay/model/OrderQueryModel.java new file mode 100644 index 00000000..4dd9b660 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/payment/kit/plugin/unionpay/model/OrderQueryModel.java @@ -0,0 +1,37 @@ +/** + *

IJPay 让支付触手可及,封装了微信支付、支付宝支付、银联支付常用的支付方式以及各种常用的接口。

+ * + *

不依赖任何第三方 mvc 框架,仅仅作为工具使用简单快速完成支付模块的开发,可轻松嵌入到任何系统里。

+ * + *

IJPay 交流群: 723992875

+ * + *

Node.js 版: https://gitee.com/javen205/TNWX

+ * + *

云闪付-订单查询

+ * + * @author Javen + */ +package cn.lili.modules.payment.kit.plugin.unionpay.model; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.Setter; + +@Builder +@AllArgsConstructor +@Getter +@Setter +public class OrderQueryModel extends BaseModel { + private String service; + private String version; + private String charset; + private String sign_type; + private String mch_id; + private String out_trade_no; + private String transaction_id; + private String sign_agentno; + private String groupno; + private String nonce_str; + private String sign; +} diff --git a/framework/src/main/java/cn/lili/modules/payment/kit/plugin/unionpay/model/RefundModel.java b/framework/src/main/java/cn/lili/modules/payment/kit/plugin/unionpay/model/RefundModel.java new file mode 100644 index 00000000..a1be6be2 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/payment/kit/plugin/unionpay/model/RefundModel.java @@ -0,0 +1,43 @@ +/** + *

IJPay 让支付触手可及,封装了微信支付、支付宝支付、银联支付常用的支付方式以及各种常用的接口。

+ * + *

不依赖任何第三方 mvc 框架,仅仅作为工具使用简单快速完成支付模块的开发,可轻松嵌入到任何系统里。

+ * + *

IJPay 交流群: 723992875

+ * + *

Node.js 版: https://gitee.com/javen205/TNWX

+ * + *

云闪付-退款

+ * + * @author Javen + */ +package cn.lili.modules.payment.kit.plugin.unionpay.model; + + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.Setter; + +@Builder +@AllArgsConstructor +@Getter +@Setter +public class RefundModel extends BaseModel{ + private String service; + private String version; + private String charset; + private String sign_type; + private String mch_id; + private String out_trade_no; + private String transaction_id; + private String out_refund_no; + private String total_fee; + private String refund_fee; + private String op_user_id; + private String refund_channel; + private String nonce_str; + private String sign; + private String sign_agentno; + private String groupno; +} diff --git a/framework/src/main/java/cn/lili/modules/payment/kit/plugin/unionpay/model/RefundQueryModel.java b/framework/src/main/java/cn/lili/modules/payment/kit/plugin/unionpay/model/RefundQueryModel.java new file mode 100644 index 00000000..8808eee9 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/payment/kit/plugin/unionpay/model/RefundQueryModel.java @@ -0,0 +1,39 @@ +/** + *

IJPay 让支付触手可及,封装了微信支付、支付宝支付、银联支付常用的支付方式以及各种常用的接口。

+ * + *

不依赖任何第三方 mvc 框架,仅仅作为工具使用简单快速完成支付模块的开发,可轻松嵌入到任何系统里。

+ * + *

IJPay 交流群: 723992875

+ * + *

Node.js 版: https://gitee.com/javen205/TNWX

+ * + *

云闪付-查询退款

+ * + * @author Javen + */ +package cn.lili.modules.payment.kit.plugin.unionpay.model; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.Setter; + +@Builder +@AllArgsConstructor +@Getter +@Setter +public class RefundQueryModel extends BaseModel{ + private String service; + private String version; + private String charset; + private String sign_type; + private String mch_id; + private String out_trade_no; + private String transaction_id; + private String out_refund_no; + private String refund_id; + private String nonce_str; + private String sign; + private String sign_agentno; + private String groupno; +} diff --git a/framework/src/main/java/cn/lili/modules/payment/kit/plugin/unionpay/model/ReverseModel.java b/framework/src/main/java/cn/lili/modules/payment/kit/plugin/unionpay/model/ReverseModel.java new file mode 100644 index 00000000..f0efb4b6 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/payment/kit/plugin/unionpay/model/ReverseModel.java @@ -0,0 +1,36 @@ +/** + *

IJPay 让支付触手可及,封装了微信支付、支付宝支付、银联支付常用的支付方式以及各种常用的接口。

+ * + *

不依赖任何第三方 mvc 框架,仅仅作为工具使用简单快速完成支付模块的开发,可轻松嵌入到任何系统里。

+ * + *

IJPay 交流群: 723992875

+ * + *

Node.js 版: https://gitee.com/javen205/TNWX

+ * + *

云闪付-撤销订单

+ * + * @author Javen + */ +package cn.lili.modules.payment.kit.plugin.unionpay.model; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.Setter; + +@Builder +@AllArgsConstructor +@Getter +@Setter +public class ReverseModel extends BaseModel{ + private String service; + private String version; + private String charset; + private String sign_type; + private String mch_id; + private String out_trade_no; + private String nonce_str; + private String sign; + private String sign_agentno; + private String groupno; +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/payment/kit/plugin/unionpay/model/UnifiedOrderModel.java b/framework/src/main/java/cn/lili/modules/payment/kit/plugin/unionpay/model/UnifiedOrderModel.java new file mode 100644 index 00000000..b760bed7 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/payment/kit/plugin/unionpay/model/UnifiedOrderModel.java @@ -0,0 +1,60 @@ +/** + *

IJPay 让支付触手可及,封装了微信支付、支付宝支付、银联支付常用的支付方式以及各种常用的接口。

+ * + *

不依赖任何第三方 mvc 框架,仅仅作为工具使用简单快速完成支付模块的开发,可轻松嵌入到任何系统里。

+ * + *

IJPay 交流群: 723992875

+ * + *

Node.js 版: https://gitee.com/javen205/TNWX

+ * + *

云闪付-统一下单

+ * + * @author Javen + */ +package cn.lili.modules.payment.kit.plugin.unionpay.model; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.Setter; + +@Builder +@AllArgsConstructor +@Getter +@Setter +public class UnifiedOrderModel extends BaseModel { + private String service; + private String version; + private String charset; + private String sign_type; + private String mch_id; + private String appid; + private String is_raw; + private String is_minipg; + private String out_trade_no; + private String device_info; + private String op_shop_id; + private String body; + private String sub_openid; + private String user_id; + private String attach; + private String sub_appid; + private String total_fee; + private String need_receipt; + private String customer_ip; + private String mch_create_ip; + private String notify_url; + private String time_start; + private String time_expire; + private String qr_code_timeout_express; + private String op_user_id; + private String goods_tag; + private String product_id; + private String nonce_str; + private String buyer_logon_id; + private String buyer_id; + private String limit_credit_pay; + private String sign; + private String sign_agentno; + private String groupno; +} diff --git a/framework/src/main/java/cn/lili/modules/payment/kit/plugin/unionpay/model/UnionPayUserIdModel.java b/framework/src/main/java/cn/lili/modules/payment/kit/plugin/unionpay/model/UnionPayUserIdModel.java new file mode 100644 index 00000000..a0795dd3 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/payment/kit/plugin/unionpay/model/UnionPayUserIdModel.java @@ -0,0 +1,37 @@ +/** + *

IJPay 让支付触手可及,封装了微信支付、支付宝支付、银联支付常用的支付方式以及各种常用的接口。

+ * + *

不依赖任何第三方 mvc 框架,仅仅作为工具使用简单快速完成支付模块的开发,可轻松嵌入到任何系统里。

+ * + *

IJPay 交流群: 723992875

+ * + *

Node.js 版: https://gitee.com/javen205/TNWX

+ * + *

云闪付-银联 JS 支付获取 userId

+ * + * @author Javen + */ +package cn.lili.modules.payment.kit.plugin.unionpay.model; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.Setter; + +@Builder +@AllArgsConstructor +@Getter +@Setter +public class UnionPayUserIdModel extends BaseModel{ + private String service; + private String version; + private String charset; + private String sign_type; + private String mch_id; + private String nonce_str; + private String sign; + private String user_auth_code; + private String app_up_identifier; + private String sign_agentno; + private String groupno; +} diff --git a/framework/src/main/java/cn/lili/modules/payment/kit/plugin/wechat/WechatPlugin.java b/framework/src/main/java/cn/lili/modules/payment/kit/plugin/wechat/WechatPlugin.java index 3ba64830..3497d8e2 100644 --- a/framework/src/main/java/cn/lili/modules/payment/kit/plugin/wechat/WechatPlugin.java +++ b/framework/src/main/java/cn/lili/modules/payment/kit/plugin/wechat/WechatPlugin.java @@ -42,6 +42,7 @@ import cn.lili.modules.system.entity.dos.Setting; import cn.lili.modules.system.entity.dto.payment.WechatPaymentSetting; import cn.lili.modules.system.entity.enums.SettingEnum; import cn.lili.modules.system.service.SettingService; +import cn.lili.modules.wallet.entity.dos.MemberWithdrawApply; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; @@ -52,6 +53,8 @@ import javax.servlet.http.HttpServletResponse; import java.nio.charset.StandardCharsets; import java.security.GeneralSecurityException; import java.security.cert.X509Certificate; +import java.util.ArrayList; +import java.util.List; import java.util.Map; import java.util.Objects; @@ -465,6 +468,63 @@ public class WechatPlugin implements Payment { } } + /** + * 微信提现 + * 文档地址:https://pay.weixin.qq.com/docs/merchant/apis/batch-transfer-to-balance/transfer-batch/initiate-batch-transfer.html + * + * @param memberWithdrawApply 会员提现申请 + */ + @Override + public void transfer(MemberWithdrawApply memberWithdrawApply) { + + try { + WechatPaymentSetting setting = wechatPaymentSetting(); + Connect connect = connectService.queryConnect( + ConnectQueryDTO.builder().userId(UserContext.getCurrentUser().getId()) + .unionType(ConnectEnum.WECHAT_OPEN_ID.name()).build() + ); + //根据自身情况设置AppId,此处我存放的是服务号的APPID,下方的openID需要对应此处的APPID配置 + TransferModel transferModel = new TransferModel() + .setAppid(setting.getServiceAppId()) + .setOut_batch_no(SnowFlake.createStr("T")) + .setBatch_name("用户提现") + .setBatch_remark("用户提现") + .setTotal_amount(CurrencyUtil.fen(memberWithdrawApply.getApplyMoney())) + .setTotal_num(1) + .setTransfer_scene_id("1000"); + List transferDetailListList = new ArrayList<>(); + { + TransferDetailInput transferDetailInput = new TransferDetailInput(); + transferDetailInput.setOut_detail_no(SnowFlake.createStr("TD")); + transferDetailInput.setTransfer_amount(CurrencyUtil.fen(memberWithdrawApply.getApplyMoney())); + transferDetailInput.setTransfer_remark("用户提现"); + transferDetailInput.setOpenid(connect.getUnionId()); +// transferDetailInput.setUserName( +// "757b340b45ebef5467rter35gf464344v3542sdf4t6re4tb4f54ty45t4yyry45"); +// transferDetailInput.setUserIdCard( +// "8609cb22e1774a50a930e414cc71eca06121bcd266335cda230d24a7886a8d9f"); + transferDetailListList.add(transferDetailInput); + } + transferModel.setTransfer_detail_list(transferDetailListList); + + PaymentHttpResponse response = WechatApi.v3( + RequestMethodEnums.POST, + WechatDomain.CHINA.toString(), + WechatApiEnum.TRANSFER_BATCHES.toString(), + setting.getMchId(), + setting.getSerialNumber(), + null, + setting.getApiclient_key(), + JSONUtil.toJsonStr(transferModel) + ); + log.info("微信提现响应 {}", response); + //根据自身业务进行接下来的任务处理 + } catch (Exception e) { + e.printStackTrace(); + } + + } + /** * 验证结果,执行支付回调 * diff --git a/framework/src/main/java/cn/lili/modules/payment/kit/plugin/wechat/enums/WechatApiEnum.java b/framework/src/main/java/cn/lili/modules/payment/kit/plugin/wechat/enums/WechatApiEnum.java index 49306c74..a7303a6e 100644 --- a/framework/src/main/java/cn/lili/modules/payment/kit/plugin/wechat/enums/WechatApiEnum.java +++ b/framework/src/main/java/cn/lili/modules/payment/kit/plugin/wechat/enums/WechatApiEnum.java @@ -555,6 +555,11 @@ public enum WechatApiEnum { * 连锁品牌-删除分账接收方 */ BRAND_PROFIT_SHARING_RECEIVERS_delete("/v3/brand/profitsharing/receivers/delete"), + + /** + * 发起商家转账 + */ + TRANSFER_BATCHES("/v3/transfer/batches"), ; /** diff --git a/framework/src/main/java/cn/lili/modules/payment/kit/plugin/wechat/model/TransferDetailInput.java b/framework/src/main/java/cn/lili/modules/payment/kit/plugin/wechat/model/TransferDetailInput.java new file mode 100644 index 00000000..bbb603fe --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/payment/kit/plugin/wechat/model/TransferDetailInput.java @@ -0,0 +1,29 @@ +package cn.lili.modules.payment.kit.plugin.wechat.model; + +import lombok.Data; +import lombok.experimental.Accessors; + +/** + * 转账的明细 + * + * @author Bulbasaur + */ +@Data +@Accessors(chain = true) +public class TransferDetailInput { + //商户系统内部区分转账批次单下不同转账明细单的唯一标识,要求此参数只能由数字、大小写字母组成 + private String out_detail_no; + //转账金额单位为“分” + private Integer transfer_amount; + //单条转账备注(微信用户会收到该备注),UTF8编码,最多允许32个字符 + private String transfer_remark; + //商户appid下,某用户的openid + private String openid; + + //收款方真实姓名。支持标准RSA算法和国密算法,公钥由微信侧提供 + //明细转账金额<0.3元时,不允许填写收款用户姓名 + //明细转账金额 >= 2,000元时,该笔明细必须填写收款用户姓名 + //同一批次转账明细中的姓名字段传入规则需保持一致,也即全部填写、或全部不填写 + //若商户传入收款用户姓名,微信支付会校验用户openID与姓名是否一致,并提供电子回单 + //private String user_name; +} diff --git a/framework/src/main/java/cn/lili/modules/payment/kit/plugin/wechat/model/TransferModel.java b/framework/src/main/java/cn/lili/modules/payment/kit/plugin/wechat/model/TransferModel.java new file mode 100644 index 00000000..a81abeca --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/payment/kit/plugin/wechat/model/TransferModel.java @@ -0,0 +1,31 @@ +package cn.lili.modules.payment.kit.plugin.wechat.model; + +import lombok.Data; +import lombok.experimental.Accessors; + +import java.util.List; + +/** + * 提现 + * @author Bulbasaur + */ +@Data +@Accessors(chain = true) +public class TransferModel { + //申请商户号的appid或商户号绑定的appid(企业号corpid即为此appid) + private String appid; + //商户系统内部的商家批次单号,要求此参数只能由数字、大小写字母组成,在商户系统内部唯一 + private String out_batch_no; + //该笔批量转账的名称 + private String batch_name; + //转账说明,UTF8编码,最多允许32个字符 + private String batch_remark; + //转账金额单位为“分”。转账总金额必须与批次内所有明细转账金额之和保持一致,否则无法发起转账操作 + private Integer total_amount; + //一个转账批次单最多发起一千笔转账。转账总笔数必须与批次内所有明细之和保持一致,否则无法发起转账操作 + private Integer total_num; + //发起批量转账的明细列表,最多一千笔 + private List transfer_detail_list; + //必填,指定该笔转账使用的转账场景ID + private String transfer_scene_id; +} diff --git a/framework/src/main/java/cn/lili/modules/sms/SmsUtil.java b/framework/src/main/java/cn/lili/modules/sms/SmsUtil.java index fbafeb10..0f0c4621 100644 --- a/framework/src/main/java/cn/lili/modules/sms/SmsUtil.java +++ b/framework/src/main/java/cn/lili/modules/sms/SmsUtil.java @@ -3,7 +3,6 @@ package cn.lili.modules.sms; import cn.lili.modules.verification.entity.enums.VerificationEnums; import java.util.List; -import java.util.Map; /** * 短信接口 @@ -35,15 +34,6 @@ public interface SmsUtil { */ boolean verifyCode(String mobile, VerificationEnums verificationEnums, String uuid, String code); - /** - * 短信发送 - * - * @param mobile 接收手机号 - * @param param 参数 - * @param templateCode 模版code - * @param signName 签名名称 - */ - void sendSmsCode(String signName, String mobile, Map param, String templateCode); /** * 短信批量发送 diff --git a/framework/src/main/java/cn/lili/modules/sms/entity/enums/SmsEnum.java b/framework/src/main/java/cn/lili/modules/sms/entity/enums/SmsEnum.java new file mode 100644 index 00000000..894e6b37 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/sms/entity/enums/SmsEnum.java @@ -0,0 +1,15 @@ +package cn.lili.modules.sms.entity.enums; + +/** + * 短信通道枚举 + * + * @author Bulbasaur + * @since 2023-02-16 + */ +public enum SmsEnum { + + /** + * 短信渠道 + */ + ALI, HUAWEI, TENCENT; +} diff --git a/framework/src/main/java/cn/lili/modules/sms/impl/SmsUtilAliImplService.java b/framework/src/main/java/cn/lili/modules/sms/impl/SmsUtilAliImplService.java index 513748c3..65fe31ba 100644 --- a/framework/src/main/java/cn/lili/modules/sms/impl/SmsUtilAliImplService.java +++ b/framework/src/main/java/cn/lili/modules/sms/impl/SmsUtilAliImplService.java @@ -1,7 +1,6 @@ package cn.lili.modules.sms.impl; import cn.hutool.core.util.StrUtil; -import cn.hutool.json.JSONUtil; import cn.lili.cache.Cache; import cn.lili.cache.CachePrefix; import cn.lili.common.enums.ResultCode; @@ -9,28 +8,22 @@ import cn.lili.common.exception.ServiceException; import cn.lili.common.properties.SmsTemplateProperties; import cn.lili.common.properties.SystemSettingProperties; import cn.lili.common.security.context.UserContext; -import cn.lili.common.utils.Base64Utils; import cn.lili.common.utils.CommonUtil; import cn.lili.modules.member.entity.dos.Member; import cn.lili.modules.member.service.MemberService; -import cn.lili.modules.sms.AliSmsUtil; import cn.lili.modules.sms.SmsUtil; -import cn.lili.modules.sms.entity.dos.SmsSign; -import cn.lili.modules.sms.entity.dos.SmsTemplate; +import cn.lili.modules.sms.plugin.SmsPluginFactory; import cn.lili.modules.system.entity.dos.Setting; import cn.lili.modules.system.entity.dto.SmsSetting; import cn.lili.modules.system.entity.enums.SettingEnum; import cn.lili.modules.system.service.SettingService; import cn.lili.modules.verification.entity.enums.VerificationEnums; -import com.aliyun.dysmsapi20170525.models.*; -import com.aliyun.teaopenapi.models.Config; import com.google.gson.Gson; import com.xkcoding.http.util.StringUtil; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; -import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -44,7 +37,7 @@ import java.util.Map; */ @Component @Slf4j -public class SmsUtilAliImplService implements SmsUtil, AliSmsUtil { +public class SmsUtilAliImplService implements SmsUtil { @Autowired private Cache cache; @@ -52,6 +45,8 @@ public class SmsUtilAliImplService implements SmsUtil, AliSmsUtil { private SettingService settingService; @Autowired private MemberService memberService; + @Autowired + private SmsPluginFactory smsPluginFactory; @Autowired private SmsTemplateProperties smsTemplateProperties; @@ -122,11 +117,11 @@ public class SmsUtilAliImplService implements SmsUtil, AliSmsUtil { //如果是测试模式 默认验证码 6个1 if (systemSettingProperties.getIsTestModel()) { code = "111111"; - log.info("测试模式 - 接收手机:{},验证码:{}",mobile,code); + log.info("测试模式 - 接收手机:{},验证码:{}", mobile, code); } else { - log.info("接收手机:{},验证码:{}",mobile,code); + log.info("接收手机:{},验证码:{}", mobile, code); //发送短信 - this.sendSmsCode(smsSetting.getSignName(), mobile, params, templateCode); + smsPluginFactory.smsPlugin().sendSmsCode(smsSetting.getSignName(), mobile, params, templateCode); } //缓存中写入要验证的信息 cache.put(cacheKey(verificationEnums, mobile, uuid), code, 300L); @@ -145,234 +140,12 @@ public class SmsUtilAliImplService implements SmsUtil, AliSmsUtil { } - @Override - public void sendSmsCode(String signName, String mobile, Map param, String templateCode) { - - com.aliyun.dysmsapi20170525.Client client = this.createClient(); - SendSmsRequest sendSmsRequest = new SendSmsRequest() - .setSignName(signName) - .setPhoneNumbers(mobile) - .setTemplateCode(templateCode) - .setTemplateParam(JSONUtil.toJsonStr(param)); - try { - SendSmsResponse response = client.sendSms(sendSmsRequest); - if (!("OK").equals(response.getBody().getCode())) { - throw new ServiceException(response.getBody().getMessage()); - } - } catch (Exception e) { - log.error("发送短信错误", e); - } - } - @Override public void sendBatchSms(String signName, List mobile, String templateCode) { - - com.aliyun.dysmsapi20170525.Client client = this.createClient(); - - List sign = new ArrayList(); - - sign.addAll(mobile); - sign.replaceAll(e -> signName); - - //手机号拆成多个小组进行发送 - List> mobileList = new ArrayList<>(); - - //签名名称多个小组 - List> signNameList = new ArrayList<>(); - - //循环分组 - for (int i = 0; i < (mobile.size() / 100 + (mobile.size() % 100 == 0 ? 0 : 1)); i++) { - int endPoint = Math.min((100 + (i * 100)), mobile.size()); - mobileList.add(mobile.subList((i * 100), endPoint)); - signNameList.add(sign.subList((i * 100), endPoint)); - } - -// //发送短信 - for (int i = 0; i < mobileList.size(); i++) { - SendBatchSmsRequest sendBatchSmsRequest = new SendBatchSmsRequest() - .setPhoneNumberJson(JSONUtil.toJsonStr(mobileList.get(i))) - .setSignNameJson(JSONUtil.toJsonStr(signNameList.get(i))) - .setTemplateCode(templateCode); - try { - client.sendBatchSms(sendBatchSmsRequest); - } catch (Exception e) { - log.error("批量发送短信错误", e); - } - } - + smsPluginFactory.smsPlugin().sendBatchSms(signName, mobile, templateCode); } - @Override - public void addSmsSign(SmsSign smsSign) throws Exception { - //设置参数添加短信签名 - com.aliyun.dysmsapi20170525.Client client = this.createClient(); - //营业执照 - AddSmsSignRequest.AddSmsSignRequestSignFileList signFileList0 = new AddSmsSignRequest.AddSmsSignRequestSignFileList() - .setFileContents(Base64Utils.encode(smsSign.getBusinessLicense())) - .setFileSuffix(smsSign.getBusinessLicense().substring(smsSign.getBusinessLicense().lastIndexOf(".") + 1)); - //授权委托书 - AddSmsSignRequest.AddSmsSignRequestSignFileList signFileList1 = new AddSmsSignRequest.AddSmsSignRequestSignFileList() - .setFileContents(Base64Utils.encode(smsSign.getLicense())) - .setFileSuffix(smsSign.getLicense().substring(smsSign.getLicense().lastIndexOf(".")) + 1); - //添加短信签名 - AddSmsSignRequest addSmsSignRequest = new AddSmsSignRequest() - .setSignName(smsSign.getSignName()) - .setSignSource(smsSign.getSignSource()) - .setRemark(smsSign.getRemark()) - .setSignFileList(java.util.Arrays.asList( - signFileList0, - signFileList1 - )); - AddSmsSignResponse response = client.addSmsSign(addSmsSignRequest); - if (!("OK").equals(response.getBody().getCode())) { - throw new ServiceException(response.getBody().getMessage()); - } - } - - @Override - public void deleteSmsSign(String signName) throws Exception { - com.aliyun.dysmsapi20170525.Client client = this.createClient(); - DeleteSmsSignRequest deleteSmsSignRequest = new DeleteSmsSignRequest() - .setSignName(signName); - - DeleteSmsSignResponse response = client.deleteSmsSign(deleteSmsSignRequest); - if (!("OK").equals(response.getBody().getCode())) { - throw new ServiceException(response.getBody().getMessage()); - } - - } - - @Override - public Map querySmsSign(String signName) throws Exception { - //设置参数查看短信签名 - com.aliyun.dysmsapi20170525.Client client = this.createClient(); - QuerySmsSignRequest querySmsSignRequest = new QuerySmsSignRequest().setSignName(signName); - - QuerySmsSignResponse response = client.querySmsSign(querySmsSignRequest); - if (!("OK").equals(response.getBody().getCode())) { - throw new ServiceException(response.getBody().getMessage()); - } - Map map = new HashMap<>(2); - map.put("SignStatus", response.getBody().getSignStatus()); - map.put("Reason", response.getBody().getReason()); - return map; - } - - @Override - public void modifySmsSign(SmsSign smsSign) throws Exception { - //设置参数添加短信签名 - com.aliyun.dysmsapi20170525.Client client = this.createClient(); - - ModifySmsSignRequest.ModifySmsSignRequestSignFileList signFileList0 = new ModifySmsSignRequest.ModifySmsSignRequestSignFileList() - .setFileContents(Base64Utils.encode(smsSign.getBusinessLicense())) - .setFileSuffix(smsSign.getBusinessLicense().substring(smsSign.getBusinessLicense().lastIndexOf(".") + 1)); - ModifySmsSignRequest.ModifySmsSignRequestSignFileList signFileList1 = new ModifySmsSignRequest.ModifySmsSignRequestSignFileList() - .setFileContents(Base64Utils.encode(smsSign.getLicense())) - .setFileSuffix(smsSign.getLicense().substring(smsSign.getBusinessLicense().lastIndexOf(".") + 1)); - ModifySmsSignRequest modifySmsSign = new ModifySmsSignRequest() - .setSignName(smsSign.getSignName()) - .setSignSource(smsSign.getSignSource()) - .setRemark(smsSign.getRemark()) - .setSignFileList(java.util.Arrays.asList( - signFileList0, - signFileList1 - )); - ModifySmsSignResponse response = client.modifySmsSign(modifySmsSign); - if (!("OK").equals(response.getBody().getCode())) { - throw new ServiceException(response.getBody().getMessage()); - } - } - - @Override - public void modifySmsTemplate(SmsTemplate smsTemplate) throws Exception { - com.aliyun.dysmsapi20170525.Client client = this.createClient(); - ModifySmsTemplateRequest modifySmsTemplateRequest = new ModifySmsTemplateRequest() - .setTemplateType(smsTemplate.getTemplateType()) - .setTemplateName(smsTemplate.getTemplateName()) - .setTemplateContent(smsTemplate.getTemplateContent()) - .setRemark(smsTemplate.getRemark()) - .setTemplateCode(smsTemplate.getTemplateCode()); - - ModifySmsTemplateResponse response = client.modifySmsTemplate(modifySmsTemplateRequest); - if (!("OK").equals(response.getBody().getCode())) { - throw new ServiceException(response.getBody().getMessage()); - } - } - - @Override - public Map querySmsTemplate(String templateCode) throws Exception { - com.aliyun.dysmsapi20170525.Client client = this.createClient(); - QuerySmsTemplateRequest querySmsTemplateRequest = new QuerySmsTemplateRequest() - .setTemplateCode(templateCode); - QuerySmsTemplateResponse response = client.querySmsTemplate(querySmsTemplateRequest); - - if (!("OK").equals(response.getBody().getCode())) { - throw new ServiceException(response.getBody().getMessage()); - } - Map map = new HashMap<>(4); - map.put("TemplateStatus", response.getBody().getTemplateStatus()); - map.put("Reason", response.getBody().getReason()); - map.put("TemplateCode", response.getBody().getTemplateCode()); - return map; - } - - @Override - public String addSmsTemplate(SmsTemplate smsTemplate) throws Exception { - com.aliyun.dysmsapi20170525.Client client = this.createClient(); - AddSmsTemplateRequest addSmsTemplateRequest = new AddSmsTemplateRequest() - .setTemplateType(1) - .setTemplateName(smsTemplate.getTemplateName()) - .setTemplateContent(smsTemplate.getTemplateContent()) - .setRemark(smsTemplate.getRemark()); - - AddSmsTemplateResponse response = client.addSmsTemplate(addSmsTemplateRequest); - if (!("OK").equals(response.getBody().getCode())) { - throw new ServiceException(response.getBody().getMessage()); - } - return response.getBody().getTemplateCode(); - } - - @Override - public void deleteSmsTemplate(String templateCode) throws Exception { - com.aliyun.dysmsapi20170525.Client client = this.createClient(); - DeleteSmsTemplateRequest deleteSmsTemplateRequest = new DeleteSmsTemplateRequest() - .setTemplateCode(templateCode); - - DeleteSmsTemplateResponse response = client.deleteSmsTemplate(deleteSmsTemplateRequest); - if (!("OK").equals(response.getBody().getCode())) { - throw new ServiceException(response.getBody().getMessage()); - } - } - - - /** - * 初始化账号Client - * - * @return Client 短信操作util - */ - public com.aliyun.dysmsapi20170525.Client createClient() { - try { - Setting setting = settingService.get(SettingEnum.SMS_SETTING.name()); - if (StrUtil.isBlank(setting.getSettingValue())) { - throw new ServiceException(ResultCode.ALI_SMS_SETTING_ERROR); - } - SmsSetting smsSetting = new Gson().fromJson(setting.getSettingValue(), SmsSetting.class); - - Config config = new Config(); - //您的AccessKey ID - config.accessKeyId = smsSetting.getAccessKeyId(); - //您的AccessKey Secret - config.accessKeySecret = smsSetting.getAccessSecret(); - //访问的域名 - config.endpoint = "dysmsapi.aliyuncs.com"; - return new com.aliyun.dysmsapi20170525.Client(config); - } catch (Exception e) { - log.error("短信初始化错误", e); - } - return null; - } - /** * 生成缓存key * diff --git a/framework/src/main/java/cn/lili/modules/sms/AliSmsUtil.java b/framework/src/main/java/cn/lili/modules/sms/plugin/SmsPlugin.java similarity index 70% rename from framework/src/main/java/cn/lili/modules/sms/AliSmsUtil.java rename to framework/src/main/java/cn/lili/modules/sms/plugin/SmsPlugin.java index abfab733..2000d81b 100644 --- a/framework/src/main/java/cn/lili/modules/sms/AliSmsUtil.java +++ b/framework/src/main/java/cn/lili/modules/sms/plugin/SmsPlugin.java @@ -1,16 +1,45 @@ -package cn.lili.modules.sms; +package cn.lili.modules.sms.plugin; import cn.lili.modules.sms.entity.dos.SmsSign; import cn.lili.modules.sms.entity.dos.SmsTemplate; +import cn.lili.modules.sms.entity.enums.SmsEnum; +import java.util.List; import java.util.Map; /** - * @author Chopper - * @version v4.1 - * @since 2021/2/1 6:05 下午 + * 短信插件接口 + * + * @author Bulbasaur + * @since 2023-02-16 */ -public interface AliSmsUtil { +public interface SmsPlugin { + + /** + * 插件名称 + */ + SmsEnum pluginName(); + + /** + * 短信发送 + * + * @param mobile 接收手机号 + * @param param 参数 + * @param templateCode 模版code + * @param signName 签名名称 + */ + void sendSmsCode(String signName, String mobile, Map param, String templateCode); + + /** + * 短信批量发送 + * + * @param mobile 接收手机号 + * @param signName 签名 + * @param templateCode 模版code + */ + void sendBatchSms(String signName, List mobile, String templateCode); + + /** * 申请短信签名 * diff --git a/framework/src/main/java/cn/lili/modules/sms/plugin/SmsPluginFactory.java b/framework/src/main/java/cn/lili/modules/sms/plugin/SmsPluginFactory.java new file mode 100644 index 00000000..0ae7bf5d --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/sms/plugin/SmsPluginFactory.java @@ -0,0 +1,59 @@ +package cn.lili.modules.sms.plugin; + +import cn.hutool.json.JSONUtil; +import cn.lili.common.exception.ServiceException; +import cn.lili.modules.sms.entity.enums.SmsEnum; +import cn.lili.modules.sms.plugin.impl.AliSmsPlugin; +import cn.lili.modules.sms.plugin.impl.HuaweiSmsPlugin; +import cn.lili.modules.sms.plugin.impl.TencentSmsPlugin; +import cn.lili.modules.system.entity.dos.Setting; +import cn.lili.modules.system.entity.dto.SmsSetting; +import cn.lili.modules.system.entity.enums.SettingEnum; +import cn.lili.modules.system.service.SettingService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +/** + * 短信服务抽象工厂 直接返回操作类 + * + * @author Bulbasaur + * @version v1.0 + * @since 2023-02-16 + */ +@Component +public class SmsPluginFactory { + + @Autowired + private SettingService settingService; + + + /** + * 获取oss client + * + * @return + */ + public SmsPlugin smsPlugin() { + + SmsSetting smsSetting = null; + try { + Setting setting = settingService.get(SettingEnum.SMS_SETTING.name()); + + smsSetting = JSONUtil.toBean(setting.getSettingValue(), SmsSetting.class); + + + switch (SmsEnum.valueOf(smsSetting.getType())) { + + case ALI: + return new AliSmsPlugin(smsSetting); + case TENCENT: + return new TencentSmsPlugin(smsSetting); + case HUAWEI: + return new HuaweiSmsPlugin(smsSetting); + default: + throw new ServiceException(); + } + } catch (Exception e) { + throw new ServiceException(); + } + } +} diff --git a/framework/src/main/java/cn/lili/modules/sms/plugin/impl/AliSmsPlugin.java b/framework/src/main/java/cn/lili/modules/sms/plugin/impl/AliSmsPlugin.java new file mode 100644 index 00000000..e8d7b1aa --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/sms/plugin/impl/AliSmsPlugin.java @@ -0,0 +1,265 @@ +package cn.lili.modules.sms.plugin.impl; + +import cn.hutool.json.JSONUtil; +import cn.lili.common.enums.ResultCode; +import cn.lili.common.exception.ServiceException; +import cn.lili.common.utils.Base64Utils; +import cn.lili.modules.sms.entity.dos.SmsSign; +import cn.lili.modules.sms.entity.dos.SmsTemplate; +import cn.lili.modules.sms.entity.enums.SmsEnum; +import cn.lili.modules.sms.plugin.SmsPlugin; +import cn.lili.modules.system.entity.dto.SmsSetting; +import com.aliyun.dysmsapi20170525.models.*; +import com.aliyun.teaopenapi.models.Config; +import lombok.extern.slf4j.Slf4j; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * 阿里云短信插件 + * + * @author Bulbasaur + * @since 2023-02-16 + */ +@Slf4j +public class AliSmsPlugin implements SmsPlugin { + + private SmsSetting smsSetting; + + public AliSmsPlugin(SmsSetting smsSetting) { + this.smsSetting = smsSetting; + } + + @Override + public SmsEnum pluginName() { + return SmsEnum.ALI; + } + + @Override + public void sendSmsCode(String signName, String mobile, Map param, String templateCode) { + com.aliyun.dysmsapi20170525.Client client = this.createClient(); + SendSmsRequest sendSmsRequest = new SendSmsRequest() + .setSignName(signName) + .setPhoneNumbers(mobile) + .setTemplateCode(templateCode) + .setTemplateParam(JSONUtil.toJsonStr(param)); + try { + SendSmsResponse response = client.sendSms(sendSmsRequest); + if (!("OK").equals(response.getBody().getCode())) { + throw new ServiceException(response.getBody().getMessage()); + } + } catch (Exception e) { + log.error("发送短信错误", e); + } + } + + + @Override + public void sendBatchSms(String signName, List mobile, String templateCode) { + + com.aliyun.dysmsapi20170525.Client client = this.createClient(); + + List sign = new ArrayList(); + + sign.addAll(mobile); + sign.replaceAll(e -> signName); + + //手机号拆成多个小组进行发送 + List> mobileList = new ArrayList<>(); + + //签名名称多个小组 + List> signNameList = new ArrayList<>(); + + //循环分组 + for (int i = 0; i < (mobile.size() / 100 + (mobile.size() % 100 == 0 ? 0 : 1)); i++) { + int endPoint = Math.min((100 + (i * 100)), mobile.size()); + mobileList.add(mobile.subList((i * 100), endPoint)); + signNameList.add(sign.subList((i * 100), endPoint)); + } + +// //发送短信 + for (int i = 0; i < mobileList.size(); i++) { + SendBatchSmsRequest sendBatchSmsRequest = new SendBatchSmsRequest() + .setPhoneNumberJson(JSONUtil.toJsonStr(mobileList.get(i))) + .setSignNameJson(JSONUtil.toJsonStr(signNameList.get(i))) + .setTemplateCode(templateCode); + try { + client.sendBatchSms(sendBatchSmsRequest); + } catch (Exception e) { + log.error("批量发送短信错误", e); + } + } + + } + + @Override + public void addSmsSign(SmsSign smsSign) throws Exception { + //设置参数添加短信签名 + com.aliyun.dysmsapi20170525.Client client = this.createClient(); + //营业执照 + AddSmsSignRequest.AddSmsSignRequestSignFileList signFileList0 = new AddSmsSignRequest.AddSmsSignRequestSignFileList() + .setFileContents(Base64Utils.encode(smsSign.getBusinessLicense())) + .setFileSuffix(smsSign.getBusinessLicense().substring(smsSign.getBusinessLicense().lastIndexOf(".") + 1)); + //授权委托书 + AddSmsSignRequest.AddSmsSignRequestSignFileList signFileList1 = new AddSmsSignRequest.AddSmsSignRequestSignFileList() + .setFileContents(Base64Utils.encode(smsSign.getLicense())) + .setFileSuffix(smsSign.getLicense().substring(smsSign.getLicense().lastIndexOf(".")) + 1); + //添加短信签名 + AddSmsSignRequest addSmsSignRequest = new AddSmsSignRequest() + .setSignName(smsSign.getSignName()) + .setSignSource(smsSign.getSignSource()) + .setRemark(smsSign.getRemark()) + .setSignFileList(java.util.Arrays.asList( + signFileList0, + signFileList1 + )); + AddSmsSignResponse response = client.addSmsSign(addSmsSignRequest); + if (!("OK").equals(response.getBody().getCode())) { + throw new ServiceException(response.getBody().getMessage()); + } + } + + @Override + public void deleteSmsSign(String signName) throws Exception { + com.aliyun.dysmsapi20170525.Client client = this.createClient(); + DeleteSmsSignRequest deleteSmsSignRequest = new DeleteSmsSignRequest() + .setSignName(signName); + + DeleteSmsSignResponse response = client.deleteSmsSign(deleteSmsSignRequest); + if (!("OK").equals(response.getBody().getCode())) { + throw new ServiceException(response.getBody().getMessage()); + } + + } + + @Override + public Map querySmsSign(String signName) throws Exception { + //设置参数查看短信签名 + com.aliyun.dysmsapi20170525.Client client = this.createClient(); + QuerySmsSignRequest querySmsSignRequest = new QuerySmsSignRequest().setSignName(signName); + + QuerySmsSignResponse response = client.querySmsSign(querySmsSignRequest); + if (!("OK").equals(response.getBody().getCode())) { + throw new ServiceException(response.getBody().getMessage()); + } + Map map = new HashMap<>(2); + map.put("SignStatus", response.getBody().getSignStatus()); + map.put("Reason", response.getBody().getReason()); + return map; + } + + @Override + public void modifySmsSign(SmsSign smsSign) throws Exception { + //设置参数添加短信签名 + com.aliyun.dysmsapi20170525.Client client = this.createClient(); + + ModifySmsSignRequest.ModifySmsSignRequestSignFileList signFileList0 = new ModifySmsSignRequest.ModifySmsSignRequestSignFileList() + .setFileContents(Base64Utils.encode(smsSign.getBusinessLicense())) + .setFileSuffix(smsSign.getBusinessLicense().substring(smsSign.getBusinessLicense().lastIndexOf(".") + 1)); + ModifySmsSignRequest.ModifySmsSignRequestSignFileList signFileList1 = new ModifySmsSignRequest.ModifySmsSignRequestSignFileList() + .setFileContents(Base64Utils.encode(smsSign.getLicense())) + .setFileSuffix(smsSign.getLicense().substring(smsSign.getBusinessLicense().lastIndexOf(".") + 1)); + ModifySmsSignRequest modifySmsSign = new ModifySmsSignRequest() + .setSignName(smsSign.getSignName()) + .setSignSource(smsSign.getSignSource()) + .setRemark(smsSign.getRemark()) + .setSignFileList(java.util.Arrays.asList( + signFileList0, + signFileList1 + )); + ModifySmsSignResponse response = client.modifySmsSign(modifySmsSign); + if (!("OK").equals(response.getBody().getCode())) { + throw new ServiceException(response.getBody().getMessage()); + } + } + + @Override + public void modifySmsTemplate(SmsTemplate smsTemplate) throws Exception { + com.aliyun.dysmsapi20170525.Client client = this.createClient(); + ModifySmsTemplateRequest modifySmsTemplateRequest = new ModifySmsTemplateRequest() + .setTemplateType(smsTemplate.getTemplateType()) + .setTemplateName(smsTemplate.getTemplateName()) + .setTemplateContent(smsTemplate.getTemplateContent()) + .setRemark(smsTemplate.getRemark()) + .setTemplateCode(smsTemplate.getTemplateCode()); + + ModifySmsTemplateResponse response = client.modifySmsTemplate(modifySmsTemplateRequest); + if (!("OK").equals(response.getBody().getCode())) { + throw new ServiceException(response.getBody().getMessage()); + } + } + + @Override + public Map querySmsTemplate(String templateCode) throws Exception { + com.aliyun.dysmsapi20170525.Client client = this.createClient(); + QuerySmsTemplateRequest querySmsTemplateRequest = new QuerySmsTemplateRequest() + .setTemplateCode(templateCode); + QuerySmsTemplateResponse response = client.querySmsTemplate(querySmsTemplateRequest); + + if (!("OK").equals(response.getBody().getCode())) { + throw new ServiceException(response.getBody().getMessage()); + } + Map map = new HashMap<>(4); + map.put("TemplateStatus", response.getBody().getTemplateStatus()); + map.put("Reason", response.getBody().getReason()); + map.put("TemplateCode", response.getBody().getTemplateCode()); + return map; + } + + @Override + public String addSmsTemplate(SmsTemplate smsTemplate) throws Exception { + com.aliyun.dysmsapi20170525.Client client = this.createClient(); + AddSmsTemplateRequest addSmsTemplateRequest = new AddSmsTemplateRequest() + .setTemplateType(1) + .setTemplateName(smsTemplate.getTemplateName()) + .setTemplateContent(smsTemplate.getTemplateContent()) + .setRemark(smsTemplate.getRemark()); + + AddSmsTemplateResponse response = client.addSmsTemplate(addSmsTemplateRequest); + if (!("OK").equals(response.getBody().getCode())) { + throw new ServiceException(response.getBody().getMessage()); + } + return response.getBody().getTemplateCode(); + } + + @Override + public void deleteSmsTemplate(String templateCode) throws Exception { + com.aliyun.dysmsapi20170525.Client client = this.createClient(); + DeleteSmsTemplateRequest deleteSmsTemplateRequest = new DeleteSmsTemplateRequest() + .setTemplateCode(templateCode); + + DeleteSmsTemplateResponse response = client.deleteSmsTemplate(deleteSmsTemplateRequest); + if (!("OK").equals(response.getBody().getCode())) { + throw new ServiceException(response.getBody().getMessage()); + } + } + + + /** + * 初始化账号Client + * + * @return Client 短信操作 + */ + public com.aliyun.dysmsapi20170525.Client createClient() { + try { + if (smsSetting == null) { + throw new ServiceException(ResultCode.ALI_SMS_SETTING_ERROR); + } + Config config = new Config(); + //您的AccessKey ID + config.accessKeyId = smsSetting.getAccessKeyId(); + //您的AccessKey Secret + config.accessKeySecret = smsSetting.getAccessSecret(); + //访问的域名 + config.endpoint = "dysmsapi.aliyuncs.com"; + return new com.aliyun.dysmsapi20170525.Client(config); + } catch (Exception e) { + log.error("短信初始化错误", e); + } + return null; + } + +} diff --git a/framework/src/main/java/cn/lili/modules/sms/plugin/impl/HuaweiSmsPlugin.java b/framework/src/main/java/cn/lili/modules/sms/plugin/impl/HuaweiSmsPlugin.java new file mode 100644 index 00000000..fa6818c5 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/sms/plugin/impl/HuaweiSmsPlugin.java @@ -0,0 +1,323 @@ +package cn.lili.modules.sms.plugin.impl; + +import cn.lili.modules.sms.entity.dos.SmsSign; +import cn.lili.modules.sms.entity.dos.SmsTemplate; +import cn.lili.modules.sms.entity.enums.SmsEnum; +import cn.lili.modules.sms.plugin.SmsPlugin; +import cn.lili.modules.system.entity.dto.SmsSetting; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; + +import javax.net.ssl.*; +import java.io.*; +import java.net.URL; +import java.net.URLEncoder; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; +import java.text.SimpleDateFormat; +import java.util.*; + +/** + * 腾讯云短信插件 + * + * @author Bulbasaur + * @since 2023-02-16 + */ +@Slf4j +public class HuaweiSmsPlugin implements SmsPlugin { + + private SmsSetting smsSetting; + + public HuaweiSmsPlugin(SmsSetting smsSetting) { + this.smsSetting = smsSetting; + } + + @Override + public SmsEnum pluginName() { + return SmsEnum.HUAWEI; + } + + //无需修改,用于格式化鉴权头域,给"X-WSSE"参数赋值 + private static final String WSSE_HEADER_FORMAT = "UsernameToken Username=\"%s\",PasswordDigest=\"%s\",Nonce=\"%s\",Created=\"%s\""; + //无需修改,用于格式化鉴权头域,给"Authorization"参数赋值 + private static final String AUTH_HEADER_VALUE = "WSSE realm=\"SDP\",profile=\"UsernameToken\",type=\"Appkey\""; + + + @Override + public void sendSmsCode(String signName, String mobile, Map param, String templateCode) { + try { + this.sendSms(signName, mobile, "[" + param.values() + "]", templateCode); + } catch (Exception e) { + e.printStackTrace(); + } + + } + + @Override + public void sendBatchSms(String signName, List mobile, String templateCode) { + try { + this.sendSms(signName, StringUtils.join(mobile, ","), null, templateCode); + } catch (Exception e) { + e.printStackTrace(); + } + } + + @Override + public void addSmsSign(SmsSign smsSign) throws Exception { + + } + + @Override + public void deleteSmsSign(String signName) throws Exception { + + } + + @Override + public Map querySmsSign(String signName) throws Exception { + return null; + } + + @Override + public void modifySmsSign(SmsSign smsSign) throws Exception { + + } + + @Override + public void modifySmsTemplate(SmsTemplate smsTemplate) throws Exception { + + } + + @Override + public Map querySmsTemplate(String templateCode) throws Exception { + return null; + } + + @Override + public String addSmsTemplate(SmsTemplate smsTemplate) throws Exception { + return null; + } + + @Override + public void deleteSmsTemplate(String templateCode) throws Exception { + + } + + + private void sendSms(String signName, String mobile, String param, String templateCode) throws Exception { + //必填,请参考"开发准备"获取如下数据,替换为实际值 + String url = "https://smsapi.cn-north-4.myhuaweicloud.com:443/sms/batchSendSms/v1"; //APP接入地址(在控制台"应用管理"页面获取)+接口访问URI + String appKey = smsSetting.getHuaweiAppKey(); //APP_Key + String appSecret = smsSetting.getHuaweiAppSecret(); //APP_Secret + String sender = smsSetting.getHuaweiSender(); //国内短信签名通道号或国际/港澳台短信通道号 + String templateId = templateCode; //模板ID + + //条件必填,国内短信关注,当templateId指定的模板类型为通用模板时生效且必填,必须是已审核通过的,与模板类型一致的签名名称 + //国际/港澳台短信不用关注该参数 + String signature = signName; //签名名称 + + //必填,全局号码格式(包含国家码),示例:+8615123456789,多个号码之间用英文逗号分隔 + String receiver = mobile; //短信接收人号码 + + //选填,短信状态报告接收地址,推荐使用域名,为空或者不填表示不接收状态报告 + String statusCallBack = ""; + + /** + * 选填,使用无变量模板时请赋空值 String templateParas = ""; + * 单变量模板示例:模板内容为"您的验证码是${1}"时,templateParas可填写为"[\"369751\"]" + * 双变量模板示例:模板内容为"您有${1}件快递请到${2}领取"时,templateParas可填写为"[\"3\",\"人民公园正门\"]" + * 模板中的每个变量都必须赋值,且取值不能为空 + * 查看更多模板和变量规范:产品介绍>模板和变量规范 + */ + String templateParas = param; //模板变量,此处以单变量验证码短信为例,请客户自行生成6位验证码,并定义为字符串类型,以杜绝首位0丢失的问题(例如:002569变成了2569)。 + + //请求Body,不携带签名名称时,signature请填null + String body = buildRequestBody(sender, receiver, templateId, templateParas, statusCallBack, signature); + if (null == body || body.isEmpty()) { + System.out.println("body is null."); + return; + } + + //请求Headers中的X-WSSE参数值 + String wsseHeader = buildWsseHeader(appKey, appSecret); + if (null == wsseHeader || wsseHeader.isEmpty()) { + System.out.println("wsse header is null."); + return; + } + + Writer out = null; + BufferedReader in = null; + StringBuffer result = new StringBuffer(); + HttpsURLConnection connection = null; + InputStream is = null; + + + HostnameVerifier hv = new HostnameVerifier() { + + @Override + public boolean verify(String hostname, SSLSession session) { + return true; + } + }; + trustAllHttpsCertificates(); + + try { + URL realUrl = new URL(url); + connection = (HttpsURLConnection) realUrl.openConnection(); + + connection.setHostnameVerifier(hv); + connection.setDoOutput(true); + connection.setDoInput(true); + connection.setUseCaches(true); + //请求方法 + connection.setRequestMethod("POST"); + //请求Headers参数 + connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded"); + connection.setRequestProperty("Authorization", AUTH_HEADER_VALUE); + connection.setRequestProperty("X-WSSE", wsseHeader); + + connection.connect(); + out = new OutputStreamWriter(connection.getOutputStream()); + out.write(body); //发送请求Body参数 + out.flush(); + out.close(); + + int status = connection.getResponseCode(); + if (200 == status) { //200 + is = connection.getInputStream(); + } else { //400/401 + is = connection.getErrorStream(); + } + in = new BufferedReader(new InputStreamReader(is, "UTF-8")); + String line = ""; + while ((line = in.readLine()) != null) { + result.append(line); + } + System.out.println(result.toString()); //打印响应消息实体 + } catch (Exception e) { + e.printStackTrace(); + } finally { + try { + if (null != out) { + out.close(); + } + if (null != is) { + is.close(); + } + if (null != in) { + in.close(); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + } + + /** + * 构造请求Body体 + * + * @param sender + * @param receiver + * @param templateId + * @param templateParas + * @param statusCallBack + * @param signature | 签名名称,使用国内短信通用模板时填写 + * @return + */ + static String buildRequestBody(String sender, String receiver, String templateId, String templateParas, + String statusCallBack, String signature) { + if (null == sender || null == receiver || null == templateId || sender.isEmpty() || receiver.isEmpty() + || templateId.isEmpty()) { + System.out.println("buildRequestBody(): sender, receiver or templateId is null."); + return null; + } + Map map = new HashMap(); + + map.put("from", sender); + map.put("to", receiver); + map.put("templateId", templateId); + if (null != templateParas && !templateParas.isEmpty()) { + map.put("templateParas", templateParas); + } + if (null != statusCallBack && !statusCallBack.isEmpty()) { + map.put("statusCallback", statusCallBack); + } + if (null != signature && !signature.isEmpty()) { + map.put("signature", signature); + } + + StringBuilder sb = new StringBuilder(); + String temp = ""; + + for (String s : map.keySet()) { + try { + temp = URLEncoder.encode(map.get(s), "UTF-8"); + } catch (UnsupportedEncodingException e) { + e.printStackTrace(); + } + sb.append(s).append("=").append(temp).append("&"); + } + + return sb.deleteCharAt(sb.length() - 1).toString(); + } + + /** + * 构造X-WSSE参数值 + * + * @param appKey + * @param appSecret + * @return + */ + static String buildWsseHeader(String appKey, String appSecret) { + if (null == appKey || null == appSecret || appKey.isEmpty() || appSecret.isEmpty()) { + System.out.println("buildWsseHeader(): appKey or appSecret is null."); + return null; + } + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'"); + String time = sdf.format(new Date()); //Created + String nonce = UUID.randomUUID().toString().replace("-", ""); //Nonce + + MessageDigest md; + byte[] passwordDigest = null; + + try { + md = MessageDigest.getInstance("SHA-256"); + md.update((nonce + time + appSecret).getBytes()); + passwordDigest = md.digest(); + } catch (NoSuchAlgorithmException e) { + e.printStackTrace(); + } + + //如果JDK版本是1.8,请加载原生Base64类,并使用如下代码 + String passwordDigestBase64Str = Base64.getEncoder().encodeToString(passwordDigest); //PasswordDigest + //如果JDK版本低于1.8,请加载三方库提供Base64类,并使用如下代码 + //String passwordDigestBase64Str = Base64.encodeBase64String(passwordDigest); //PasswordDigest + //若passwordDigestBase64Str中包含换行符,请执行如下代码进行修正 + //passwordDigestBase64Str = passwordDigestBase64Str.replaceAll("[\\s*\t\n\r]", ""); + return String.format(WSSE_HEADER_FORMAT, appKey, passwordDigestBase64Str, nonce, time); + } + + /*** @throws Exception + */ + static void trustAllHttpsCertificates() throws Exception { + TrustManager[] trustAllCerts = new TrustManager[]{ + new X509TrustManager() { + public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { + return; + } + + public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { + return; + } + + public X509Certificate[] getAcceptedIssuers() { + return null; + } + } + }; + SSLContext sc = SSLContext.getInstance("SSL"); + sc.init(null, trustAllCerts, null); + HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory()); + } +} diff --git a/framework/src/main/java/cn/lili/modules/sms/plugin/impl/TencentSmsPlugin.java b/framework/src/main/java/cn/lili/modules/sms/plugin/impl/TencentSmsPlugin.java new file mode 100644 index 00000000..dae2cba0 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/sms/plugin/impl/TencentSmsPlugin.java @@ -0,0 +1,254 @@ +package cn.lili.modules.sms.plugin.impl; + +import cn.hutool.core.convert.Convert; +import cn.lili.modules.sms.entity.dos.SmsSign; +import cn.lili.modules.sms.entity.dos.SmsTemplate; +import cn.lili.modules.sms.entity.enums.SmsEnum; +import cn.lili.modules.sms.plugin.SmsPlugin; +import cn.lili.modules.system.entity.dto.SmsSetting; +import com.tencentcloudapi.common.Credential; +import com.tencentcloudapi.common.exception.TencentCloudSDKException; +import com.tencentcloudapi.common.profile.HttpProfile; +import com.tencentcloudapi.sms.v20210111.SmsClient; +import com.tencentcloudapi.sms.v20210111.models.SendSmsRequest; +import com.tencentcloudapi.sms.v20210111.models.SendSmsResponse; +import lombok.extern.slf4j.Slf4j; + +import java.util.List; +import java.util.Map; + +/** + * 腾讯云短信插件 + * + * @author Bulbasaur + * @since 2023-02-16 + */ +@Slf4j +public class TencentSmsPlugin implements SmsPlugin { + + private SmsSetting smsSetting; + + public TencentSmsPlugin(SmsSetting smsSetting) { + this.smsSetting = smsSetting; + } + + @Override + public SmsEnum pluginName() { + return SmsEnum.TENCENT; + } + + + @Override + public void sendSmsCode(String signName, String mobile, Map param, String templateCode) { + try { + /* 实例化一个请求对象,根据调用的接口和实际情况,可以进一步设置请求参数 + * 你可以直接查询SDK源码确定接口有哪些属性可以设置 + * 属性可能是基本类型,也可能引用了另一个数据结构 + * 推荐使用IDE进行开发,可以方便的跳转查阅各个接口和数据结构的文档说明 */ + SendSmsRequest req = new SendSmsRequest(); + + /* 填充请求参数,这里request对象的成员变量即对应接口的入参 + * 你可以通过官网接口文档或跳转到request对象的定义处查看请求参数的定义 + * 基本类型的设置: + * 帮助链接: + * 短信控制台: https://console.cloud.tencent.com/smsv2 + * 腾讯云短信小助手: https://cloud.tencent.com/document/product/382/3773#.E6.8A.80.E6.9C.AF.E4.BA.A4.E6.B5.81 */ + + /* 短信应用ID: 短信SdkAppId在 [短信控制台] 添加应用后生成的实际SdkAppId,示例如1400006666 */ + // 应用 ID 可前往 [短信控制台](https://console.cloud.tencent.com/smsv2/app-manage) 查看 + req.setSmsSdkAppId(smsSetting.getTencentSdkAppId()); + + /* 短信签名内容: 使用 UTF-8 编码,必须填写已审核通过的签名 */ + // 签名信息可前往 [国内短信](https://console.cloud.tencent.com/smsv2/csms-sign) 或 [国际/港澳台短信](https://console.cloud.tencent.com/smsv2/isms-sign) 的签名管理查看 + req.setSignName(smsSetting.getTencentSignName()); + + /* 模板 ID: 必须填写已审核通过的模板 ID */ + // 模板 ID 可前往 [国内短信](https://console.cloud.tencent.com/smsv2/csms-template) 或 [国际/港澳台短信](https://console.cloud.tencent.com/smsv2/isms-template) 的正文模板管理查看 + req.setTemplateId(templateCode); + + /* 模板参数: 模板参数的个数需要与 TemplateId 对应模板的变量个数保持一致,若无模板参数,则设置为空 */ + req.setTemplateParamSet(param.values().toArray(new String[0])); + + /* 下发手机号码,采用 E.164 标准,+[国家或地区码][手机号] + * 示例如:+8613711112222, 其中前面有一个+号 ,86为国家码,13711112222为手机号,最多不要超过200个手机号 */ + String[] phoneNumberSet = {"+86" + mobile}; + req.setPhoneNumberSet(phoneNumberSet); + + /* 用户的 session 内容(无需要可忽略): 可以携带用户侧 ID 等上下文信息,server 会原样返回 */ + String sessionContext = ""; + req.setSessionContext(sessionContext); + + /* 短信码号扩展号(无需要可忽略): 默认未开通,如需开通请联系 [腾讯云短信小助手] */ + String extendCode = ""; + req.setExtendCode(extendCode); + + /* 国际/港澳台短信 SenderId(无需要可忽略): 国内短信填空,默认未开通,如需开通请联系 [腾讯云短信小助手] */ + String senderid = ""; + req.setSenderId(senderid); + + /* 通过 client 对象调用 SendSms 方法发起请求。注意请求方法名与请求对象是对应的 + * 返回的 res 是一个 SendSmsResponse 类的实例,与请求对象对应 */ + SendSmsResponse res = getClient().SendSms(req); + + // 输出json格式的字符串回包 + System.out.println(SendSmsResponse.toJsonString(res)); + + // 也可以取出单个值,你可以通过官网接口文档或跳转到response对象的定义处查看返回字段的定义 + // System.out.println(res.getRequestId()); + + /* 当出现以下错误码时,快速解决方案参考 + * [FailedOperation.SignatureIncorrectOrUnapproved](https://cloud.tencent.com/document/product/382/9558#.E7.9F.AD.E4.BF.A1.E5.8F.91.E9.80.81.E6.8F.90.E7.A4.BA.EF.BC.9Afailedoperation.signatureincorrectorunapproved-.E5.A6.82.E4.BD.95.E5.A4.84.E7.90.86.EF.BC.9F) + * [FailedOperation.TemplateIncorrectOrUnapproved](https://cloud.tencent.com/document/product/382/9558#.E7.9F.AD.E4.BF.A1.E5.8F.91.E9.80.81.E6.8F.90.E7.A4.BA.EF.BC.9Afailedoperation.templateincorrectorunapproved-.E5.A6.82.E4.BD.95.E5.A4.84.E7.90.86.EF.BC.9F) + * [UnauthorizedOperation.SmsSdkAppIdVerifyFail](https://cloud.tencent.com/document/product/382/9558#.E7.9F.AD.E4.BF.A1.E5.8F.91.E9.80.81.E6.8F.90.E7.A4.BA.EF.BC.9Aunauthorizedoperation.smssdkappidverifyfail-.E5.A6.82.E4.BD.95.E5.A4.84.E7.90.86.EF.BC.9F) + * [UnsupportedOperation.ContainDomesticAndInternationalPhoneNumber](https://cloud.tencent.com/document/product/382/9558#.E7.9F.AD.E4.BF.A1.E5.8F.91.E9.80.81.E6.8F.90.E7.A4.BA.EF.BC.9Aunsupportedoperation.containdomesticandinternationalphonenumber-.E5.A6.82.E4.BD.95.E5.A4.84.E7.90.86.EF.BC.9F) + * 更多错误,可咨询[腾讯云助手](https://tccc.qcloud.com/web/im/index.html#/chat?webAppId=8fa15978f85cb41f7e2ea36920cb3ae1&title=Sms) + */ + } catch (TencentCloudSDKException tencentCloudSDKException) { + tencentCloudSDKException.printStackTrace(); + } + + } + + @Override + public void sendBatchSms(String signName, List mobile, String templateCode) { + try { + /* 实例化一个请求对象,根据调用的接口和实际情况,可以进一步设置请求参数 + * 你可以直接查询SDK源码确定接口有哪些属性可以设置 + * 属性可能是基本类型,也可能引用了另一个数据结构 + * 推荐使用IDE进行开发,可以方便的跳转查阅各个接口和数据结构的文档说明 */ + SendSmsRequest req = new SendSmsRequest(); + + /* 填充请求参数,这里request对象的成员变量即对应接口的入参 + * 你可以通过官网接口文档或跳转到request对象的定义处查看请求参数的定义 + * 基本类型的设置: + * 帮助链接: + * 短信控制台: https://console.cloud.tencent.com/smsv2 + * 腾讯云短信小助手: https://cloud.tencent.com/document/product/382/3773#.E6.8A.80.E6.9C.AF.E4.BA.A4.E6.B5.81 */ + + /* 短信应用ID: 短信SdkAppId在 [短信控制台] 添加应用后生成的实际SdkAppId,示例如1400006666 */ + // 应用 ID 可前往 [短信控制台](https://console.cloud.tencent.com/smsv2/app-manage) 查看 + req.setSmsSdkAppId(smsSetting.getTencentSdkAppId()); + + /* 短信签名内容: 使用 UTF-8 编码,必须填写已审核通过的签名 */ + // 签名信息可前往 [国内短信](https://console.cloud.tencent.com/smsv2/csms-sign) 或 [国际/港澳台短信](https://console.cloud.tencent.com/smsv2/isms-sign) 的签名管理查看 + req.setSignName(smsSetting.getTencentSignName()); + + /* 模板 ID: 必须填写已审核通过的模板 ID */ + // 模板 ID 可前往 [国内短信](https://console.cloud.tencent.com/smsv2/csms-template) 或 [国际/港澳台短信](https://console.cloud.tencent.com/smsv2/isms-template) 的正文模板管理查看 + req.setTemplateId(templateCode); + + /* 模板参数: 模板参数的个数需要与 TemplateId 对应模板的变量个数保持一致,若无模板参数,则设置为空 */ + req.setTemplateParamSet(null); + + /* 下发手机号码,采用 E.164 标准,+[国家或地区码][手机号] + * 示例如:+8613711112222, 其中前面有一个+号 ,86为国家码,13711112222为手机号,最多不要超过200个手机号 */ + req.setPhoneNumberSet(Convert.toStrArray(mobile)); + + /* 用户的 session 内容(无需要可忽略): 可以携带用户侧 ID 等上下文信息,server 会原样返回 */ + String sessionContext = ""; + req.setSessionContext(sessionContext); + + /* 短信码号扩展号(无需要可忽略): 默认未开通,如需开通请联系 [腾讯云短信小助手] */ + String extendCode = ""; + req.setExtendCode(extendCode); + + /* 国际/港澳台短信 SenderId(无需要可忽略): 国内短信填空,默认未开通,如需开通请联系 [腾讯云短信小助手] */ + String senderid = ""; + req.setSenderId(senderid); + + /* 通过 client 对象调用 SendSms 方法发起请求。注意请求方法名与请求对象是对应的 + * 返回的 res 是一个 SendSmsResponse 类的实例,与请求对象对应 */ + SendSmsResponse res = getClient().SendSms(req); + + // 输出json格式的字符串回包 + System.out.println(SendSmsResponse.toJsonString(res)); + + // 也可以取出单个值,你可以通过官网接口文档或跳转到response对象的定义处查看返回字段的定义 + // System.out.println(res.getRequestId()); + + /* 当出现以下错误码时,快速解决方案参考 + * [FailedOperation.SignatureIncorrectOrUnapproved](https://cloud.tencent.com/document/product/382/9558#.E7.9F.AD.E4.BF.A1.E5.8F.91.E9.80.81.E6.8F.90.E7.A4.BA.EF.BC.9Afailedoperation.signatureincorrectorunapproved-.E5.A6.82.E4.BD.95.E5.A4.84.E7.90.86.EF.BC.9F) + * [FailedOperation.TemplateIncorrectOrUnapproved](https://cloud.tencent.com/document/product/382/9558#.E7.9F.AD.E4.BF.A1.E5.8F.91.E9.80.81.E6.8F.90.E7.A4.BA.EF.BC.9Afailedoperation.templateincorrectorunapproved-.E5.A6.82.E4.BD.95.E5.A4.84.E7.90.86.EF.BC.9F) + * [UnauthorizedOperation.SmsSdkAppIdVerifyFail](https://cloud.tencent.com/document/product/382/9558#.E7.9F.AD.E4.BF.A1.E5.8F.91.E9.80.81.E6.8F.90.E7.A4.BA.EF.BC.9Aunauthorizedoperation.smssdkappidverifyfail-.E5.A6.82.E4.BD.95.E5.A4.84.E7.90.86.EF.BC.9F) + * [UnsupportedOperation.ContainDomesticAndInternationalPhoneNumber](https://cloud.tencent.com/document/product/382/9558#.E7.9F.AD.E4.BF.A1.E5.8F.91.E9.80.81.E6.8F.90.E7.A4.BA.EF.BC.9Aunsupportedoperation.containdomesticandinternationalphonenumber-.E5.A6.82.E4.BD.95.E5.A4.84.E7.90.86.EF.BC.9F) + * 更多错误,可咨询[腾讯云助手](https://tccc.qcloud.com/web/im/index.html#/chat?webAppId=8fa15978f85cb41f7e2ea36920cb3ae1&title=Sms) + */ + } catch (TencentCloudSDKException tencentCloudSDKException) { + tencentCloudSDKException.printStackTrace(); + } + } + + @Override + public void addSmsSign(SmsSign smsSign) throws Exception { + + } + + @Override + public void deleteSmsSign(String signName) throws Exception { + + } + + @Override + public Map querySmsSign(String signName) throws Exception { + return null; + } + + @Override + public void modifySmsSign(SmsSign smsSign) throws Exception { + + } + + @Override + public void modifySmsTemplate(SmsTemplate smsTemplate) throws Exception { + + } + + @Override + public Map querySmsTemplate(String templateCode) throws Exception { + return null; + } + + @Override + public String addSmsTemplate(SmsTemplate smsTemplate) throws Exception { + return null; + } + + @Override + public void deleteSmsTemplate(String templateCode) throws Exception { + + } + + + /** + * 获取smsclient + * + * @return + */ + private SmsClient getClient() { + /* 必要步骤: + * 实例化一个认证对象,入参需要传入腾讯云账户密钥对secretId,secretKey。 + * 这里采用的是从环境变量读取的方式,需要在环境变量中先设置这两个值。 + * 你也可以直接在代码中写死密钥对,但是小心不要将代码复制、上传或者分享给他人, + * 以免泄露密钥对危及你的财产安全。 + * SecretId、SecretKey 查询: https://console.cloud.tencent.com/cam/capi */ + Credential cred = new Credential(smsSetting.getTencentSecretId(), smsSetting.getTencentSecretKey()); + + // 实例化一个http选项,可选,没有特殊需求可以跳过 + HttpProfile httpProfile = new HttpProfile(); + // 设置代理(无需要直接忽略) + // httpProfile.setProxyHost("真实代理ip"); + // httpProfile.setProxyPort(真实代理端口); + /* SDK默认使用POST方法。 + * 如果你一定要使用GET方法,可以在这里设置。GET方法无法处理一些较大的请求 */ + httpProfile.setReqMethod("POST"); + /* SDK有默认的超时时间,非必要请不要进行调整 + * 如有需要请在代码中查阅以获取最新的默认值 */ + httpProfile.setConnTimeout(60); + /* 指定接入地域域名,默认就近地域接入域名为 sms.tencentcloudapi.com ,也支持指定地域域名访问,例如广州地域的域名为 sms.ap-guangzhou.tencentcloudapi.com */ + httpProfile.setEndpoint("sms.tencentcloudapi.com"); + + /* 实例化要请求产品(以sms为例)的client对象 + * 第二个参数是地域信息,可以直接填写字符串ap-guangzhou,支持的地域列表参考 https://cloud.tencent.com/document/api/382/52071#.E5.9C.B0.E5.9F.9F.E5.88.97.E8.A1.A8 */ + return new SmsClient(cred, "ap-guangzhou"); + } +} diff --git a/framework/src/main/java/cn/lili/modules/sms/serviceimpl/SmsSignServiceImpl.java b/framework/src/main/java/cn/lili/modules/sms/serviceimpl/SmsSignServiceImpl.java index d55c8b98..6820b3fa 100644 --- a/framework/src/main/java/cn/lili/modules/sms/serviceimpl/SmsSignServiceImpl.java +++ b/framework/src/main/java/cn/lili/modules/sms/serviceimpl/SmsSignServiceImpl.java @@ -3,9 +3,9 @@ package cn.lili.modules.sms.serviceimpl; import cn.lili.common.enums.ResultCode; import cn.lili.common.exception.ServiceException; import cn.lili.common.vo.PageVO; -import cn.lili.modules.sms.AliSmsUtil; import cn.lili.modules.sms.entity.dos.SmsSign; import cn.lili.modules.sms.mapper.SmsSignMapper; +import cn.lili.modules.sms.plugin.SmsPluginFactory; import cn.lili.modules.sms.service.SmsSignService; import cn.lili.mybatis.util.PageUtil; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; @@ -23,6 +23,7 @@ import java.util.Map; /** * 短信签名业务层实现 + * * @author Chopper * @since 2021/1/30 4:27 下午 */ @@ -30,7 +31,7 @@ import java.util.Map; @Service public class SmsSignServiceImpl extends ServiceImpl implements SmsSignService { @Autowired - private AliSmsUtil aliSmsUtil; + private SmsPluginFactory smsPluginFactory; @Override @Transactional(rollbackFor = Exception.class) @@ -40,11 +41,11 @@ public class SmsSignServiceImpl extends ServiceImpl impl if (this.getOne(new QueryWrapper().eq("sign_name", smsSign.getSignName())) != null) { throw new ServiceException(ResultCode.SMS_SIGN_EXIST_ERROR); } - aliSmsUtil.addSmsSign(smsSign); + smsPluginFactory.smsPlugin().addSmsSign(smsSign); smsSign.setSignStatus(0); this.save(smsSign); } catch (Exception e) { - log.error("添加短信签名错误",e); + log.error("添加短信签名错误", e); } } @@ -54,11 +55,11 @@ public class SmsSignServiceImpl extends ServiceImpl impl try { SmsSign smsSign = this.getById(id); if (smsSign != null) { - aliSmsUtil.deleteSmsSign(smsSign.getSignName()); + smsPluginFactory.smsPlugin().deleteSmsSign(smsSign.getSignName()); this.removeById(id); } } catch (Exception e) { - log.error("删除短信签名错误",e); + log.error("删除短信签名错误", e); } } @@ -72,13 +73,13 @@ public class SmsSignServiceImpl extends ServiceImpl impl List list = list(new LambdaQueryWrapper().ne(SmsSign::getSignStatus, 1)); //查询签名状态 for (SmsSign smsSign : list) { - map = aliSmsUtil.querySmsSign(smsSign.getSignName()); + map = smsPluginFactory.smsPlugin().querySmsSign(smsSign.getSignName()); smsSign.setSignStatus((Integer) map.get("SignStatus")); smsSign.setReason(map.get("Reason").toString()); this.updateById(smsSign); } } catch (Exception e) { - log.error("查询短信签名错误",e); + log.error("查询短信签名错误", e); } } @@ -86,10 +87,10 @@ public class SmsSignServiceImpl extends ServiceImpl impl @Transactional(rollbackFor = Exception.class) public void modifySmsSign(SmsSign smsSign) { try { - aliSmsUtil.modifySmsSign(smsSign); + smsPluginFactory.smsPlugin().modifySmsSign(smsSign); this.updateById(smsSign); } catch (Exception e) { - log.error("更新短信签名错误",e); + log.error("更新短信签名错误", e); } } diff --git a/framework/src/main/java/cn/lili/modules/sms/serviceimpl/SmsTemplateServiceImpl.java b/framework/src/main/java/cn/lili/modules/sms/serviceimpl/SmsTemplateServiceImpl.java index b23a4990..8f813b5a 100644 --- a/framework/src/main/java/cn/lili/modules/sms/serviceimpl/SmsTemplateServiceImpl.java +++ b/framework/src/main/java/cn/lili/modules/sms/serviceimpl/SmsTemplateServiceImpl.java @@ -1,9 +1,9 @@ package cn.lili.modules.sms.serviceimpl; import cn.lili.common.vo.PageVO; -import cn.lili.modules.sms.AliSmsUtil; import cn.lili.modules.sms.entity.dos.SmsTemplate; import cn.lili.modules.sms.mapper.SmsTemplateMapper; +import cn.lili.modules.sms.plugin.SmsPluginFactory; import cn.lili.modules.sms.service.SmsTemplateService; import cn.lili.mybatis.util.PageUtil; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; @@ -19,25 +19,27 @@ import java.util.Map; /** * 短信模板业务层实现 + * * @author Chopper * @since 2021/1/30 4:27 下午 */ @Slf4j @Service public class SmsTemplateServiceImpl extends ServiceImpl implements SmsTemplateService { + @Autowired - private AliSmsUtil aliSmsUtil; + private SmsPluginFactory smsPluginFactory; @Override public void addSmsTemplate(SmsTemplate smsTemplate) { try { - smsTemplate.setTemplateCode(aliSmsUtil.addSmsTemplate(smsTemplate)); + smsTemplate.setTemplateCode(smsPluginFactory.smsPlugin().addSmsTemplate(smsTemplate)); smsTemplate.setTemplateStatus(0); smsTemplate.setTemplateType(1); this.save(smsTemplate); } catch (Exception e) { - log.error("添加短信模板错误",e); + log.error("添加短信模板错误", e); } } @@ -46,11 +48,11 @@ public class SmsTemplateServiceImpl extends ServiceImpl list = list(new LambdaQueryWrapper().eq(SmsTemplate::getTemplateStatus, 0)); //查询签名状态 for (SmsTemplate smsTemplate : list) { - map = aliSmsUtil.querySmsTemplate(smsTemplate.getTemplateCode()); + map = smsPluginFactory.smsPlugin().querySmsTemplate(smsTemplate.getTemplateCode()); smsTemplate.setTemplateStatus((Integer) map.get("TemplateStatus")); smsTemplate.setReason(map.get("Reason").toString()); smsTemplate.setTemplateCode(map.get("TemplateCode").toString()); this.updateById(smsTemplate); } } catch (Exception e) { - log.error("查询短信模板错误",e); + log.error("查询短信模板错误", e); } } @Override public void modifySmsTemplate(SmsTemplate smsTemplate) { try { - aliSmsUtil.modifySmsTemplate(smsTemplate); + smsPluginFactory.smsPlugin().modifySmsTemplate(smsTemplate); smsTemplate.setTemplateStatus(0); this.updateById(smsTemplate); } catch (Exception e) { - log.error("重新提交短信模板错误",e); + log.error("重新提交短信模板错误", e); } } diff --git a/framework/src/main/java/cn/lili/modules/system/entity/dto/KuaidiSetting.java b/framework/src/main/java/cn/lili/modules/system/entity/dto/KuaidiSetting.java deleted file mode 100644 index 2348871e..00000000 --- a/framework/src/main/java/cn/lili/modules/system/entity/dto/KuaidiSetting.java +++ /dev/null @@ -1,33 +0,0 @@ -package cn.lili.modules.system.entity.dto; - -import lombok.Data; - -import java.io.Serializable; - -/** - * 快递设置 - * - * @author Chopper - * @since 2020-03-10 10:04 上午 - */ -@Data -public class KuaidiSetting implements Serializable { - private static final long serialVersionUID = 3520379500723173689L; - /** - * 企业id - */ - private String ebusinessID; - /** - * 密钥 - */ - private String appKey; - /** - * api地址 - */ - private String reqURL; - - /** - * 电子面单api地址 - */ - private String sheetReqURL; -} diff --git a/framework/src/main/java/cn/lili/modules/system/entity/dto/LogisticsSetting.java b/framework/src/main/java/cn/lili/modules/system/entity/dto/LogisticsSetting.java new file mode 100644 index 00000000..3eb5a893 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/system/entity/dto/LogisticsSetting.java @@ -0,0 +1,67 @@ +package cn.lili.modules.system.entity.dto; + +import io.swagger.annotations.ApiModelProperty; +import cn.lili.modules.logistics.entity.enums.LogisticsEnum; +import lombok.Data; + +import java.io.Serializable; + +/** + * 快递设置 + * + * @author Chopper + * @since 2020-03-10 10:04 上午 + */ +@Data +public class LogisticsSetting implements Serializable { + private static final long serialVersionUID = 3520379500723173689L; + + /** + * 快递查询类型 + * @see LogisticsEnum + */ + private String type; + + /** + * 企业id + */ + private String kdniaoEbusinessID; + /** + * 密钥 + */ + private String kdniaoAppKey; + + /** + * 快递100 授权码,请申请企业版获取 + */ + private String kuaidi100Customer; + /** + * 快递100 Key + */ + private String kuaidi100Key; + + /** + * 顺丰顾客编码 + */ + String clientCode; + + /** + * 顺丰校验码 + */ + String checkWord; + + /** + * 顺丰请求地址 + */ + String callUrl; + + /** + * 顺丰打印模板 + */ + String templateCode; + + /** + * 顺丰月结号 + */ + String monthlyCardNo; +} diff --git a/framework/src/main/java/cn/lili/modules/system/entity/dto/OssSetting.java b/framework/src/main/java/cn/lili/modules/system/entity/dto/OssSetting.java index 760c191c..95c225ba 100644 --- a/framework/src/main/java/cn/lili/modules/system/entity/dto/OssSetting.java +++ b/framework/src/main/java/cn/lili/modules/system/entity/dto/OssSetting.java @@ -24,25 +24,25 @@ public class OssSetting implements Serializable { private String type; /** - * 域名 + * 阿里云-域名 */ - private String endPoint = ""; + private String aliyunOSSEndPoint = ""; /** - * 储存空间 + * 阿里云-储存空间 */ - private String bucketName = ""; + private String aliyunOSSBucketName = ""; /** - * 存放路径路径 + * 阿里云-存放路径路径 */ - private String picLocation = ""; + private String aliyunOSSPicLocation = ""; /** - * 密钥id + * 阿里云-密钥id */ - private String accessKeyId = ""; + private String aliyunOSSAccessKeyId = ""; /** - * 密钥 + * 阿里云-密钥 */ - private String accessKeySecret = ""; + private String aliyunOSSAccessKeySecret = ""; /** @@ -71,6 +71,48 @@ public class OssSetting implements Serializable { private String m_bucketName; + /** + * 华为云-发起者的Access Key + * + * @return + */ + + String huaweicloudOBSAccessKey; + /** + * 华为云-密钥 + */ + String huaweicloudOBSSecretKey; + /** + * 华为云OBS-节点 + */ + String huaweicloudOBSEndPoint; + + /** + * 华为云OBS-桶 + */ + private String huaweicloudOBSBucketName = ""; + + /** + * 腾讯云 用户的 SecretId + */ + String tencentCOSSecretId; + /** + * 腾讯云 用户的 SecretKey + */ + String tencentCOSSecretKey; + /** + * 腾讯云 bucket 的地域 + */ + String tencentCOSRegion; + /** + * 腾讯云 bucket + */ + String tencentCOSBucket; + /** + * 腾讯云-域名 + */ + private String tencentCOSEndPoint = ""; + public String getType() { //默认给阿里云oss存储类型 if (StringUtils.isEmpty(type)) { diff --git a/framework/src/main/java/cn/lili/modules/system/entity/dto/SmsSetting.java b/framework/src/main/java/cn/lili/modules/system/entity/dto/SmsSetting.java index 683a0663..f72589ca 100644 --- a/framework/src/main/java/cn/lili/modules/system/entity/dto/SmsSetting.java +++ b/framework/src/main/java/cn/lili/modules/system/entity/dto/SmsSetting.java @@ -7,23 +7,60 @@ import java.io.Serializable; /** * 短信配置 * 这里在前台不做调整,方便客户直接把服务商的内容配置在我们平台 + * * @author Chopper * @since 2020/11/30 15:23 */ @Data public class SmsSetting implements Serializable { + + /** + * 类型 + */ + private String type; + /** - * 从上到下yi依次是 - * 节点地址 * key + */ + private String accessKeyId; + /** * 密钥 + */ + private String accessSecret; + /** * 签名 */ - private String regionId; - - private String accessKeyId; - - private String accessSecret; - private String signName; + + + /** + * 腾讯云 用户的 SecretId + */ + String tencentSecretId; + /** + * 腾讯云 用户的 SecretKey + */ + String tencentSecretKey; + /* 短信应用ID: 短信SdkAppId在 [短信控制台] 添加应用后生成的实际SdkAppId,示例如1400006666 */ + String tencentSdkAppId; + /* 短信签名内容: 使用 UTF-8 编码,必须填写已审核通过的签名 */ + String tencentSignName; + + /** + * 华为 APP_Key + */ + String huaweiAppKey; + /** + * 华为 APP_Secret + */ + String huaweiAppSecret; + + /** + * 国内短信签名通道号或国际/港澳台短信通道号 + */ + String huaweiSender; + /** + * 签名名称 + */ + String huaweiSignature; } diff --git a/framework/src/main/java/cn/lili/modules/system/entity/dto/payment/UnionPaymentSetting.java b/framework/src/main/java/cn/lili/modules/system/entity/dto/payment/UnionPaymentSetting.java new file mode 100644 index 00000000..ba3e47b2 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/system/entity/dto/payment/UnionPaymentSetting.java @@ -0,0 +1,35 @@ +package cn.lili.modules.system.entity.dto.payment; + +import lombok.Data; +import lombok.experimental.Accessors; +/** + * 银联-云闪付支付设置 + * + * @author Bulbasaur + * @since 2023-02-17 + */ +@Data +@Accessors(chain = true) +public class UnionPaymentSetting { + + /** + * 商户号 + */ + private String unionPayMachId; + /** + * 密钥 + */ + private String unionPayKey; + /** + * 请求地址 + */ + private String unionPayServerUrl; + /** + * 交易请求地址 + */ + private String unionPayDomain; + /** + * 应用ID + */ + private String unionPayAppId; +} diff --git a/framework/src/main/java/cn/lili/modules/system/entity/enums/SettingEnum.java b/framework/src/main/java/cn/lili/modules/system/entity/enums/SettingEnum.java index 00d7b6cf..0ec08b9e 100644 --- a/framework/src/main/java/cn/lili/modules/system/entity/enums/SettingEnum.java +++ b/framework/src/main/java/cn/lili/modules/system/entity/enums/SettingEnum.java @@ -17,8 +17,8 @@ public enum SettingEnum { EMAIL_SETTING, //商品设置 GOODS_SETTING, - //快递鸟设置 - KUAIDI_SETTING, + //快递设置 + LOGISTICS_SETTING, //订单配置 ORDER_SETTING, //阿里OSS配置 @@ -45,6 +45,8 @@ public enum SettingEnum { ALIPAY_PAYMENT, //微信支付设置 WECHAT_PAYMENT, + //银联支付设置 + UNIONPAY_PAYMENT, //热词设置 HOT_WORDS } diff --git a/framework/src/main/java/cn/lili/modules/system/service/LogisticsService.java b/framework/src/main/java/cn/lili/modules/system/service/LogisticsService.java index 5cdf8698..54bf557a 100644 --- a/framework/src/main/java/cn/lili/modules/system/service/LogisticsService.java +++ b/framework/src/main/java/cn/lili/modules/system/service/LogisticsService.java @@ -1,10 +1,13 @@ package cn.lili.modules.system.service; +import cn.lili.modules.order.order.entity.vo.OrderDetailVO; import cn.lili.modules.system.entity.dos.Logistics; +import cn.lili.modules.system.entity.dto.LogisticsSetting; import cn.lili.modules.system.entity.vo.Traces; import com.baomidou.mybatisplus.extension.service.IService; import java.util.List; +import java.util.Map; /** * 物流公司业务层 @@ -19,10 +22,36 @@ public interface LogisticsService extends IService { * * @param logisticsId 物流公司ID * @param logisticsNo 单号 - * @param customerName 手机号后四位 + * @param phone 手机号 * @return */ - Traces getLogistic(String logisticsId, String logisticsNo, String customerName); + Traces getLogisticTrack(String logisticsId, String logisticsNo, String phone); + + /** + * 获取物流信息 + * @param logisticsId + * @param logisticsNo + * @param phone + * @param from + * @param to + * @return + */ + Traces getLogisticMapTrack(String logisticsId, String logisticsNo, String phone, String from, String to); + + /** + * 打印电子面单 + * @param orderSn 订单编号 + * @param logisticsId 物流Id + * @return + */ + Map labelOrder(String orderSn, String logisticsId); + + /** + * 顺丰平台下单 + * @param orderDetailVO 订单信息 + * @return 顺丰单号 + */ + String sfCreateOrder(OrderDetailVO orderDetailVO); /** * 获取已开启的物流公司列表 @@ -30,4 +59,10 @@ public interface LogisticsService extends IService { * @return 物流公司列表 */ List getOpenLogistics(); + + /** + * 获取物流设置 + * @return + */ + LogisticsSetting getLogisticsSetting(); } \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/system/serviceimpl/LogisticsServiceImpl.java b/framework/src/main/java/cn/lili/modules/system/serviceimpl/LogisticsServiceImpl.java index 6dd2cbac..36e6ce20 100644 --- a/framework/src/main/java/cn/lili/modules/system/serviceimpl/LogisticsServiceImpl.java +++ b/framework/src/main/java/cn/lili/modules/system/serviceimpl/LogisticsServiceImpl.java @@ -1,32 +1,40 @@ package cn.lili.modules.system.serviceimpl; -import cn.hutool.core.text.CharSequenceUtil; +import cn.hutool.json.JSONUtil; import cn.lili.common.enums.ResultCode; import cn.lili.common.enums.SwitchEnum; import cn.lili.common.exception.ServiceException; +import cn.lili.common.security.OperationalJudgment; +import cn.lili.common.utils.BeanUtil; +import cn.lili.modules.logistics.LogisticsPluginFactory; +import cn.lili.modules.logistics.entity.dto.LabelOrderDTO; +import cn.lili.modules.logistics.entity.enums.LogisticsEnum; +import cn.lili.modules.member.service.StoreLogisticsService; +import cn.lili.modules.order.order.entity.dos.Order; +import cn.lili.modules.order.order.entity.dos.OrderItem; +import cn.lili.modules.order.order.entity.enums.DeliverStatusEnum; +import cn.lili.modules.order.order.entity.enums.OrderStatusEnum; +import cn.lili.modules.order.order.entity.vo.OrderDetailVO; +import cn.lili.modules.order.order.service.OrderItemService; +import cn.lili.modules.order.order.service.OrderService; +import cn.lili.modules.store.entity.dos.StoreLogistics; +import cn.lili.modules.store.entity.dto.StoreDeliverGoodsAddressDTO; +import cn.lili.modules.store.service.StoreDetailService; import cn.lili.modules.system.entity.dos.Logistics; import cn.lili.modules.system.entity.dos.Setting; -import cn.lili.modules.system.entity.dto.KuaidiSetting; +import cn.lili.modules.system.entity.dto.LogisticsSetting; import cn.lili.modules.system.entity.enums.SettingEnum; import cn.lili.modules.system.entity.vo.Traces; import cn.lili.modules.system.mapper.LogisticsMapper; import cn.lili.modules.system.service.LogisticsService; import cn.lili.modules.system.service.SettingService; -import com.alibaba.fastjson.JSON; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; -import com.google.gson.Gson; import groovy.util.logging.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; -import java.io.*; -import java.net.HttpURLConnection; -import java.net.URL; -import java.net.URLEncoder; -import java.nio.charset.StandardCharsets; -import java.security.MessageDigest; -import java.util.HashMap; import java.util.List; import java.util.Map; @@ -39,20 +47,88 @@ import java.util.Map; @Slf4j @Service public class LogisticsServiceImpl extends ServiceImpl implements LogisticsService { + @Autowired + private LogisticsPluginFactory logisticsPluginFactory; + @Autowired + private OrderService orderService; + @Autowired + private OrderItemService orderItemService; + @Autowired + private StoreLogisticsService storeLogisticsService; + @Autowired + private StoreDetailService storeDetailService; + @Autowired private SettingService settingService; @Override - public Traces getLogistic(String logisticsId, String logisticsNo, String customerName) { + public Traces getLogisticTrack(String logisticsId, String logisticsNo, String phone) { try { - return getOrderTracesByJson(logisticsId, logisticsNo,customerName); + return logisticsPluginFactory.filePlugin().pollQuery(this.getById(logisticsId), logisticsNo, phone); } catch (Exception e) { - log.error("获取物流公司错误",e); + log.error("获取物流公司错误", e); } return null; } + @Override + public Traces getLogisticMapTrack(String logisticsId, String logisticsNo, String phone, String from, String to) { + try { + return logisticsPluginFactory.filePlugin().pollMapTrack(this.getById(logisticsId), logisticsNo, phone, from, to); + } catch (Exception e) { + log.error("获取物流公司错误", e); + + } + return null; + } + + @Override + public Map labelOrder(String orderSn, String logisticsId) { + //获取设置 + LogisticsSetting logisticsSetting = this.getLogisticsSetting(); + //获取订单及子订单 + Order order = OperationalJudgment.judgment(orderService.getBySn(orderSn)); + if ((LogisticsEnum.SHUNFENG.name().equals(logisticsSetting.getType()) && order.getDeliverStatus().equals(DeliverStatusEnum.DELIVERED.name()) && order.getOrderStatus().equals(OrderStatusEnum.DELIVERED.name())) + || (order.getDeliverStatus().equals(DeliverStatusEnum.UNDELIVERED.name()) && order.getOrderStatus().equals(OrderStatusEnum.UNDELIVERED.name()))) { + //订单货物 + List orderItems = orderItemService.getByOrderSn(orderSn); + //获取对应物流 + Logistics logistics; + + if(LogisticsEnum.SHUNFENG.name().equals(logisticsSetting.getType())){ + logistics = this.getOne(new LambdaQueryWrapper().eq(Logistics::getCode,"SF")); + }else{ + logistics = this.getById(logisticsId); + } + // 店铺-物流公司设置 + LambdaQueryWrapper lambdaQueryWrapper = Wrappers.lambdaQuery(); + lambdaQueryWrapper.eq(StoreLogistics::getLogisticsId, logistics.getId()); + lambdaQueryWrapper.eq(StoreLogistics::getStoreId, order.getStoreId()); + StoreLogistics storeLogistics = storeLogisticsService.getOne(lambdaQueryWrapper); + //获取店家信息 + StoreDeliverGoodsAddressDTO storeDeliverGoodsAddressDTO = storeDetailService.getStoreDeliverGoodsAddressDto(order.getStoreId()); + + LabelOrderDTO labelOrderDTO = new LabelOrderDTO(); + labelOrderDTO.setOrder(order); + labelOrderDTO.setOrderItems(orderItems); + labelOrderDTO.setLogistics(logistics); + labelOrderDTO.setStoreLogistics(storeLogistics); + labelOrderDTO.setStoreDeliverGoodsAddressDTO(storeDeliverGoodsAddressDTO); + //触发电子面单 + return logisticsPluginFactory.filePlugin().labelOrder(labelOrderDTO); + } else { + throw new ServiceException(ResultCode.ORDER_LABEL_ORDER_ERROR); + } + + } + + @Override + public String sfCreateOrder(OrderDetailVO orderDetailVO) { + return logisticsPluginFactory.filePlugin().createOrder(orderDetailVO); + } + + @Override public List getOpenLogistics() { LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); @@ -60,218 +136,10 @@ public class LogisticsServiceImpl extends ServiceImpl params = new HashMap<>(8); - params.put("RequestData", urlEncoder(requestData, "UTF-8")); - params.put("EBusinessID", EBusinessID); - params.put("RequestType", "1002"); - String dataSign = encrypt(requestData, AppKey, "UTF-8"); - params.put("DataSign", urlEncoder(dataSign, "UTF-8")); - params.put("DataType", "2"); - - String result = sendPost(ReqURL, params); - Map map = (Map) JSON.parse(result); - return new Traces(logistics.getName(), expNo, (List) map.get("Traces")); - } - return null; - } - - /** - * MD5加密 - * - * @param str 内容 - * @param charset 编码方式 - * @throws Exception - */ - @SuppressWarnings("unused") - private String MD5(String str, String charset) throws Exception { - MessageDigest md = MessageDigest.getInstance("MD5"); - md.update(str.getBytes(charset)); - byte[] result = md.digest(); - StringBuffer sb = new StringBuffer(32); - for (int i = 0; i < result.length; i++) { - int val = result[i] & 0xff; - if (val <= 0xf) { - sb.append("0"); - } - sb.append(Integer.toHexString(val)); - } - return sb.toString().toLowerCase(); - } - - /** - * base64编码 - * - * @param str 内容 - * @param charset 编码方式di - * @throws UnsupportedEncodingException - */ - private String base64(String str, String charset) throws UnsupportedEncodingException { - return base64Encode(str.getBytes(charset)); - } - - @SuppressWarnings("unused") - private String urlEncoder(String str, String charset) throws UnsupportedEncodingException { - return URLEncoder.encode(str, charset); - } - - /** - * 电商Sign签名生成 - * - * @param content 内容 - * @param keyValue Appkey - * @param charset 编码方式 - * @return DataSign签名 - * @throws UnsupportedEncodingException ,Exception - */ - @SuppressWarnings("unused") - private String encrypt(String content, String keyValue, String charset) throws Exception { - if (keyValue != null) { - return base64(MD5(content + keyValue, charset), charset); - } - return base64(MD5(content, charset), charset); - } - - /** - * 向指定 URL 发送POST方法的请求 - * - * @param url 发送请求的 URL - * @param params 请求的参数集合 - * @return 远程资源的响应结果 - */ - @SuppressWarnings("unused") - private String sendPost(String url, Map params) { - OutputStreamWriter out = null; - BufferedReader in = null; - StringBuilder result = new StringBuilder(); - try { - URL realUrl = new URL(url); - HttpURLConnection conn = (HttpURLConnection) realUrl.openConnection(); - //发送POST请求必须设置如下两行 - conn.setDoOutput(true); - conn.setDoInput(true); - //POST方法 - conn.setRequestMethod("POST"); - //设置通用的请求属性 - conn.setRequestProperty("accept", "*/*"); - conn.setRequestProperty("connection", "Keep-Alive"); - conn.setRequestProperty("user-agent", - "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)"); - conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded"); - conn.connect(); - //获取URLConnection对象对应的输出流 - out = new OutputStreamWriter(conn.getOutputStream(), StandardCharsets.UTF_8); - //发送请求参数 - if (params != null) { - StringBuilder param = new StringBuilder(); - for (Map.Entry entry : params.entrySet()) { - if (param.length() > 0) { - param.append("&"); - } - param.append(entry.getKey()); - param.append("="); - param.append(entry.getValue()); - } - out.write(param.toString()); - } - //flush输出流的缓冲 - out.flush(); - //定义BufferedReader输入流来读取URL的响应 - in = new BufferedReader( - new InputStreamReader(conn.getInputStream(), StandardCharsets.UTF_8)); - String line; - while ((line = in.readLine()) != null) { - result.append(line); - } - } catch (Exception e) { - log.error("向指定 URL 发送POST方法的请求错误",e); - } - //使用finally块来关闭输出流、输入流 - finally { - try { - if (out != null) { - out.close(); - } - if (in != null) { - in.close(); - } - } catch (IOException ex) { - ex.printStackTrace(); - } - } - return result.toString(); - } - - - private static final char[] BASE64_ENCODE_CHARS = new char[]{ - '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', '0', '1', '2', '3', - '4', '5', '6', '7', '8', '9', '+', '/'}; - - public static String base64Encode(byte[] data) { - StringBuffer sb = new StringBuffer(); - int len = data.length; - int i = 0; - int b1, b2, b3; - while (i < len) { - b1 = data[i++] & 0xff; - if (i == len) { - sb.append(BASE64_ENCODE_CHARS[b1 >>> 2]); - sb.append(BASE64_ENCODE_CHARS[(b1 & 0x3) << 4]); - sb.append("=="); - break; - } - b2 = data[i++] & 0xff; - if (i == len) { - sb.append(BASE64_ENCODE_CHARS[b1 >>> 2]); - sb.append(BASE64_ENCODE_CHARS[((b1 & 0x03) << 4) | ((b2 & 0xf0) >>> 4)]); - sb.append(BASE64_ENCODE_CHARS[(b2 & 0x0f) << 2]); - sb.append("="); - break; - } - b3 = data[i++] & 0xff; - sb.append(BASE64_ENCODE_CHARS[b1 >>> 2]); - sb.append(BASE64_ENCODE_CHARS[((b1 & 0x03) << 4) | ((b2 & 0xf0) >>> 4)]); - sb.append(BASE64_ENCODE_CHARS[((b2 & 0x0f) << 2) | ((b3 & 0xc0) >>> 6)]); - sb.append(BASE64_ENCODE_CHARS[b3 & 0x3f]); - } - return sb.toString(); + @Override + public LogisticsSetting getLogisticsSetting() { + Setting setting = settingService.get(SettingEnum.LOGISTICS_SETTING.name()); + return JSONUtil.toBean(setting.getSettingValue(), LogisticsSetting.class); } } diff --git a/framework/src/main/java/cn/lili/modules/system/token/ManagerTokenGenerate.java b/framework/src/main/java/cn/lili/modules/system/token/ManagerTokenGenerate.java index 484b38a5..310740e2 100644 --- a/framework/src/main/java/cn/lili/modules/system/token/ManagerTokenGenerate.java +++ b/framework/src/main/java/cn/lili/modules/system/token/ManagerTokenGenerate.java @@ -40,18 +40,26 @@ public class ManagerTokenGenerate extends AbstractTokenGenerate { @Override public Token createToken(AdminUser adminUser, Boolean longTerm) { - AuthUser authUser = new AuthUser(adminUser.getUsername(), adminUser.getId(), adminUser.getAvatar(), UserEnums.MANAGER, adminUser.getNickName(), adminUser.getIsSuper()); + AuthUser authUser = AuthUser.builder() + .username(adminUser.getUsername()) + .id(adminUser.getId()) + .face(adminUser.getAvatar()) + .role(UserEnums.MANAGER) + .nickName(adminUser.getNickName()) + .isSuper(adminUser.getIsSuper()) + .longTerm(longTerm) + .build(); List userMenuVOList = roleMenuService.findAllMenu(authUser.getId()); //缓存权限列表 cache.put(CachePrefix.PERMISSION_LIST.getPrefix(UserEnums.MANAGER) + authUser.getId(), this.permissionList(userMenuVOList)); - return tokenUtil.createToken(adminUser.getUsername(), authUser, longTerm, UserEnums.MANAGER); + return tokenUtil.createToken(authUser); } @Override public Token refreshToken(String refreshToken) { - return tokenUtil.refreshToken(refreshToken, UserEnums.MANAGER); + return tokenUtil.refreshToken(refreshToken); } /** diff --git a/framework/src/main/java/cn/lili/modules/wallet/entity/dos/MemberWithdrawApply.java b/framework/src/main/java/cn/lili/modules/wallet/entity/dos/MemberWithdrawApply.java index 364bd5ad..78c630ae 100644 --- a/framework/src/main/java/cn/lili/modules/wallet/entity/dos/MemberWithdrawApply.java +++ b/framework/src/main/java/cn/lili/modules/wallet/entity/dos/MemberWithdrawApply.java @@ -56,4 +56,9 @@ public class MemberWithdrawApply extends BaseEntity { @ApiModelProperty(value = "sn") private String sn; + @ApiModelProperty(value = "真实姓名") + private String realName; + @ApiModelProperty(value = "第三方平台账号") + private String connectNumber; + } diff --git a/framework/src/main/java/cn/lili/modules/wallet/serviceimpl/MemberWalletServiceImpl.java b/framework/src/main/java/cn/lili/modules/wallet/serviceimpl/MemberWalletServiceImpl.java index be6f99ee..9d838430 100644 --- a/framework/src/main/java/cn/lili/modules/wallet/serviceimpl/MemberWalletServiceImpl.java +++ b/framework/src/main/java/cn/lili/modules/wallet/serviceimpl/MemberWalletServiceImpl.java @@ -11,6 +11,8 @@ import cn.lili.common.utils.SnowFlake; import cn.lili.common.utils.StringUtils; import cn.lili.modules.member.entity.dos.Member; import cn.lili.modules.member.service.MemberService; +import cn.lili.modules.payment.entity.enums.PaymentMethodEnum; +import cn.lili.modules.payment.kit.CashierSupport; import cn.lili.modules.system.entity.dos.Setting; import cn.lili.modules.system.entity.dto.WithdrawalSetting; import cn.lili.modules.system.entity.enums.SettingEnum; @@ -77,6 +79,8 @@ public class MemberWalletServiceImpl extends ServiceImpl 1000000) { + throw new ServiceException(ResultCode.WALLET_WITHDRAWAL_AMOUNT_ERROR); + } + MemberWithdrawalMessage memberWithdrawalMessage = new MemberWithdrawalMessage(); AuthUser authUser = UserContext.getCurrentUser(); //构建审核参数 @@ -302,7 +311,8 @@ public class MemberWalletServiceImpl extends ServiceImpl i @Override public Recharge recharge(Double price) { + + if (price == null || price <= 0 || price > 1000000) { + throw new ServiceException(ResultCode.RECHARGE_PRICE_ERROR); + } + //获取当前登录的会员 AuthUser authUser = UserContext.getCurrentUser(); //构建sn diff --git a/framework/src/main/java/cn/lili/modules/wechat/util/WechatMessageUtil.java b/framework/src/main/java/cn/lili/modules/wechat/util/WechatMessageUtil.java index da0dfc45..196569c0 100644 --- a/framework/src/main/java/cn/lili/modules/wechat/util/WechatMessageUtil.java +++ b/framework/src/main/java/cn/lili/modules/wechat/util/WechatMessageUtil.java @@ -7,7 +7,6 @@ import cn.lili.common.enums.ClientTypeEnum; import cn.lili.common.enums.ResultCode; import cn.lili.common.exception.ServiceException; import cn.lili.common.utils.DateUtil; -import cn.lili.common.utils.HttpUtils; import cn.lili.common.utils.StringUtils; import cn.lili.modules.connect.entity.Connect; import cn.lili.modules.connect.entity.enums.ConnectEnum; @@ -111,7 +110,7 @@ public class WechatMessageUtil { map.put("data", postParams); log.info("参数内容:" + JSONUtil.toJsonStr(map)); - String content = HttpUtils.doPostWithJson(url, map); + String content = HttpUtil.post(url, JSONUtil.toJsonStr(map)); JSONObject json = new JSONObject(content); log.info("微信消息发送结果:" + content); String errorMessage = json.getStr("errmsg"); diff --git a/framework/src/main/resources/maven-repository/SF-CSIM-EXPRESS-SDK-V2.1.7.jar b/framework/src/main/resources/maven-repository/SF-CSIM-EXPRESS-SDK-V2.1.7.jar new file mode 100644 index 00000000..f7028c96 Binary files /dev/null and b/framework/src/main/resources/maven-repository/SF-CSIM-EXPRESS-SDK-V2.1.7.jar differ diff --git a/im-api/src/main/java/cn/lili/controller/im/ImMessageController.java b/im-api/src/main/java/cn/lili/controller/im/ImMessageController.java index 3aedb25d..6ef1180a 100644 --- a/im-api/src/main/java/cn/lili/controller/im/ImMessageController.java +++ b/im-api/src/main/java/cn/lili/controller/im/ImMessageController.java @@ -2,6 +2,7 @@ package cn.lili.controller.im; import cn.lili.common.enums.ResultCode; import cn.lili.common.enums.ResultUtil; +import cn.lili.common.exception.ServiceException; import cn.lili.common.vo.ResultMessage; import cn.lili.modules.im.entity.dos.ImMessage; import cn.lili.modules.im.entity.dto.MessageQueryParams; @@ -32,39 +33,39 @@ public class ImMessageController { @ApiOperation(value = "查看Im消息详情") public ResultMessage get(@PathVariable String id) { ImMessage imMessage = imMessageService.getById(id); - return new ResultUtil().setData(imMessage); + return ResultUtil.data(imMessage); } @GetMapping @ApiOperation(value = "分页获取Im消息") public ResultMessage> historyMessage(MessageQueryParams messageQueryParams) { List data = imMessageService.getList(messageQueryParams); - return new ResultUtil>().setData(data); + return ResultUtil.data(data); } @PostMapping @ApiOperation(value = "新增Im消息") public ResultMessage save(ImMessage imMessage) { if (imMessageService.save(imMessage)) { - return new ResultUtil().setData(imMessage); + return ResultUtil.data(imMessage); } - return new ResultUtil().setErrorMsg(ResultCode.ERROR); + throw new ServiceException(ResultCode.IM_MESSAGE_ADD_ERROR); } @PutMapping("/{id}") @ApiOperation(value = "更新Im消息") public ResultMessage update(@PathVariable String id, ImMessage imMessage) { if (imMessageService.updateById(imMessage)) { - return new ResultUtil().setData(imMessage); + return ResultUtil.data(imMessage); } - return new ResultUtil().setErrorMsg(ResultCode.ERROR); + throw new ServiceException(ResultCode.IM_MESSAGE_EDIT_ERROR); } @DeleteMapping(value = "/{ids}") @ApiOperation(value = "删除Im消息") public ResultMessage delAllByIds(@PathVariable List ids) { imMessageService.removeByIds(ids); - return ResultUtil.success(ResultCode.SUCCESS); + return ResultUtil.success(); } diff --git a/im-api/src/main/java/cn/lili/controller/im/ImTalkController.java b/im-api/src/main/java/cn/lili/controller/im/ImTalkController.java index 4ed9576a..3ecbcd35 100644 --- a/im-api/src/main/java/cn/lili/controller/im/ImTalkController.java +++ b/im-api/src/main/java/cn/lili/controller/im/ImTalkController.java @@ -2,16 +2,11 @@ package cn.lili.controller.im; import cn.lili.common.enums.ResultCode; import cn.lili.common.enums.ResultUtil; -import cn.lili.common.security.AuthUser; -import cn.lili.common.security.context.UserContext; import cn.lili.common.vo.ResultMessage; import cn.lili.modules.im.entity.dos.ImTalk; import cn.lili.modules.im.entity.vo.ImTalkVO; import cn.lili.modules.im.service.ImTalkService; -import cn.lili.modules.store.service.StoreService; -import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import io.swagger.annotations.Api; -import io.swagger.annotations.ApiImplicitParam; import io.swagger.annotations.ApiOperation; import lombok.RequiredArgsConstructor; import org.springframework.beans.factory.annotation.Autowired; @@ -19,7 +14,6 @@ import org.springframework.transaction.annotation.Transactional; import org.springframework.web.bind.annotation.*; import java.util.List; -import java.util.stream.Collectors; /** @@ -34,43 +28,38 @@ public class ImTalkController { private final ImTalkService imTalkService; - @Autowired - private StoreService storeService; - @GetMapping(value = "/{id}") @ApiOperation(value = "查看聊天详情") public ResultMessage get(@PathVariable String id) { ImTalk imTalk = imTalkService.getById(id); - return new ResultUtil().setData(imTalk); + return ResultUtil.data(imTalk); } @GetMapping(value = "/user/{uid}") @ApiOperation(value = "查看与某人聊天详情") public ResultMessage getUser(@PathVariable String uid) { - AuthUser authUser = UserContext.getCurrentUser(); - return ResultUtil.data(imTalkService.getTalkByUser(authUser.getId(), uid)); + //通过长度判断,保证每次都是同一个聊天 + return ResultUtil.data(imTalkService.getTalkByUser(uid)); } @GetMapping(value = "/by/user/{userId}") @ApiOperation(value = "查看与某人聊天详情") public ResultMessage getByUser(@PathVariable String userId) { - AuthUser authUser = UserContext.getCurrentUser(); - return ResultUtil.data(new ImTalkVO(imTalkService.getTalkByUser(authUser.getId(), userId), authUser.getId())); + return ResultUtil.data(new ImTalkVO(imTalkService.getTalkByUser(userId),userId)); } @GetMapping(value = "/top") @ApiOperation(value = "查看与某人聊天详情") - public ResultMessage top(String id, Boolean top) { + public ResultMessage top(String id, Boolean top) { imTalkService.top(id, top); return ResultUtil.success(); } @GetMapping("/list") - @ApiOperation(value = "分页获取聊天") - @ApiImplicitParam(name = "userName", value = "用户名称", paramType = "query", dataType = "String") - public ResultMessage> getUserTalkList(String userName) { - return ResultUtil.data(imTalkService.getUserTalkList(userName)); + @ApiOperation(value = "分页获取用户聊天") + public ResultMessage> getUserTalkList() { + return ResultUtil.data(imTalkService.getUserTalkList()); } @GetMapping("/store/list") diff --git a/im-api/src/main/java/cn/lili/controller/im/ImUserController.java b/im-api/src/main/java/cn/lili/controller/im/ImUserController.java deleted file mode 100644 index d39e0e59..00000000 --- a/im-api/src/main/java/cn/lili/controller/im/ImUserController.java +++ /dev/null @@ -1,80 +0,0 @@ -package cn.lili.controller.im; - - -import cn.lili.common.enums.ResultUtil; -import cn.lili.common.security.AuthUser; -import cn.lili.common.security.context.UserContext; -import cn.lili.common.vo.ResultMessage; -import cn.lili.modules.member.entity.dos.Member; -import cn.lili.modules.member.entity.dto.FootPrintQueryParams; -import cn.lili.modules.member.service.FootprintService; -import cn.lili.modules.member.service.MemberService; -import cn.lili.modules.search.entity.dos.EsGoodsIndex; -import cn.lili.modules.store.entity.dos.Store; -import cn.lili.modules.store.service.StoreService; -import com.baomidou.mybatisplus.core.metadata.IPage; -import io.swagger.annotations.Api; -import io.swagger.annotations.ApiImplicitParam; -import io.swagger.annotations.ApiOperation; -import lombok.RequiredArgsConstructor; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.transaction.annotation.Transactional; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; - - -/** - * @author Chopper - */ -@RestController -@Api(tags = "Im消息接口") -@RequestMapping("/im/user") -@Transactional(rollbackFor = Exception.class) -@RequiredArgsConstructor(onConstructor = @__(@Autowired)) -public class ImUserController { - - private final MemberService memberService; - - @Autowired - private StoreService storeService; - - @Autowired - private FootprintService footprintService; - - @GetMapping - @ApiOperation(value = "获取用户信息") - public ResultMessage getImUser() { - AuthUser authUser = UserContext.getCurrentUser(); - return ResultUtil.data(memberService.getById(authUser.getId())); - } - - @GetMapping("/store") - @ApiOperation(value = "获取店铺信息") - public ResultMessage getStoreUser() { - AuthUser authUser = UserContext.getCurrentUser(); - return ResultUtil.data(storeService.getById(authUser.getStoreId())); - } - - @GetMapping("/{memberId}") - @ApiImplicitParam(name = "memberId", value = "店铺Id", required = true, dataType = "String", paramType = "path") - @ApiOperation(value = "获取用户信息") - public ResultMessage getImUserDetail(@PathVariable String memberId) { - return ResultUtil.data(memberService.getById(memberId)); - } - - @GetMapping("/store/{storeId}") - @ApiImplicitParam(name = "storeId", value = "店铺Id", required = true, dataType = "String", paramType = "path") - @ApiOperation(value = "获取店铺信息") - public ResultMessage getStoreUserDetail(@PathVariable String storeId) { - return ResultUtil.data(storeService.getById(storeId)); - } - - @GetMapping("/history") - @ApiOperation(value = "获取会员的历史足迹") - public ResultMessage> getMemberHistory(FootPrintQueryParams params) { - return ResultUtil.data(footprintService.footPrintPage(params)); - } - -} diff --git a/manager-api/src/main/java/cn/lili/controller/setting/SettingManagerController.java b/manager-api/src/main/java/cn/lili/controller/setting/SettingManagerController.java index bf85cf9b..b7063459 100644 --- a/manager-api/src/main/java/cn/lili/controller/setting/SettingManagerController.java +++ b/manager-api/src/main/java/cn/lili/controller/setting/SettingManagerController.java @@ -1,6 +1,7 @@ package cn.lili.controller.setting; import cn.hutool.json.JSONUtil; +import cn.lili.cache.Cache; import cn.lili.common.aop.annotation.DemoSite; import cn.lili.common.enums.ResultCode; import cn.lili.common.enums.ResultUtil; @@ -12,6 +13,7 @@ import cn.lili.modules.system.entity.dto.connect.QQConnectSetting; import cn.lili.modules.system.entity.dto.connect.WechatConnectSetting; import cn.lili.modules.system.entity.dto.payment.AlipayPaymentSetting; import cn.lili.modules.system.entity.dto.payment.PaymentSupportSetting; +import cn.lili.modules.system.entity.dto.payment.UnionPaymentSetting; import cn.lili.modules.system.entity.dto.payment.WechatPaymentSetting; import cn.lili.modules.system.entity.dto.payment.dto.PaymentSupportForm; import cn.lili.modules.system.entity.enums.SettingEnum; @@ -36,6 +38,11 @@ import java.util.Collections; public class SettingManagerController { @Autowired private SettingService settingService; + /** + * 缓存 + */ + @Autowired + private Cache cache; @DemoSite @@ -79,7 +86,6 @@ public class SettingManagerController { } - /** * 对配置进行过滤 * @@ -111,6 +117,7 @@ public class SettingManagerController { */ private ResultMessage createSetting(String key) { SettingEnum settingEnum = SettingEnum.valueOf(key); + cache.remove(key); Setting setting = settingService.get(key); switch (settingEnum) { case BASE_SETTING: @@ -133,10 +140,10 @@ public class SettingManagerController { return setting == null ? ResultUtil.data(new GoodsSetting()) : ResultUtil.data(JSONUtil.toBean(setting.getSettingValue(), GoodsSetting.class)); - case KUAIDI_SETTING: + case LOGISTICS_SETTING: return setting == null ? - ResultUtil.data(new KuaidiSetting()) : - ResultUtil.data(JSONUtil.toBean(setting.getSettingValue(), KuaidiSetting.class)); + ResultUtil.data(new LogisticsSetting()) : + ResultUtil.data(JSONUtil.toBean(setting.getSettingValue(), LogisticsSetting.class)); case ORDER_SETTING: return setting == null ? ResultUtil.data(new OrderSetting()) : @@ -165,6 +172,10 @@ public class SettingManagerController { return setting == null ? ResultUtil.data(new AlipayPaymentSetting()) : ResultUtil.data(JSONUtil.toBean(setting.getSettingValue(), AlipayPaymentSetting.class)); + case UNIONPAY_PAYMENT: + return setting == null ? + ResultUtil.data(new UnionPaymentSetting()) : + ResultUtil.data(JSONUtil.toBean(setting.getSettingValue(), UnionPaymentSetting.class)); case WECHAT_CONNECT: return setting == null ? ResultUtil.data(new WechatConnectSetting()) : diff --git a/pom.xml b/pom.xml index fb8c22ca..1d30464c 100644 --- a/pom.xml +++ b/pom.xml @@ -61,6 +61,10 @@ 2.3.1 20211018.2 8.0.3 + 3.21.8 + 5.6.97 + 3.1.693 + 1.0.11 diff --git a/seller-api/src/main/java/cn/lili/controller/order/OrderStoreController.java b/seller-api/src/main/java/cn/lili/controller/order/OrderStoreController.java index 3bbdd2c0..d1345a1b 100644 --- a/seller-api/src/main/java/cn/lili/controller/order/OrderStoreController.java +++ b/seller-api/src/main/java/cn/lili/controller/order/OrderStoreController.java @@ -7,7 +7,6 @@ import cn.lili.common.enums.ResultUtil; import cn.lili.common.security.OperationalJudgment; import cn.lili.common.security.context.UserContext; import cn.lili.common.vo.ResultMessage; -import cn.lili.modules.kdBrid.service.KdNiaoService; import cn.lili.modules.member.entity.dto.MemberAddressDTO; import cn.lili.modules.member.service.StoreLogisticsService; import cn.lili.modules.order.order.entity.dto.OrderExportDTO; @@ -16,6 +15,7 @@ import cn.lili.modules.order.order.entity.vo.OrderDetailVO; import cn.lili.modules.order.order.entity.vo.OrderSimpleVO; import cn.lili.modules.order.order.service.OrderPriceService; import cn.lili.modules.order.order.service.OrderService; +import cn.lili.modules.system.service.LogisticsService; import com.baomidou.mybatisplus.core.metadata.IPage; import io.swagger.annotations.Api; import io.swagger.annotations.ApiImplicitParam; @@ -63,10 +63,10 @@ public class OrderStoreController { private StoreLogisticsService storeLogisticsService; /** - * 快递鸟电子面单 + * 快递 */ @Autowired - private KdNiaoService kdNiaoService; + private LogisticsService logisticsService; @ApiOperation(value = "查询订单列表") @@ -120,6 +120,14 @@ public class OrderStoreController { return ResultUtil.data(orderService.delivery(orderSn, logisticsNo, logisticsId)); } + @PreventDuplicateSubmissions + @ApiOperation(value = "订单顺丰发货") + @ApiImplicitParam(name = "orderSn", value = "订单sn", required = true, dataType = "String", paramType = "path") + @PostMapping(value = "/{orderSn}/shunfeng/delivery") + public ResultMessage shunFengDelivery(@NotNull(message = "参数非法") @PathVariable String orderSn) { + return ResultUtil.data(orderService.shunFengDelivery(orderSn)); + } + @PreventDuplicateSubmissions @ApiOperation(value = "取消订单") @ApiImplicitParams({ @@ -200,7 +208,7 @@ public class OrderStoreController { @ApiImplicitParam(name = "logisticsId", value = "物流公司", required = true, dataType = "String", paramType = "query") }) public ResultMessage createElectronicsFaceSheet(@NotNull(message = "参数非法") @PathVariable String orderSn, - @NotNull(message = "请选择物流公司") String logisticsId) throws Exception{ - return ResultUtil.data(kdNiaoService.createElectronicsFaceSheet(orderSn,logisticsId)); + @NotNull(message = "请选择物流公司") String logisticsId) { + return ResultUtil.data(logisticsService.labelOrder(orderSn, logisticsId)); } } \ No newline at end of file diff --git a/seller-api/src/main/java/cn/lili/controller/other/LogisticsStoreController.java b/seller-api/src/main/java/cn/lili/controller/other/LogisticsStoreController.java index 2571ab17..40c0f783 100644 --- a/seller-api/src/main/java/cn/lili/controller/other/LogisticsStoreController.java +++ b/seller-api/src/main/java/cn/lili/controller/other/LogisticsStoreController.java @@ -1,13 +1,21 @@ package cn.lili.controller.other; +import cn.hutool.json.JSONUtil; +import cn.lili.common.enums.ResultCode; import cn.lili.common.enums.ResultUtil; +import cn.lili.common.exception.ServiceException; import cn.lili.common.security.context.UserContext; import cn.lili.common.vo.ResultMessage; import cn.lili.modules.member.service.StoreLogisticsService; import cn.lili.modules.store.entity.dos.StoreLogistics; import cn.lili.modules.store.entity.dto.StoreLogisticsCustomerDTO; +import cn.lili.modules.system.entity.dos.Setting; +import cn.lili.modules.system.entity.dto.ImSetting; +import cn.lili.modules.system.entity.dto.LogisticsSetting; +import cn.lili.modules.system.entity.enums.SettingEnum; import cn.lili.modules.system.entity.vo.StoreLogisticsVO; +import cn.lili.modules.system.service.SettingService; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import io.swagger.annotations.Api; import io.swagger.annotations.ApiImplicitParam; @@ -36,6 +44,9 @@ public class LogisticsStoreController { @Autowired private StoreLogisticsService storeLogisticsService; + @Autowired + private SettingService settingService; + @ApiOperation(value = "获取商家物流公司列表,如果已选择则checked有值") @GetMapping public ResultMessage> get() { @@ -99,4 +110,18 @@ public class LogisticsStoreController { return ResultUtil.data(storeLogisticsService.getStoreLogisticsInfo(logisticsId)); } + @ApiOperation(value = "获取IM接口前缀") + @GetMapping("/setting") + public ResultMessage getUrl() { + String logisticsType; + try { + Setting logisticsSettingVal = settingService.get(SettingEnum.LOGISTICS_SETTING.name()); + LogisticsSetting logisticsSetting = JSONUtil.toBean(logisticsSettingVal.getSettingValue(), LogisticsSetting.class); + logisticsType = logisticsSetting.getType(); + } catch (Exception e) { + throw new ServiceException(ResultCode.ORDER_LOGISTICS_ERROR); + } + return ResultUtil.data(logisticsType); + } + } diff --git a/seller-api/src/main/java/cn/lili/controller/passport/StorePassportController.java b/seller-api/src/main/java/cn/lili/controller/passport/StorePassportController.java index 864cd1b8..d9c95721 100644 --- a/seller-api/src/main/java/cn/lili/controller/passport/StorePassportController.java +++ b/seller-api/src/main/java/cn/lili/controller/passport/StorePassportController.java @@ -11,6 +11,7 @@ import cn.lili.common.security.enums.UserEnums; import cn.lili.common.vo.ResultMessage; import cn.lili.modules.member.entity.dos.Member; import cn.lili.modules.member.service.MemberService; +import cn.lili.modules.sms.SmsUtil; import cn.lili.modules.verification.entity.enums.VerificationEnums; import cn.lili.modules.verification.service.VerificationService; import io.swagger.annotations.Api; @@ -41,6 +42,9 @@ public class StorePassportController { @Autowired private MemberService memberService; + @Autowired + private SmsUtil smsUtil; + @Autowired private VerificationService verificationService; @@ -59,6 +63,22 @@ public class StorePassportController { } } + @ApiOperation(value = "短信登录接口") + @ApiImplicitParams({ + @ApiImplicitParam(name = "mobile", value = "手机号", required = true, paramType = "query"), + @ApiImplicitParam(name = "code", value = "验证码", required = true, paramType = "query") + }) + @PostMapping("/smsLogin") + public ResultMessage smsLogin(@NotNull(message = "手机号为空") @RequestParam String mobile, + @NotNull(message = "验证码为空") @RequestParam String code, + @RequestHeader String uuid) { + if (smsUtil.verifyCode(mobile, VerificationEnums.LOGIN, uuid, code)) { + return ResultUtil.data(memberService.mobilePhoneStoreLogin(mobile)); + } else { + throw new ServiceException(ResultCode.VERIFICATION_SMS_CHECKED_ERROR); + } + } + @ApiOperation(value = "注销接口") @PostMapping("/logout") public ResultMessage logout() { @@ -66,6 +86,33 @@ public class StorePassportController { return ResultUtil.success(); } + @ApiOperation(value = "通过短信重置密码") + @ApiImplicitParams({ + @ApiImplicitParam(name = "mobile", value = "手机号", required = true, paramType = "query"), + @ApiImplicitParam(name = "password", value = "是否保存登录", required = true, paramType = "query") + }) + @PostMapping("/resetByMobile") + public ResultMessage resetByMobile(@NotNull(message = "手机号为空") @RequestParam String mobile, + @NotNull(message = "验证码为空") @RequestParam String code, + @RequestHeader String uuid) { + //校验短信验证码是否正确 + if (smsUtil.verifyCode(mobile, VerificationEnums.FIND_USER, uuid, code)) { + //校验是否通过手机号可获取会员,存在则将会员信息存入缓存,有效时间3分钟 + memberService.findByMobile(uuid, mobile); + return ResultUtil.success(); + } else { + throw new ServiceException(ResultCode.VERIFICATION_SMS_CHECKED_ERROR); + } + } + @ApiOperation(value = "修改密码") + @ApiImplicitParams({ + @ApiImplicitParam(name = "password", value = "密码", required = true, paramType = "query") + }) + @PostMapping("/resetPassword") + public ResultMessage resetByMobile(@NotNull(message = "密码为空") @RequestParam String password, @RequestHeader String uuid) { + return ResultUtil.data(memberService.resetByMobile(uuid, password)); + } + @ApiOperation(value = "修改密码") @ApiImplicitParams({ @ApiImplicitParam(name = "password", value = "旧密码", required = true, paramType = "query"), @@ -86,7 +133,4 @@ public class StorePassportController { public ResultMessage refreshToken(@NotNull(message = "刷新token不能为空") @PathVariable String refreshToken) { return ResultUtil.data(this.memberService.refreshStoreToken(refreshToken)); } - - - }