xuhy
2024-08-22 5e1cf8e0e94b42a94c3a3d3ccda5147fe6ce94f0
Merge remote-tracking branch 'origin/master'
72个文件已修改
2个文件已删除
31个文件已添加
3047 ■■■■ 已修改文件
ruoyi-api/ruoyi-api-account/src/main/java/com/ruoyi/account/api/dto/UserListQueryDto.java 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-api/ruoyi-api-account/src/main/java/com/ruoyi/account/api/factory/AppUserVipDetailFallbackFactory.java 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-api/ruoyi-api-account/src/main/java/com/ruoyi/account/api/feignClient/AppCouponClient.java 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-api/ruoyi-api-account/src/main/java/com/ruoyi/account/api/feignClient/AppUserVipDetailClient.java 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-api/ruoyi-api-account/src/main/java/com/ruoyi/account/api/model/TAppUser.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-api/ruoyi-api-account/src/main/java/com/ruoyi/account/api/model/TAppUserVipDetail.java 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-api/ruoyi-api-account/src/main/java/com/ruoyi/account/api/vo/GetAppUserVipDetail.java 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-api/ruoyi-api-chargingPile/src/main/java/com/ruoyi/chargingPile/api/dto/GetPermissionConfigurationDTO.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-api/ruoyi-api-chargingPile/src/main/java/com/ruoyi/chargingPile/api/dto/GetSiteListDTO.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-api/ruoyi-api-chargingPile/src/main/java/com/ruoyi/chargingPile/api/factory/ChargingPileFallbackFactory.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-api/ruoyi-api-chargingPile/src/main/java/com/ruoyi/chargingPile/api/feignClient/ChargingPileClient.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-api/ruoyi-api-chargingPile/src/main/java/com/ruoyi/chargingPile/api/model/Site.java 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-api/ruoyi-api-chargingPile/src/main/java/com/ruoyi/chargingPile/api/model/TChargingPile.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-api/ruoyi-api-chargingPile/src/main/java/com/ruoyi/chargingPile/api/vo/SiteInfoVO.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-api/ruoyi-api-chargingPile/src/main/java/com/ruoyi/chargingPile/api/vo/TChargingGunVO.java 18 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-api/ruoyi-api-chargingPile/src/main/resources/template/充电桩申请记录.xlsx 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-api/ruoyi-api-order/src/main/java/com/ruoyi/order/api/factory/OrderFallbackFactory.java 7 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-api/ruoyi-api-order/src/main/java/com/ruoyi/order/api/feignClient/OrderClient.java 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-api/ruoyi-api-order/src/main/java/com/ruoyi/order/api/model/TChargingOrder.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-api/ruoyi-api-order/src/main/java/com/ruoyi/order/api/query/ChargingOrderQuery.java 34 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-api/ruoyi-api-order/src/main/java/com/ruoyi/order/api/vo/ChargingOrderVO.java 44 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-api/ruoyi-api-other/src/main/java/com/ruoyi/other/api/domain/TCoupon.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-api/ruoyi-api-other/src/main/java/com/ruoyi/other/api/domain/TGoods.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-api/ruoyi-api-other/src/main/java/com/ruoyi/other/api/domain/TRoleSite.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-api/ruoyi-api-other/src/main/java/com/ruoyi/other/api/domain/TUserSite.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-api/ruoyi-api-other/src/main/java/com/ruoyi/other/api/dto/ExchangeDto.java 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-api/ruoyi-api-other/src/main/java/com/ruoyi/other/api/dto/WebSocketMsg.java 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-api/ruoyi-api-other/src/main/java/com/ruoyi/other/api/factory/UserSiteFallbackFactory.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-api/ruoyi-api-other/src/main/java/com/ruoyi/other/api/factory/WebSocketFallbackFactory.java 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-api/ruoyi-api-other/src/main/java/com/ruoyi/other/api/feignClient/OtherClient.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-api/ruoyi-api-other/src/main/java/com/ruoyi/other/api/feignClient/WebSocketClient.java 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-api/ruoyi-api-other/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-api/ruoyi-api-payment/src/main/java/com/ruoyi/payment/api/factory/ChargingOrderFallbackFactory.java 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-api/ruoyi-api-payment/src/main/java/com/ruoyi/payment/api/factory/WxPaymentFallbackFactory.java 51 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-api/ruoyi-api-payment/src/main/java/com/ruoyi/payment/api/feignClient/ChargingOrderClient.java 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-api/ruoyi-api-payment/src/main/java/com/ruoyi/payment/api/feignClient/WxPaymentClient.java 62 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-api/ruoyi-api-payment/src/main/java/com/ruoyi/payment/api/vo/NotifyV3PayDecodeRespBody.java 222 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-api/ruoyi-api-payment/src/main/java/com/ruoyi/payment/api/vo/PaymentOrder.java 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-api/ruoyi-api-system/src/main/java/com/ruoyi/system/api/domain/SysOperLog.java 212 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/constant/UserConstants.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/OrderCodeUtil.java 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-security/src/main/java/com/ruoyi/common/security/utils/SecurityUtils.java 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-gateway/src/main/java/com/ruoyi/gateway/filter/AuthFilter.java 24 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-gateway/src/main/java/com/ruoyi/gateway/filter/RequestParamGlobalFilter.java 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-gateway/src/main/java/com/ruoyi/gateway/filter/SignFilter.java 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/controller/SysUserController.java 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/query/ShopUserStart.java 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysMenuServiceImpl.java 17 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysUserServiceImpl.java 28 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/controller/TAppCouponController.java 42 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/controller/TAppUserController.java 15 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/controller/TAppUserVipDetailController.java 41 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/util/PointDetailUtil.java 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-account/src/main/resources/mapper/account/TAppCouponMapper.xml 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-account/src/main/resources/mybatis-config.xml 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-chargingPile/src/main/java/com/ruoyi/chargingPile/controller/FileController.java 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-chargingPile/src/main/java/com/ruoyi/chargingPile/controller/SiteController.java 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-chargingPile/src/main/java/com/ruoyi/chargingPile/controller/TAccountingStrategyController.java 41 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-chargingPile/src/main/java/com/ruoyi/chargingPile/controller/TApplyChargingPileController.java 68 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-chargingPile/src/main/java/com/ruoyi/chargingPile/controller/TChargingGunController.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-chargingPile/src/main/java/com/ruoyi/chargingPile/controller/TChargingPileController.java 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-chargingPile/src/main/java/com/ruoyi/chargingPile/mapper/TChargingPileMapper.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-chargingPile/src/main/java/com/ruoyi/chargingPile/service/impl/PartnerServiceImpl.java 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-chargingPile/src/main/java/com/ruoyi/chargingPile/service/impl/TAccountingStrategyServiceImpl.java 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-chargingPile/src/main/resources/logback.xml 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-chargingPile/src/main/resources/mapper/chargingPile/SiteMapper.xml 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-chargingPile/src/main/resources/mapper/chargingPile/TChargingGunMapper.xml 22 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-chargingPile/src/main/resources/mapper/chargingPile/TChargingPileMapper.xml 15 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-order/pom.xml 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/controller/TChargingOrderController.java 114 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/controller/TExchangeOrderController.java 49 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/dto/AddChargingOrder.java 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/dto/ChargingDetails.java 42 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/dto/MyChargingOrderList.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/dto/PreChargeCheck.java 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/service/TChargingOrderService.java 41 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/service/impl/TChargingOrderServiceImpl.java 227 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-other/pom.xml 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/RuoYiOtherApplication.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/controller/TActivityController.java 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/controller/TAdvertisingController.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/controller/TCouponController.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/controller/TEnterpriseUserApplicationController.java 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/controller/TGoodsController.java 67 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/controller/TVipController.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/controller/WebSocketController.java 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/query/PassDto.java 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/service/impl/TCouponServiceImpl.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/webSocket/ChildChannelHandler.java 36 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/webSocket/Global.java 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/webSocket/Method.java 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/webSocket/NettyChannelMap.java 123 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/webSocket/NettyMsg.java 94 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/webSocket/NettyServer.java 77 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/webSocket/NettyWebSocketController.java 154 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/webSocket/WebSocketHandler.java 160 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/webSocket/createSSLContext.java 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-other/src/main/resources/mapper/other/TActivityMapper.xml 10 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-other/src/main/resources/mapper/other/TAdvertisingMapper.xml 10 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-other/src/main/resources/mybatis-config.xml 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-payment/pom.xml 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-payment/src/main/java/com/ruoyi/payment/wx/controller/WxPayController.java 76 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-payment/src/main/java/com/ruoyi/payment/wx/model/WxCloseOrderModel.java 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-payment/src/main/java/com/ruoyi/payment/wx/utils/WxAbstractPay.java 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-payment/src/main/java/com/ruoyi/payment/wx/utils/WxV3Pay.java 24 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-api/ruoyi-api-account/src/main/java/com/ruoyi/account/api/dto/UserListQueryDto.java
@@ -18,4 +18,10 @@
    Integer vipTypeId;
    @ApiModelProperty(value = "单位id")
    Integer companyId;
    @ApiModelProperty(value = "到期天数1")
    Integer vipEndTime1;
    @ApiModelProperty(value = "到期天数2")
    Integer vipEndTime2;
}
ruoyi-api/ruoyi-api-account/src/main/java/com/ruoyi/account/api/factory/AppUserVipDetailFallbackFactory.java
New file
@@ -0,0 +1,33 @@
package com.ruoyi.account.api.factory;
import com.ruoyi.account.api.feignClient.AppUserVipDetailClient;
import com.ruoyi.account.api.model.TAppUserVipDetail;
import com.ruoyi.account.api.vo.GetAppUserVipDetail;
import com.ruoyi.common.core.domain.R;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.openfeign.FallbackFactory;
import org.springframework.stereotype.Component;
/**
 *
 * @author ruoyi
 */
@Component
public class AppUserVipDetailFallbackFactory implements FallbackFactory<AppUserVipDetailClient>
{
    private static final Logger log = LoggerFactory.getLogger(AppUserVipDetailFallbackFactory.class);
    @Override
    public AppUserVipDetailClient create(Throwable throwable) {
        log.error("用户会员明细调用失败:{}", throwable.getMessage());
        return new AppUserVipDetailClient() {
            @Override
            public R<TAppUserVipDetail> getAppUserVipDetail(GetAppUserVipDetail getAppUserVipDetail) {
                return R.fail("获取用户当前有效的VIP明细调用失败:" + throwable.getMessage());
            }
        };
    }
}
ruoyi-api/ruoyi-api-account/src/main/java/com/ruoyi/account/api/feignClient/AppCouponClient.java
@@ -9,6 +9,7 @@
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.core.web.page.PageInfo;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
@@ -25,15 +26,15 @@
     * 管理后台远程调用 根据优惠券ids 查询对应的发放数量
     * @return 优惠券ids 查询每个优惠券的发放数量
     */
    @PostMapping("/t-app-coupon/getCountByCouponIds")
    public R<List<Integer>> getCountByCouponIds(String couponIds);
    @PostMapping("/t-app-coupon/getCountByCouponIds/{couponIds}")
    public R<List<Integer>> getCountByCouponIds(@PathVariable("couponIds") String couponIds);
    /**
     * 管理后台远程调用 根据优惠券id 查询对应的使用数量
     * @param couponId
     * @return
     */
    @PostMapping("/t-app-coupon/getUseCountByCouponId")
    public R<Integer> getUseCountByCouponId(Integer couponId);
    @PostMapping("/t-app-coupon/getUseCountByCouponId/{couponId}")
    public R<Integer> getUseCountByCouponId(@PathVariable("couponId")Integer couponId);
    /**
     * 后台远程调用 根据优惠券id 查询领取记录
     * @param couponId
ruoyi-api/ruoyi-api-account/src/main/java/com/ruoyi/account/api/feignClient/AppUserVipDetailClient.java
New file
@@ -0,0 +1,27 @@
package com.ruoyi.account.api.feignClient;
import com.ruoyi.account.api.factory.AppUserVipDetailFallbackFactory;
import com.ruoyi.account.api.model.TAppUserVipDetail;
import com.ruoyi.account.api.vo.GetAppUserVipDetail;
import com.ruoyi.common.core.constant.ServiceNameConstants;
import com.ruoyi.common.core.domain.R;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PostMapping;
/**
 * @author zhibing.pu
 * @Date 2024/8/21 10:27
 */
@FeignClient(contextId = "AppUserVipDetailClient", value = ServiceNameConstants.ACCOUNT_SERVICE, fallbackFactory = AppUserVipDetailFallbackFactory.class)
public interface AppUserVipDetailClient {
    /**
     * 获取用户当前有效的VIP明细
     * @param getAppUserVipDetail
     * @return
     */
    @PostMapping("/appUserVipDetail/getAppUserVipDetail")
    R<TAppUserVipDetail> getAppUserVipDetail(GetAppUserVipDetail getAppUserVipDetail);
}
ruoyi-api/ruoyi-api-account/src/main/java/com/ruoyi/account/api/model/TAppUser.java
@@ -118,5 +118,9 @@
    @TableField(exist = false)
    private Long orderCount;
    @ApiModelProperty("剩余天数")
    @TableField(exist = false)
    private Long lastDays;
}
ruoyi-api/ruoyi-api-account/src/main/java/com/ruoyi/account/api/model/TAppUserVipDetail.java
@@ -38,10 +38,12 @@
    @ApiModelProperty(value = "剩余充电次数")
    @TableField("charge_num")
    private Integer chargeNum;
    @ApiModelProperty(value = "购买时折扣金额")
    @ApiModelProperty(value = "折扣")
    @TableField("discount")
    private BigDecimal discount;
    @ApiModelProperty(value = "最高优惠金额")
    @TableField("discount_money")
    private BigDecimal discountMoney;
ruoyi-api/ruoyi-api-account/src/main/java/com/ruoyi/account/api/vo/GetAppUserVipDetail.java
New file
@@ -0,0 +1,19 @@
package com.ruoyi.account.api.vo;
import lombok.Data;
/**
 * @author zhibing.pu
 * @Date 2024/8/21 10:30
 */
@Data
public class GetAppUserVipDetail {
    /**
     * 用户id
     */
    private Long appUserId;
    /**
     * vipid
     */
    private Integer vipId;
}
ruoyi-api/ruoyi-api-chargingPile/src/main/java/com/ruoyi/chargingPile/api/dto/GetPermissionConfigurationDTO.java
@@ -17,5 +17,5 @@
    @ApiModelProperty(value = "备注")
    private String permissionRemarks;
    @ApiModelProperty(value = "权限菜单")
    private Map<String, Object> menu;
    private List<Map<String, Object>> menu;
}
ruoyi-api/ruoyi-api-chargingPile/src/main/java/com/ruoyi/chargingPile/api/dto/GetSiteListDTO.java
@@ -17,6 +17,8 @@
    private String code;
    @ApiModelProperty(value = "站点名称")
    private String name;
    @ApiModelProperty(value = "合作商id")
    private Integer partnerId;
    @ApiModelProperty(value = "合作商名称")
    private String partnerName;
    @ApiModelProperty(value = "站点类型(0=其他,1=公共,2=个人,3=公交(专业),4=环卫(专用),5=物流(专用),6=出租车(专用))")
ruoyi-api/ruoyi-api-chargingPile/src/main/java/com/ruoyi/chargingPile/api/factory/ChargingPileFallbackFactory.java
@@ -41,6 +41,11 @@
            public R<SiteInfoVO> getSiteInfoByNumber(String number) {
                return R.fail("通过桩编号获取电站信息失败:"+throwable.getMessage());
            }
            @Override
            public R<TChargingPile> getChargingPileById(Integer id) {
                return R.fail(throwable.getMessage());
            }
        };
    }
}
ruoyi-api/ruoyi-api-chargingPile/src/main/java/com/ruoyi/chargingPile/api/feignClient/ChargingPileClient.java
@@ -7,6 +7,7 @@
import com.ruoyi.common.core.domain.R;
import io.swagger.annotations.ApiOperation;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
@@ -37,4 +38,8 @@
    @PostMapping("/site/getSiteInfoByNumber")
    @ApiOperation(value = "扫一扫后通过桩编号获取电站信息", tags = {"小程序-扫一扫"})
    R<SiteInfoVO> getSiteInfoByNumber(@RequestParam("number") String number);
    @PostMapping("/t-charging-pile/getChargingPileById/{id}")
    R<TChargingPile> getChargingPileById(@PathVariable("id") Integer id);
}
ruoyi-api/ruoyi-api-chargingPile/src/main/java/com/ruoyi/chargingPile/api/model/Site.java
@@ -10,6 +10,7 @@
import lombok.Data;
import java.time.LocalDate;
import java.time.LocalDateTime;
/**
 * @author zhibing.pu
@@ -204,7 +205,7 @@
    @TableField("establishment_time")
    @ApiModelProperty(value = "建站时间", notes = "yyyy-MM-dd HH:mm:ss")
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private LocalDate establishmentTime;
    private LocalDateTime establishmentTime;
    /**
     * 排序
     */
ruoyi-api/ruoyi-api-chargingPile/src/main/java/com/ruoyi/chargingPile/api/model/TChargingPile.java
@@ -58,7 +58,7 @@
    
    @ApiModelProperty(value = "站点名称")
    @TableField(exist = false)
    private Integer siteName;
    private String siteName;
    @ApiModelProperty(value = "合作商id", required = true)
    @TableField("partner_id")
@@ -66,7 +66,7 @@
    
    @ApiModelProperty(value = "合作商名称")
    @TableField(exist = false)
    private Integer partnerName;
    private String partnerName;
    @ApiModelProperty(value = "生产商机构代码")
    @TableField("manufacturer_code")
ruoyi-api/ruoyi-api-chargingPile/src/main/java/com/ruoyi/chargingPile/api/vo/SiteInfoVO.java
@@ -14,6 +14,8 @@
    private String name;
    @ApiModelProperty(value = "桩编号")
    private String number;
    @ApiModelProperty(value = "充电枪id")
    private Integer chargingGunId;
    @ApiModelProperty(value = "普通电价")
    private BigDecimal electrovalence;
    @ApiModelProperty(value = "会员电价")
ruoyi-api/ruoyi-api-chargingPile/src/main/java/com/ruoyi/chargingPile/api/vo/TChargingGunVO.java
@@ -11,23 +11,23 @@
    @ApiModelProperty(value = "策略名称")
    private String strategyName;
    @ApiModelProperty(value = "编辑权限")
    private boolean authUpdate = true;
    @ApiModelProperty(value = "删除权限")
    private boolean authDelete = true;
    @ApiModelProperty(value = "查看费率权限")
    private boolean authViewRates = true;
    @ApiModelProperty(value = "编辑权限")
    @ApiModelProperty(value = "下载二维码权限")
    private boolean authDownloadQRCode = true;
    @ApiModelProperty(value = "编辑权限")
    @ApiModelProperty(value = "查看详情权限")
    private boolean authQueryInfo = true;
    @ApiModelProperty(value = "编辑权限")
    @ApiModelProperty(value = "结束充电权限")
    private boolean authEndCharge = true;
}
ruoyi-api/ruoyi-api-chargingPile/src/main/resources/template/充电桩申请记录.xlsx
Binary files differ
ruoyi-api/ruoyi-api-order/src/main/java/com/ruoyi/order/api/factory/OrderFallbackFactory.java
@@ -1,13 +1,10 @@
package com.ruoyi.order.api.factory;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.order.api.feignClient.ChargingOrderClient;
import com.ruoyi.common.core.dto.ExchangeDto;
import com.ruoyi.order.api.feignClient.OrderClient;
import com.ruoyi.order.api.model.TChargingOrder;
import com.ruoyi.order.api.model.TExchangeOrder;
import com.ruoyi.order.api.query.TActivityStatisticsQuery;
import com.ruoyi.order.api.vo.TActivityVO;
import com.ruoyi.other.api.dto.ExchangeDto;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.openfeign.FallbackFactory;
@@ -43,7 +40,7 @@
            }
            @Override
            public R<Long> getExchangeById(Integer goodsIds, Long userId) {
            public R<Long> getExchangeById(Integer goodsIds, Long userId,Integer goodType) {
                return R.fail("根据订单数" + cause.getMessage());
            }
ruoyi-api/ruoyi-api-order/src/main/java/com/ruoyi/order/api/feignClient/OrderClient.java
@@ -2,14 +2,11 @@
import com.ruoyi.common.core.constant.ServiceNameConstants;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.order.api.factory.ChargingOrderFallbackFactory;
import com.ruoyi.common.core.dto.ExchangeDto;
import com.ruoyi.order.api.factory.OrderFallbackFactory;
import com.ruoyi.order.api.model.TExchangeOrder;
import com.ruoyi.order.api.query.TActivityStatisticsQuery;
import com.ruoyi.order.api.vo.TActivityVO;
import com.ruoyi.other.api.dto.ExchangeDto;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
@@ -35,7 +32,8 @@
    public R<List<String>> getCodeByOrderId(String goodsIds);
    //查询已兑换的订单数
    @PostMapping("/t-exchange-order/getById")
    public R<Long> getExchangeById(@RequestParam("goodId") Integer goodId,@RequestParam("userId")  Long userId);
    public R<Long> getExchangeById(@RequestParam("goodId") Integer goodId,@RequestParam("userId")  Long userId,@RequestParam("goodType") Integer goodType);
    @PostMapping("/t-exchange-order/create")
    public R<Long> exchangeCreate(@RequestBody ExchangeDto exchangeDto);
ruoyi-api/ruoyi-api-order/src/main/java/com/ruoyi/order/api/model/TChargingOrder.java
@@ -86,7 +86,7 @@
    @TableField("end_time")
    private LocalDateTime endTime;
    @ApiModelProperty(value = "状态(0=未知,1=等待中,2=启动中,3=充电中,4=停止中,5=已结束)")
    @ApiModelProperty(value = "状态(0=未知,1=等待中/已插枪,2=启动中,3=充电中,4=停止中,5=已结束)")
    @TableField("status")
    private Integer status;
    
@@ -110,7 +110,7 @@
    @TableField("recharge_serial_number")
    private String rechargeSerialNumber;
    @ApiModelProperty(value = "订单金额")
    @ApiModelProperty(value = "订单金额(总金额)")
    @TableField("order_amount")
    private BigDecimal orderAmount;
ruoyi-api/ruoyi-api-order/src/main/java/com/ruoyi/order/api/query/ChargingOrderQuery.java
New file
@@ -0,0 +1,34 @@
package com.ruoyi.order.api.query;
import com.ruoyi.common.core.web.page.BasePage;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.time.LocalDateTime;
import java.util.List;
@Data
@ApiModel(value = "ChargingOrderQuery对象",description = "管理后台充电订单查询对象")
public class ChargingOrderQuery extends BasePage {
    @ApiModelProperty(value = "订单编号")
    private String code;
    @ApiModelProperty(value = "订单类型(1=充电订单(小程序),2=充电订单(刷卡))")
    private Integer orderType;
    @ApiModelProperty(value = "手机号")
    private String phone;
    @ApiModelProperty(value = "状态(0=未知,1=等待中,2=启动中,3=充电中,4=停止中,5=已结束)")
    private Integer status;
    @ApiModelProperty(value = "站点id")
    private Integer siteId;
    @ApiModelProperty(value = "充电桩ids 逗号拼接")
    private String pileIds;
    @ApiModelProperty(value = "充电枪ids 逗号拼接")
    private String gunIds;
    @ApiModelProperty(value = "开始时间2020-01-01 00:00:00 - 2021-01-01 23:59:59")
    private String startTime;
    @ApiModelProperty(value = "结束时间2020-01-01 00:00:00 - 2021-01-01 23:59:59")
    private String endTime;
    @ApiModelProperty(value = "订单来源 1明星 2快电 3新电途")
    private Integer orderSource;
}
ruoyi-api/ruoyi-api-order/src/main/java/com/ruoyi/order/api/vo/ChargingOrderVO.java
New file
@@ -0,0 +1,44 @@
package com.ruoyi.order.api.vo;
import com.baomidou.mybatisplus.annotation.TableField;
import com.ruoyi.order.api.model.TChargingOrder;
import com.ruoyi.order.api.model.TOrderEvaluate;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.math.BigDecimal;
import java.util.List;
@Data
@ApiModel(value = "SiteDetailEvaluateVO对象",description = "站点详情订单评价")
public class ChargingOrderVO extends TChargingOrder {
    @ApiModelProperty(value = "订单id")
    private Long id;
    @ApiModelProperty(value = "用户id")
    private Long userId;
    @ApiModelProperty(value = "站点名称")
    private String siteName;
    @ApiModelProperty(value = "充电终端名称 桩+枪")
    private String terminalName;
    @ApiModelProperty(value = "充电时间 秒")
    private Integer chargingSecond;
    @ApiModelProperty(value = "平台手续费")
    private BigDecimal platFormMoney;
    @ApiModelProperty(value = "三方平台分佣")
    private BigDecimal commission;
    @ApiModelProperty(value = "车牌号")
    private String licensePlate;
    @ApiModelProperty(value = "手机号")
    private String phone;
    @ApiModelProperty(value = "累计充电量")
    private BigDecimal chargingCapacity;
    @ApiModelProperty(value = "累计电费")
    private BigDecimal electrovalence;
    @ApiModelProperty(value = "累计服务费")
    private BigDecimal serviceCharge;
    @ApiModelProperty(value = "时段数")
    private Integer count;
    @ApiModelProperty(value = "1明星充电 2快电 3新电途")
    private Integer orderSource;
}
ruoyi-api/ruoyi-api-other/src/main/java/com/ruoyi/other/api/domain/TCoupon.java
@@ -106,8 +106,8 @@
    private BigDecimal vipPaymentAmount;
    @ApiModelProperty(value = "优惠券说明")
    @TableField("explain")
    private String explain;
    @TableField("explains")
    private String explains;
    @ApiModelProperty(value = "说明")
    @TableField("remark")
ruoyi-api/ruoyi-api-other/src/main/java/com/ruoyi/other/api/domain/TGoods.java
@@ -103,5 +103,8 @@
    @ApiModelProperty(value = "销量")
    @TableField(exist = false)
    private Integer salesCount;
    @ApiModelProperty(value = "1商品2优惠卷")
    @TableField(exist = false)
    private Integer goodType;
}
ruoyi-api/ruoyi-api-other/src/main/java/com/ruoyi/other/api/domain/TRoleSite.java
@@ -24,7 +24,7 @@
@EqualsAndHashCode(callSuper = false)
@TableName("t_role_site")
@ApiModel(value="TRoleSite对象", description="")
public class TRoleSite extends BasePojo {
public class TRoleSite {
    private static final long serialVersionUID = 1L;
ruoyi-api/ruoyi-api-other/src/main/java/com/ruoyi/other/api/domain/TUserSite.java
@@ -24,7 +24,7 @@
@EqualsAndHashCode(callSuper = false)
@TableName("t_user_site")
@ApiModel(value="TUserSite对象", description="")
public class TUserSite extends BasePojo {
public class TUserSite {
    private static final long serialVersionUID = 1L;
ruoyi-api/ruoyi-api-other/src/main/java/com/ruoyi/other/api/dto/ExchangeDto.java
@@ -1,4 +1,4 @@
package com.ruoyi.other.api.dto;
package com.ruoyi.common.core.dto;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
@@ -13,7 +13,11 @@
    String remark;
    @ApiModelProperty(hidden = true)
    Long userId;
    @ApiModelProperty("1商品2优惠卷")
    Integer goodType;
    @ApiModelProperty(hidden = true)
    Integer userId;
    Integer point;
}
ruoyi-api/ruoyi-api-other/src/main/java/com/ruoyi/other/api/dto/WebSocketMsg.java
New file
@@ -0,0 +1,19 @@
package com.ruoyi.other.api.dto;
import lombok.Data;
/**
 * @author zhibing.pu
 * @Date 2024/8/21 17:43
 */
@Data
public class WebSocketMsg {
    /**
     * 用户id
     */
    private Long userId;
    /**
     * 消息内容
     */
    private String msg;
}
ruoyi-api/ruoyi-api-other/src/main/java/com/ruoyi/other/api/factory/UserSiteFallbackFactory.java
@@ -31,12 +31,12 @@
    
            @Override
            public R addUserSite(List<TUserSite> userSite) {
                return R.fail("添加用户站点失败:" + throwable.getMessage());
                throw new RuntimeException("添加用户站点失败:" + throwable.getMessage());
            }
    
            @Override
            public R delUserSite(Long userId) {
                return R.fail("删除用户站点失败:" + throwable.getMessage());
                throw new RuntimeException("删除用户站点失败:" + throwable.getMessage());
            }
        };
    }
ruoyi-api/ruoyi-api-other/src/main/java/com/ruoyi/other/api/factory/WebSocketFallbackFactory.java
New file
@@ -0,0 +1,33 @@
package com.ruoyi.other.api.factory;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.other.api.domain.TVip;
import com.ruoyi.other.api.dto.WebSocketMsg;
import com.ruoyi.other.api.feignClient.VipClient;
import com.ruoyi.other.api.feignClient.WebSocketClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.openfeign.FallbackFactory;
import org.springframework.stereotype.Component;
/**
 * 会员服务降级处理
 *
 * @author ruoyi
 */
@Component
public class WebSocketFallbackFactory implements FallbackFactory<WebSocketClient> {
    private static final Logger log = LoggerFactory.getLogger(WebSocketFallbackFactory.class);
    @Override
    public WebSocketClient create(Throwable throwable) {
        log.error("WebSocket调用失败:{}", throwable.getMessage());
        return new WebSocketClient() {
            @Override
            public R send(WebSocketMsg webSocketMsg) {
                return R.fail("发送WebSocket消息失败:" + throwable.getMessage());
            }
        };
    }
}
ruoyi-api/ruoyi-api-other/src/main/java/com/ruoyi/other/api/feignClient/OtherClient.java
@@ -74,4 +74,6 @@
}
ruoyi-api/ruoyi-api-other/src/main/java/com/ruoyi/other/api/feignClient/WebSocketClient.java
New file
@@ -0,0 +1,24 @@
package com.ruoyi.other.api.feignClient;
import com.ruoyi.common.core.constant.ServiceNameConstants;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.other.api.dto.WebSocketMsg;
import com.ruoyi.other.api.factory.WebSocketFallbackFactory;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PostMapping;
/**
 * @author zhibing.pu
 * @Date 2024/8/21 17:41
 */
@FeignClient(contextId = "WebSocketClient", value = ServiceNameConstants.ORDER_SERVICE, fallbackFactory = WebSocketFallbackFactory.class)
public interface WebSocketClient {
    /**
     * 发送WebSocket消息
     * @return
     */
    @PostMapping("/webSocket/send")
    R send(WebSocketMsg webSocketMsg);
}
ruoyi-api/ruoyi-api-other/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
@@ -6,4 +6,5 @@
com.ruoyi.other.api.factory.IntegralRuleFallbackFactory
com.ruoyi.other.api.factory.GoodsFallbackFactory
com.ruoyi.other.api.factory.CouponFallbackFactory
com.ruoyi.other.api.factory.InvoiceTypeFallbackFactory
com.ruoyi.other.api.factory.InvoiceTypeFallbackFactory
com.ruoyi.other.api.factory.WebSocketFallbackFactory
ruoyi-api/ruoyi-api-payment/src/main/java/com/ruoyi/payment/api/factory/ChargingOrderFallbackFactory.java
File was deleted
ruoyi-api/ruoyi-api-payment/src/main/java/com/ruoyi/payment/api/factory/WxPaymentFallbackFactory.java
New file
@@ -0,0 +1,51 @@
package com.ruoyi.payment.api.factory;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.payment.api.feignClient.WxPaymentClient;
import com.ruoyi.payment.api.vo.NotifyV3PayDecodeRespBody;
import com.ruoyi.payment.api.vo.PaymentOrder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.openfeign.FallbackFactory;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import java.util.Map;
/**
 * 充电订单服务降级处理
 *
 * @author ruoyi
 */
@Component
public class WxPaymentFallbackFactory implements FallbackFactory<WxPaymentClient>
{
    private static final Logger log = LoggerFactory.getLogger(WxPaymentFallbackFactory.class);
    @Override
    public WxPaymentClient create(Throwable throwable) {
        log.error("微信支付调用失败:{}", throwable.getMessage());
        return new WxPaymentClient() {
            @Override
            public R<NotifyV3PayDecodeRespBody> queryOrderInfo(String orderId) {
                return R.fail("查询支付订单信息失败:" + throwable.getMessage());
            }
            @Override
            public R<Map<String, Object>> orderPay(PaymentOrder paymentOrder) {
                throw new RuntimeException("调起微信支付失败:" + throwable.getMessage());
            }
            @Override
            public R<Map<String, Object>> payNotify(HttpServletRequest request) {
                return R.fail("微信支付回调失败:" + throwable.getMessage());
            }
            @Override
            public void ack() {
            }
        };
    }
}
ruoyi-api/ruoyi-api-payment/src/main/java/com/ruoyi/payment/api/feignClient/ChargingOrderClient.java
File was deleted
ruoyi-api/ruoyi-api-payment/src/main/java/com/ruoyi/payment/api/feignClient/WxPaymentClient.java
New file
@@ -0,0 +1,62 @@
package com.ruoyi.payment.api.feignClient;
import com.ruoyi.common.core.constant.ServiceNameConstants;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.payment.api.factory.WxPaymentFallbackFactory;
import com.ruoyi.payment.api.vo.NotifyV3PayDecodeRespBody;
import com.ruoyi.payment.api.vo.PaymentOrder;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import javax.servlet.http.HttpServletRequest;
import java.util.Map;
/**
 * 充电订单服务
 * @author ruoyi
 */
@FeignClient(contextId = "WxPaymentClient", value = ServiceNameConstants.PAYMENT_SERVICE, fallbackFactory = WxPaymentFallbackFactory.class)
public interface WxPaymentClient {
    /**
     * 查询支付订单信息
     * @param orderId
     * @return
     */
    @PostMapping("/wx/query/queryOrderInfo")
    R<NotifyV3PayDecodeRespBody> queryOrderInfo(String orderId);
    /**
     * 订单支付
     * @param paymentOrder
     * @return
     */
    @PostMapping("/wx/orderPay")
    R<Map<String, Object>> orderPay(@RequestBody PaymentOrder paymentOrder);
    /**
     * 支付回调
     * @param request
     * @return
     */
    @PostMapping("/wx/pay/notify")
    R<Map<String, Object>> payNotify(HttpServletRequest request);
    /**
     * 支付回调成功后的成功应答
     */
    @PostMapping("/wx/pay/ack")
    void ack();
    /**
     * 关闭订单
     * @param outTradeNo
     */
    @PostMapping("/wx/pay/close")
    void close(String outTradeNo);
}
ruoyi-api/ruoyi-api-payment/src/main/java/com/ruoyi/payment/api/vo/NotifyV3PayDecodeRespBody.java
New file
@@ -0,0 +1,222 @@
package com.ruoyi.payment.api.vo;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
import java.util.List;
/**
 * @author xiaochen
 * @ClassName FacilV3PayNotifyRespBody
 * @Description
 */
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Data
public class NotifyV3PayDecodeRespBody implements Serializable {
    // 合单--开始
    private String combine_appid;
    private String combine_mchid;
    private String combine_out_trade_no;
    private List<SubOrders> sub_orders;
    // 合单--结束
    /**
     * 服务商应用ID
     */
    private String sp_appid;
    /**
     * 服务商户号
     */
    private String sp_mchid;
    /**
     * 商户号
     */
    private String mchid;
    /**
     * 子商户应用ID
     */
    private String sub_appid;
    /**
     * 子商户号
     */
    private String sub_mchid;
    /**
     * 商户订单号
     */
    private String out_trade_no;
    /**
     * 交易状态描述
     */
    private String trade_state_desc;
    /**
     * 交易类型,枚举值:
     * JSAPI:公众号支付
     * NATIVE:扫码支付
     * APP:APP支付
     * MICROPAY:付款码支付
     * MWEB:H5支付
     * FACEPAY:刷脸支付
     */
    private String trade_type;
    /**
     * 附加数据,在查询API和支付通知中原样返回,可作为自定义参数使用
     */
    private String attach;
    /**
     * 微信支付订单号
     */
    private String transaction_id;
    /**
     * 交易状态,枚举值:
     * SUCCESS:支付成功
     * REFUND:转入退款
     * NOTPAY:未支付
     * CLOSED:已关闭
     * REVOKED:已撤销(付款码支付)
     * USERPAYING:用户支付中(付款码支付)
     * PAYERROR:支付失败(其他原因,如银行返回失败)
     */
    private String trade_state;
    /**
     * 银行类型,采用字符串类型的银行标识。银行标识请参考《银行类型对照表》
     * https://pay.weixin.qq.com/wiki/doc/apiv3_partner/terms_definition/chapter1_1_3.shtml#part-6
     */
    private String bank_type;
    /**
     * 支付完成时间,遵循rfc3339标准格式,格式为YYYY-MM-DDTHH:mm:ss+TIMEZONE,
     * YYYY-MM-DD表示年月日,T出现在字符串中,表示time元素的开头,HH:mm:ss表示时分秒,
     * TIMEZONE表示时区(+08:00表示东八区时间,领先UTC 8小时,即北京时间)。
     * 例如:2015-05-20T13:29:35+08:00表示,北京时间2015年5月20日 13点29分35秒。
     * 示例值:2018-06-08T10:34:56+08:00
     */
    private String success_time;
    /**
     * 支付者信息
     */
    private Payer payer;
    /**
     * 支付者
     */
    private Payer combine_payer_info;
    /**
     * 订单金额信息
     */
    private Amount amount;
    /**
     * 场景信息
     */
    private SceneInfo scene_info;
    /**
     * 优惠功能,享受优惠时返回该字段
     */
    private List<PromotionDetail> promotion_detail;
    @Builder
    @NoArgsConstructor
    @AllArgsConstructor
    @Data
    public static class Amount implements Serializable{
        /**
         * 用户支付金额
         */
        private int payer_total;
        /**
         * 总金额
         */
        private int total;
        /**
         * 标价金额
         */
        private int total_amount;
        /**
         * 现金支付金额
         */
        private int payer_amount;
        private String currency;
        private String payer_currency;
    }
    @Builder
    @NoArgsConstructor
    @AllArgsConstructor
    @Data
    public static class GoodsDetail implements Serializable{
        private String goods_id;
        private int quantity;
        private int unit_price;
        private int discount_amount;
        private String goods_remark;
    }
    @Builder
    @NoArgsConstructor
    @AllArgsConstructor
    @Data
    public static class Payer implements Serializable{
        private String openid;
        private String sp_openid;
        private String sub_openid;
    }
    @Builder
    @NoArgsConstructor
    @AllArgsConstructor
    @Data
    public static class PromotionDetail implements Serializable{
        private String coupon_id;
        private String name;
        private String scope;
        private String type;
        private int amount;
        private String stock_id;
        private int wechatpay_contribute;
        private int merchant_contribute;
        private int other_contribute;
        private String currency;
        private List<GoodsDetail> goods_detail;
    }
    @Builder
    @NoArgsConstructor
    @AllArgsConstructor
    @Data
    public static class SceneInfo implements Serializable{
        /**
         * 商户端设备号
         */
        private String device_id;
    }
    @Builder
    @NoArgsConstructor
    @AllArgsConstructor
    @Data
    public static class SubOrders implements Serializable{
        private String mchid;
        private String trade_type;
        private String trade_state;
        private String trade_state_desc;
        private String bank_type;
        private String attach;
        private String success_time;
        private String transaction_id;
        private String out_trade_no;
        private String sub_mchid;
        private Amount amount;
        /**
         * 优惠功能,享受优惠时返回该字段
         */
        private List<PromotionDetail> promotion_detail;
    }
}
ruoyi-api/ruoyi-api-payment/src/main/java/com/ruoyi/payment/api/vo/PaymentOrder.java
New file
@@ -0,0 +1,33 @@
package com.ruoyi.payment.api.vo;
import lombok.Data;
import java.math.BigDecimal;
/**
 * @author zhibing.pu
 * @Date 2024/8/21 9:06
 */
@Data
public class PaymentOrder {
    /**
     * 订单流水号
     */
    private String code;
    /**
     * 支付金额
     */
    private BigDecimal amount;
    /**
     * openid
     */
    private String openId;
    /**
     * 描述
     */
    private String description;
    /**
     * 回调地址
     */
    private String notifyUrl;
}
ruoyi-api/ruoyi-api-system/src/main/java/com/ruoyi/system/api/domain/SysOperLog.java
@@ -2,254 +2,158 @@
import java.util.Date;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.ruoyi.common.core.annotation.Excel;
import com.ruoyi.common.core.annotation.Excel.ColumnType;
import com.ruoyi.common.core.web.domain.BaseEntity;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
 * 操作日志记录表 oper_log
 *
 * @author ruoyi
 */
public class SysOperLog extends BaseEntity {
@Data
@ApiModel
@TableName("sys_oper_log")
public class SysOperLog {
    private static final long serialVersionUID = 1L;
    /**
     * 日志主键
     */
    @Excel(name = "操作序号", cellType = ColumnType.NUMERIC)
    @ApiModelProperty("数据id")
    @TableId(value = "oper_id", type = IdType.AUTO)
    private Long operId;
    /**
     * 操作模块
     */
    @Excel(name = "操作模块")
    @ApiModelProperty("操作模块")
    private String title;
    /**
     * 业务类型(0其它 1新增 2修改 3删除)
     */
    @Excel(name = "业务类型", readConverterExp = "0=其它,1=新增,2=修改,3=删除,4=授权,5=导出,6=导入,7=强退,8=生成代码,9=清空数据")
    @ApiModelProperty("0=其它,1=新增,2=修改,3=删除,4=授权,5=导出,6=导入,7=强退,8=生成代码,9=清空数据")
    @TableField("business_type")
    private Integer businessType;
    /**
     * 业务类型数组
     */
    @TableField(exist = false)
    private Integer[] businessTypes;
    /**
     * 请求方法
     */
    @Excel(name = "请求方法")
    @ApiModelProperty("请求方法")
    private String method;
    /**
     * 请求方式
     */
    @Excel(name = "请求方式")
    @ApiModelProperty("请求方式")
    @TableField("request_method")
    private String requestMethod;
    /**
     * 操作类别(0其它 1后台用户 2手机端用户)
     */
    @Excel(name = "操作类别", readConverterExp = "0=其它,1=后台用户,2=手机端用户")
    @ApiModelProperty("0=其它,1=后台用户,2=手机端用户")
    @TableField("operator_type")
    private Integer operatorType;
    /**
     * 操作人员
     */
    @Excel(name = "操作人员")
    @ApiModelProperty("操作人员")
    @TableField("oper_name")
    private String operName;
    /**
     * 部门名称
     */
    @Excel(name = "部门名称")
    @ApiModelProperty("部门名称")
    @TableField("dept_name")
    private String deptName;
    /**
     * 请求url
     */
    @Excel(name = "请求地址")
    @ApiModelProperty("请求地址")
    @TableField("oper_url")
    private String operUrl;
    /**
     * 操作地址
     */
    @Excel(name = "操作地址")
    @ApiModelProperty("操作地址")
    @TableField("oper_ip")
    private String operIp;
    /**
     * 请求参数
     */
    @Excel(name = "请求参数")
    @ApiModelProperty("请求参数")
    @TableField("oper_param")
    private String operParam;
    /**
     * 返回参数
     */
    @Excel(name = "返回参数")
    @ApiModelProperty("返回参数")
    @TableField("json_result")
    private String jsonResult;
    /**
     * 操作状态(0正常 1异常)
     */
    @Excel(name = "状态", readConverterExp = "0=正常,1=异常")
    @ApiModelProperty("0=正常,1=异常")
    private Integer status;
    /**
     * 错误消息
     */
    @Excel(name = "错误消息")
    @ApiModelProperty("错误消息")
    @TableField("error_msg")
    private String errorMsg;
    /**
     * 操作时间
     */
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    @Excel(name = "操作时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
    @ApiModelProperty("操作时间")
    @TableField("oper_time")
    private Date operTime;
    /**
     * 消耗时间
     */
    @Excel(name = "消耗时间", suffix = "毫秒")
    @ApiModelProperty("消耗时间")
    @TableField("cost_time")
    private Long costTime;
    public Long getOperId() {
        return operId;
    }
    public void setOperId(Long operId) {
        this.operId = operId;
    }
    public String getTitle() {
        return title;
    }
    public void setTitle(String title) {
        this.title = title;
    }
    public Integer getBusinessType() {
        return businessType;
    }
    public void setBusinessType(Integer businessType) {
        this.businessType = businessType;
    }
    public Integer[] getBusinessTypes() {
        return businessTypes;
    }
    public void setBusinessTypes(Integer[] businessTypes) {
        this.businessTypes = businessTypes;
    }
    public String getMethod() {
        return method;
    }
    public void setMethod(String method) {
        this.method = method;
    }
    public String getRequestMethod() {
        return requestMethod;
    }
    public void setRequestMethod(String requestMethod) {
        this.requestMethod = requestMethod;
    }
    public Integer getOperatorType() {
        return operatorType;
    }
    public void setOperatorType(Integer operatorType) {
        this.operatorType = operatorType;
    }
    public String getOperName() {
        return operName;
    }
    public void setOperName(String operName) {
        this.operName = operName;
    }
    public String getDeptName() {
        return deptName;
    }
    public void setDeptName(String deptName) {
        this.deptName = deptName;
    }
    public String getOperUrl() {
        return operUrl;
    }
    public void setOperUrl(String operUrl) {
        this.operUrl = operUrl;
    }
    public String getOperIp() {
        return operIp;
    }
    public void setOperIp(String operIp) {
        this.operIp = operIp;
    }
    public String getOperParam() {
        return operParam;
    }
    public void setOperParam(String operParam) {
        this.operParam = operParam;
    }
    public String getJsonResult() {
        return jsonResult;
    }
    public void setJsonResult(String jsonResult) {
        this.jsonResult = jsonResult;
    }
    public Integer getStatus() {
        return status;
    }
    public void setStatus(Integer status) {
        this.status = status;
    }
    public String getErrorMsg() {
        return errorMsg;
    }
    public void setErrorMsg(String errorMsg) {
        this.errorMsg = errorMsg;
    }
    public Date getOperTime() {
        return operTime;
    }
    public void setOperTime(Date operTime) {
        this.operTime = operTime;
    }
    public Long getCostTime() {
        return costTime;
    }
    public void setCostTime(Long costTime) {
        this.costTime = costTime;
    }
}
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/constant/UserConstants.java
@@ -76,5 +76,5 @@
     */
    public static final int PASSWORD_MIN_LENGTH = 5;
    public static final int PASSWORD_MAX_LENGTH = 20;
    public static final int PASSWORD_MAX_LENGTH = 40;
}
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/OrderCodeUtil.java
New file
@@ -0,0 +1,26 @@
package com.ruoyi.common.core.utils;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Random;
public class OrderCodeUtil {
    public static String getOrderCode(String code){
        LocalDateTime now = LocalDateTime.now();
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMddHHmmss");
        String formattedDate = now.format(formatter);
        // 四位随机数
        Random random = new Random();
        int randomNumber = random.nextInt(9999); // 生成0-9998之间的随机数
        String formattedRandomNumber = String.format("%04d", randomNumber); // 补齐到4位
        // 拼接日期和随机数
        return code+formattedDate + formattedRandomNumber;
    }
    public static void main(String[] args) {
        // 测试方法
        System.out.println(getOrderCode("DH"));
    }
}
ruoyi-common/ruoyi-common-security/src/main/java/com/ruoyi/common/security/utils/SecurityUtils.java
@@ -114,4 +114,11 @@
        BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
        return passwordEncoder.matches(rawPassword, encodedPassword);
    }
    public static void main(String[] args) {
        BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
        String encode = passwordEncoder.encode("49a15811ea47e8e9c6d8f3ef4d7bbc54");
        System.err.println(encode);
    }
}
ruoyi-gateway/src/main/java/com/ruoyi/gateway/filter/AuthFilter.java
@@ -69,20 +69,20 @@
            return chain.filter(exchange);
        }
        //防抖校验
        try {
            antiShake(request);
        }catch (Exception e){
            log.error("[重复提交]请求路径:{}", exchange.getRequest().getPath());
            return ServletUtils.webFluxResponseWriter(exchange.getResponse(), e.getMessage(), HttpStatus.SUCCESS);
        }
//        try {
//            antiShake(request);
//        }catch (Exception e){
//            log.error("[重复提交]请求路径:{}", exchange.getRequest().getPath());
//            return ServletUtils.webFluxResponseWriter(exchange.getResponse(), e.getMessage(), HttpStatus.SUCCESS);
//        }
        
        //校验账户是否有效
        try {
            verifyToken(request);
            verifyAccount(request);
        }catch (Exception e){
            return unauthorizedResponse(exchange, e.getMessage());
        }
//        try {
//            verifyToken(request);
//            verifyAccount(request);
//        }catch (Exception e){
//            return unauthorizedResponse(exchange, e.getMessage());
//        }
        String token = getToken(request);
        Claims claims = JwtUtils.parseToken(token);
        String userkey = JwtUtils.getUserKey(claims);
ruoyi-gateway/src/main/java/com/ruoyi/gateway/filter/RequestParamGlobalFilter.java
@@ -6,6 +6,7 @@
import com.ruoyi.common.core.utils.ServletUtils;
import com.ruoyi.common.core.utils.StringUtils;
import com.ruoyi.common.core.utils.html.EscapeUtil;
import com.ruoyi.gateway.config.properties.IgnoreWhiteProperties;
import com.ruoyi.gateway.config.properties.XssProperties;
import io.netty.buffer.ByteBufAllocator;
import org.apache.commons.codec.binary.Base64;
@@ -42,10 +43,24 @@
 */
@Component
public class RequestParamGlobalFilter implements GlobalFilter, Ordered {
    @Autowired
    private IgnoreWhiteProperties ignoreWhite;
    
    
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        ServerHttpRequest.Builder mutate = request.mutate();
        HttpMethod method = request.getMethod();
        if(method != HttpMethod.POST){
            return chain.filter(exchange.mutate().request(mutate.build()).build());
        }
        // 跳过不需要验证的路径
        String url = request.getURI().getPath();
        if (StringUtils.matches(url, ignoreWhite.getWhites())) {
            return chain.filter(exchange);
        }
        ServerHttpRequestDecorator httpRequestDecorator = requestDecorator(exchange);
        return chain.filter(exchange.mutate().request(httpRequestDecorator).build());
    }
ruoyi-gateway/src/main/java/com/ruoyi/gateway/filter/SignFilter.java
@@ -51,6 +51,9 @@
    @Value("${security.sign}")
    private boolean parameter_signature;
    @Autowired
    private IgnoreWhiteProperties ignoreWhite;
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
@@ -61,6 +64,11 @@
        if(method != HttpMethod.POST){
            return chain.filter(exchange.mutate().request(mutate.build()).build());
        }
        // 跳过不需要验证的路径
        String url = request.getURI().getPath();
        if (StringUtils.matches(url, ignoreWhite.getWhites())) {
            return chain.filter(exchange);
        }
        String sign = request.getHeaders().getFirst(TokenConstants.SIGN);
        String nonce_str = request.getHeaders().getFirst(TokenConstants.NONCE_STR);
        if (parameter_signature && StringUtils.isEmpty(sign)) {
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/controller/SysUserController.java
@@ -25,6 +25,7 @@
import com.ruoyi.system.domain.vo.CompanyUserVo;
import com.ruoyi.system.domain.vo.TCompanyToUserVo;
import com.ruoyi.system.query.GetSysUserList;
import com.ruoyi.system.query.ShopUserStart;
import com.ruoyi.system.service.*;
import io.seata.spring.annotation.GlobalTransactional;
import io.swagger.annotations.Api;
@@ -174,6 +175,9 @@
    public AjaxResult<List<String>> getRoleSiteName(@PathVariable Integer roleId){
        List<Integer> ids = roleSiteClient.getSiteIds(roleId.longValue()).getData();
        List<Site> data = siteClient.getSiteByIds(ids).getData();
        if(null == data){
            return AjaxResult.success(new ArrayList<>());
        }
        List<String> siteNames = data.stream().map(Site::getName).collect(Collectors.toList());
        return AjaxResult.success(siteNames);
    }
@@ -289,14 +293,14 @@
    @PostMapping("/shopUserStart")
    @ApiOperation(value = "账号管理--禁用/启用", tags = {"管理后台-系统用户管理"})
    public AjaxResult shopUserStart(@RequestParam("userId") Long userId, @RequestParam("remark") String remark) {
        if (userId == null) {
    public AjaxResult shopUserStart(@RequestBody ShopUserStart shopUserStart) {
        if (shopUserStart.getUserId() == null) {
            return AjaxResult.error("userId不能为空");
        }
        SysUser sysUser = userService.selectUserById(userId);
        SysUser sysUser = userService.selectUserById(shopUserStart.getUserId());
        if (sysUser.getStatus().equals("0")) {
            sysUser.setStatus("1");
            sysUser.setRemark(remark);
            sysUser.setRemark(shopUserStart.getRemark());
        } else {
            sysUser.setStatus("0");
            sysUser.setRemark("");
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/query/ShopUserStart.java
New file
@@ -0,0 +1,18 @@
package com.ruoyi.system.query;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
 * @author zhibing.pu
 * @date 2024/8/21 20:24
 */
@Data
@ApiModel
public class ShopUserStart {
    @ApiModelProperty("用户id")
    private Long userId;
    @ApiModelProperty("注释")
    private String remark;
}
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysMenuServiceImpl.java
@@ -14,10 +14,7 @@
import com.ruoyi.system.domain.vo.MetaVo;
import com.ruoyi.system.domain.vo.RouterVo;
import com.ruoyi.system.domain.vo.TreeSelect;
import com.ruoyi.system.mapper.SysMenuMapper;
import com.ruoyi.system.mapper.SysRoleMapper;
import com.ruoyi.system.mapper.SysRoleMenuMapper;
import com.ruoyi.system.mapper.SysUserRoleMapper;
import com.ruoyi.system.mapper.*;
import com.ruoyi.system.service.ISysMenuService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@@ -27,6 +24,8 @@
import com.ruoyi.common.security.utils.SecurityUtils;
import com.ruoyi.system.api.domain.SysRole;
import com.ruoyi.system.api.domain.SysUser;
import javax.annotation.Resource;
/**
 * 菜单 业务层处理
@@ -52,6 +51,12 @@
    @Autowired
    private SysUserRoleMapper sysUserRoleMapper;
    @Resource
    private SysUserMapper sysUserMapper;
    /**
     * 根据用户查询系统菜单列表
@@ -352,7 +357,9 @@
    @Override
    public List<SysMenus> getAllMenu() {
        Long roleId = tokenService.getLoginUser().getSysUser().getRoles().get(0).getRoleId();
        Long userid = tokenService.getLoginUser().getUserid();
        SysUserRole sysUserRole = sysUserRoleMapper.selectSysUserRoleByUserId(userid);
        Long roleId = sysUserRole.getRoleId();
        List<SysMenus> list=null;
        if(roleId!=1){
            list = menuMapper.getAllOne();
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysUserServiceImpl.java
@@ -21,6 +21,7 @@
import com.ruoyi.system.domain.SysUserRole;
import com.ruoyi.system.query.GetSysUserList;
import com.ruoyi.system.service.ISysConfigService;
import com.ruoyi.system.service.ISysRoleService;
import com.ruoyi.system.service.ISysUserRoleService;
import com.ruoyi.system.service.ISysUserService;
import org.slf4j.Logger;
@@ -86,6 +87,9 @@
    
    @Resource
    private SiteClient siteClient;
    @Resource
    private ISysRoleService sysRoleService;
    
    
    
@@ -578,18 +582,30 @@
        for (SysUser sysUser : list) {
            List<SysUserRole> list1 = sysUserRoleService.list(new LambdaQueryWrapper<SysUserRole>().eq(SysUserRole::getUserId, sysUser.getUserId()));
            List<Integer> data1 = userSiteClient.getSiteIds(sysUser.getUserId()).getData();
            if(null == data1){
                data1 = new ArrayList<>();
            }
            for (SysUserRole sysUserRole : list1) {
                List<Integer> data = roleSiteClient.getSiteIds(sysUserRole.getRoleId()).getData();
                if (null == data){
                    continue;
                }
                data1.addAll(data);
            }
            Set<Integer> siteIds = new HashSet<>(data1);
            sysUser.setSiteIds(siteIds.stream().collect(Collectors.toList()));
            List<Site> data = siteClient.getSiteByIds(siteIds.stream().collect(Collectors.toList())).getData();
            List<String> siteNames = data.stream().map(Site::getName).collect(Collectors.toList());
            sysUser.setSiteNames(siteNames);
            Set<Long> collect = list1.stream().map(SysUserRole::getRoleId).collect(Collectors.toSet());
            List<SysRole> sysRoles = roleMapper.selectBatchIds(collect);
            List<String> roleNames = sysRoles.stream().map(SysRole::getRoleName).collect(Collectors.toList());
            if(null != data){
                List<String> siteNames = data.stream().map(Site::getName).collect(Collectors.toList());
                sysUser.setSiteNames(siteNames);
            }
            List<String> roleNames = new ArrayList<>();
            for (SysUserRole sysUserRole : list1) {
                SysRole sysRole = sysRoleService.selectRoleById(sysUserRole.getRoleId());
                roleNames.add(sysRole.getRoleName());
            }
            Long[] roleIds = new Long[]{};
            sysUser.setRoleIds(list1.stream().map(SysUserRole::getRoleId).collect(Collectors.toList()).toArray(roleIds));
            sysUser.setRoleNames(roleNames);
        }
        return pageInfo.setRecords(list);
ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/controller/TAppCouponController.java
@@ -1,12 +1,16 @@
package com.ruoyi.account.controller;
import com.alibaba.fastjson2.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.ruoyi.account.api.dto.CouponListDto;
import com.ruoyi.account.api.dto.GrantCouponDto;
import com.ruoyi.account.api.model.TAppCoupon;
import com.ruoyi.account.api.model.TAppUserCar;
import com.ruoyi.account.api.query.ExchangeRecordGoodsQuery;
import com.ruoyi.chargingPile.api.feignClient.ChargingGunClient;
import com.ruoyi.chargingPile.api.model.TChargingGun;
import com.ruoyi.chargingPile.api.model.TChargingPile;
import com.ruoyi.other.api.vo.CouponListVOVO;
import com.ruoyi.account.api.vo.ExchangeRecordVO;
import com.ruoyi.account.service.TAppCouponService;
@@ -26,6 +30,9 @@
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import java.math.BigDecimal;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
@@ -44,6 +51,8 @@
public class TAppCouponController {
    @Autowired
    private TAppCouponService tAppCouponService;
    @Resource
    private OtherClient otherClient;
    @Autowired
    private TAppUserCarService appUserCarService;
@@ -53,6 +62,11 @@
    private ChargingPileClient chargingPileClient;
    @Autowired
    private ChargingOrderClient chargingOrderClient;
    @Resource
    private ChargingGunClient chargingGunClient;
    /**
     * 小程序扫一扫 添加车辆
     * @param dto
@@ -69,10 +83,13 @@
     * 查询用户可用优惠券数量
     * @return
     */
    @ApiOperation(value = "通过桩编号获取电站详情", tags = {"小程序-扫一扫"})
    @ApiOperation(value = "通过充电枪id和预付金额获取电站详情", tags = {"小程序-扫一扫"})
    @GetMapping(value = "/scan/siteInfo")
    public AjaxResult<SiteInfoVO> siteInfo(String number) {
        SiteInfoVO data = chargingPileClient.getSiteInfoByNumber(number).getData();
    public AjaxResult<SiteInfoVO> siteInfo(Integer id, BigDecimal money) {
        TChargingGun chargingGun = chargingGunClient.getChargingGunById(id).getData();
        TChargingPile chargingPile = chargingPileClient.getChargingPileById(chargingGun.getChargingPileId()).getData();
        SiteInfoVO data = chargingPileClient.getSiteInfoByNumber(chargingPile.getNumber().toString()).getData();
        data.setChargingGunId(id);
        List<TAppUserCar> cars = appUserCarService.list(new QueryWrapper<TAppUserCar>()
                .eq("app_user_id",tokenService.getLoginUserApplet().getUserId())
                .orderByDesc("create_time"));
@@ -93,6 +110,15 @@
                }
            }
        }
        BigDecimal electrovalence = data.getElectrovalence();
        BigDecimal vipElectrovalence = data.getVipElectrovalence();
        // 计算预付金额能充多少度普通电
        BigDecimal divide1 = money.divide(electrovalence, 2, BigDecimal.ROUND_HALF_UP);
        // 计算冲会员电能充多少度会员电
        BigDecimal divide = vipElectrovalence.divide(electrovalence, 2, BigDecimal.ROUND_HALF_UP);
        BigDecimal subtract = divide.subtract(divide1);
        // 优惠金额
        data.setMoney(subtract.multiply(vipElectrovalence).setScale(2, BigDecimal.ROUND_HALF_UP));
        return AjaxResult.ok(data);
    }
    /**
@@ -122,8 +148,8 @@
     * 管理后台远程调用 根据优惠券ids 查询对应的发放数量
     * @return 优惠券ids 查询每个优惠券的发放数量
     */
    @PostMapping("/getCountByCouponIds")
    public R<List<Integer>> getCountByCouponIds(@RequestParam("couponIds") String couponIds) {
    @PostMapping("/getCountByCouponIds/{couponIds}")
    public R<List<Integer>> getCountByCouponIds(@PathVariable("couponIds")String couponIds) {
        // 最终结果 和优惠券id一一对应
        List<Integer> res = new ArrayList<>();
        String[] split = couponIds.split(",");
@@ -142,8 +168,8 @@
     * @param couponId
     * @return
     */
    @PostMapping("/getUseCountByCouponId")
    public R<Integer> getUseCountByCouponId(Integer couponId){
    @PostMapping("/getUseCountByCouponId/{couponId}")
    public R<Integer> getUseCountByCouponId(@PathVariable("couponId") Integer couponId){
        return R.ok(tAppCouponService.list(new QueryWrapper<TAppCoupon>()
                .eq("coupon_id", couponId)
                .eq("status",2)).size());
@@ -171,6 +197,8 @@
            tAppCoupon.setEndTime(dto.getEndTime());
            tAppCoupon.setWaysToObtain(dto.getWaysToObtain());
            tAppCoupon.setStatus(1);
            R<TCoupon> couponById = otherClient.getCouponById(Integer.valueOf(s));
            tAppCoupon.setCouponJson(JSON.toJSONString(couponById.getData()));
            res.add(tAppCoupon);
        }
        tAppCouponService.saveBatch(res);
ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/controller/TAppUserController.java
@@ -236,12 +236,15 @@
        }
        //列表查询
        Page<TAppUser> page = appUserService.lambdaQuery()
                .ge(TAppUser::getVipEndTime, LocalDateTime.now())
                .like(userListQueryDto.getUserPhone() != null && !"".equals(userListQueryDto.getUserPhone()), TAppUser::getPhone, userListQueryDto.getUserPhone())
                .eq(userListQueryDto.getCompanyId() != null, TAppUser::getCompanyId, userListQueryDto.getCompanyId())
                .eq(userListQueryDto.getCityCode() != null && !"".equals(userListQueryDto.getCityCode()), TAppUser::getCityCode, userListQueryDto.getCityCode())
                .eq(userListQueryDto.getStatus() != null, TAppUser::getStatus, userListQueryDto.getStatus())
                .eq(userListQueryDto.getVipTypeId() != null, TAppUser::getVipId, userListQueryDto.getVipTypeId())
                .in(!userIds.isEmpty(),TAppUser::getId,userIds)
                .ge(userListQueryDto.getVipEndTime1() != null, TAppUser::getVipEndTime, userListQueryDto.getVipEndTime1() == null ? null : LocalDateTime.now().plusDays(userListQueryDto.getVipEndTime1()))
                .le(userListQueryDto.getVipEndTime2() != null, TAppUser::getVipEndTime, userListQueryDto.getVipEndTime2() == null ? null : LocalDateTime.now().plusDays(userListQueryDto.getVipEndTime2()))
                .in(!userIds.isEmpty(), TAppUser::getId, userIds)
                .page(Page.of(userListQueryDto.getPageCurr(), userListQueryDto.getPageSize()));
        if (page.getRecords().isEmpty()){
            return R.ok(page);
@@ -255,15 +258,19 @@
            //拿到最新的tagId
            TAppUserTag one = appUserTagService.lambdaQuery().eq(TAppUserTag::getAppUserId, appUser.getId()).orderByDesc(TAppUserTag::getCreateTime).last("limit 1").one();
            //设置最新的tagName
            R<TUserTag> byIdTag = otherClient.getByIdTag(one.getUserTagId());
            if (byIdTag.getData()!=null) {
                appUser.setTagName(byIdTag.getData().getName());
            if (one!=null) {
                R<TUserTag> byIdTag = otherClient.getByIdTag(one.getUserTagId());
                if (byIdTag.getData() != null) {
                    appUser.setTagName(byIdTag.getData().getName());
                }
            }
            //匹配vipMap的值
            appUser.setVipName(vipMap.getData().get(appUser.getVipId()));
            //累计充电次数
            R<Long> useOrderCount = chargingOrderClient.useOrderCount(appUser.getId());
            appUser.setOrderCount(useOrderCount.getData());
            appUser.setLastDays( Duration.between(LocalDateTime.now(), appUser.getVipEndTime()).toDays());
            appUser.setUid(appUser.getId().toString());
        }
        return R.ok(page);
ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/controller/TAppUserVipDetailController.java
New file
@@ -0,0 +1,41 @@
package com.ruoyi.account.controller;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.ruoyi.account.api.model.TAppUserVipDetail;
import com.ruoyi.account.api.vo.GetAppUserVipDetail;
import com.ruoyi.account.service.TAppUserVipDetailService;
import com.ruoyi.common.core.domain.R;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
/**
 * @author zhibing.pu
 * @Date 2024/8/21 10:35
 */
@RestController
@RequestMapping("/appUserVipDetail")
public class TAppUserVipDetailController {
    @Resource
    private TAppUserVipDetailService appUserVipDetailService;
    /**
     * 获取用户当前有效的vip明细
     * @param getAppUserVipDetail
     * @return
     */
    @PostMapping("/getAppUserVipDetail")
    public R<TAppUserVipDetail> getAppUserVipDetail(@RequestBody GetAppUserVipDetail getAppUserVipDetail){
        TAppUserVipDetail one = appUserVipDetailService.getOne(new LambdaQueryWrapper<TAppUserVipDetail>()
                .eq(TAppUserVipDetail::getAppUserId, getAppUserVipDetail.getAppUserId())
                .eq(TAppUserVipDetail::getVipId, getAppUserVipDetail.getVipId())
                .last(" and now() between start_time and end_time"));
        return R.ok(one);
    }
}
ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/util/PointDetailUtil.java
@@ -3,6 +3,7 @@
import com.ruoyi.account.api.model.TAppUserIntegralChange;
import com.ruoyi.account.service.TAppUserIntegralChangeService;
import com.ruoyi.common.core.utils.OrderCodeUtil;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
@@ -14,7 +15,7 @@
    public void addDetail(Integer old,Integer now,Integer type,Long userId,String extension,String code){
        TAppUserIntegralChange tAppUserIntegralChange= new TAppUserIntegralChange();
        tAppUserIntegralChange.setCode(code);
        tAppUserIntegralChange.setCode(OrderCodeUtil.getOrderCode("DH"));
        tAppUserIntegralChange.setAppUserId(userId);
        tAppUserIntegralChange.setChangeType(type);
        tAppUserIntegralChange.setHistoricalIntegral(old);
ruoyi-service/ruoyi-account/src/main/resources/mapper/account/TAppCouponMapper.xml
@@ -43,13 +43,13 @@
                AND  t2.phone LIKE concat('%',#{req.phone}, '%')
            </if>
            <if test="req.status != null and req.status = 1">
                AND t1.status =1 and t1.endTime &gt;= now()
                AND t1.status =1 and t1.end_time &gt;= now()
            </if>
            <if test="req.status != null and req.status = 2">
                AND t1.status =2
            </if>
            <if test="req.status != null and req.status = 3">
                AND t1.status =1 and t1.endTime &lt;= now() and t1.del_flag = 0
                AND t1.status =1 and t1.end_time &lt;= now() and t1.del_flag = 0
            </if>
            <if test="req.status != null and req.status = 4">
                AND t1.del_flag = 0
ruoyi-service/ruoyi-account/src/main/resources/mybatis-config.xml
@@ -10,7 +10,7 @@
        <setting name="cacheEnabled" value="true"/>
        <!-- 延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。默认 false  -->
        <!-- <setting name="lazyLoadingEnabled" value="true"/> -->
        <setting name="mapUnderscoreToCamelCase" value="false"/><!--是否将map下划线方式转为驼峰式命名-->
        <setting name="mapUnderscoreToCamelCase" value="true"/><!--是否将map下划线方式转为驼峰式命名-->
        <!-- 当开启时,任何方法的调用都会加载该对象的所有属性。默认 false,可通过select标签的 fetchType来覆盖-->
        <!-- <setting name="aggressiveLazyLoading" value="false"/>-->
        <!--  Mybatis 创建具有延迟加载能力的对象所用到的代理工具,默认JAVASSIST -->
ruoyi-service/ruoyi-chargingPile/src/main/java/com/ruoyi/chargingPile/controller/FileController.java
@@ -21,7 +21,6 @@
 */
@Api(tags = "服务器文件上传")
@RestController
@CrossOrigin
@RequestMapping("/file/")
public class FileController {
@@ -47,7 +46,7 @@
        // 获取文件名称
        String filename = mf.getOriginalFilename();
        // 获取文件后缀
        String ext = filename.substring(filename.lastIndexOf("."), filename.length());
        String ext = filename.substring(filename.lastIndexOf(".") + 1, filename.length());
        // 检查文件类型
        if (!fileUploadConfig.getAllowExt().contains(ext)) {
            return AjaxResult.error("上传文件格式不正确,仅支持" + fileUploadConfig.getAllowExt());
ruoyi-service/ruoyi-chargingPile/src/main/java/com/ruoyi/chargingPile/controller/SiteController.java
@@ -88,7 +88,7 @@
            // 当前时间属于那个阶段 取哪个阶段的电价
            if(LocalTime.now().isAfter(LocalTime.parse(tAccountingStrategyDetail.getStartTime())) && LocalTime.now().isBefore(LocalTime.parse(tAccountingStrategyDetail.getEndTime()))){
                siteInfoVO.setElectrovalence(tAccountingStrategyDetail.getElectrovalence());
                siteInfoVO.setVipElectrovalence(tAccountingStrategyDetail.getElectrovalence().multiply(byId1.getDiscount()).setScale(2, BigDecimal.ROUND_HALF_UP));
                siteInfoVO.setVipElectrovalence(tAccountingStrategyDetail.getElectrovalence().multiply(byId1.getDiscount()).setScale(4, BigDecimal.ROUND_HALF_UP));
            }
        }
        siteInfoVO.setName(byId.getName());
@@ -109,7 +109,7 @@
        return AjaxResult.success(siteService.list(new QueryWrapper<>()));
    }
    @PostMapping("/getSiteListGun")
    @GetMapping("/getSiteListGun")
    @ApiOperation(value = "获取站点列表 不分页", tags = {"管理后台-接口信息使用"})
    public AjaxResult<List<Site>> getSiteListGun(){
        return AjaxResult.success(siteService.getSiteListGun());
@@ -165,7 +165,7 @@
    
    
    @ResponseBody
    @PostMapping("/getSiteInfo")
    @PostMapping("/getSiteInfo/{id}")
    @ApiOperation(value = "获取站点详情", tags = {"管理后台-站点管理"})
    public AjaxResult<Site> getSiteInfo(@PathVariable Integer id){
        Site site = siteService.getById(id);
ruoyi-service/ruoyi-chargingPile/src/main/java/com/ruoyi/chargingPile/controller/TAccountingStrategyController.java
@@ -23,6 +23,8 @@
import com.ruoyi.common.log.annotation.Log;
import com.ruoyi.common.log.enums.BusinessType;
import com.ruoyi.common.log.enums.OperatorType;
import com.ruoyi.common.security.utils.SecurityUtils;
import com.ruoyi.system.api.domain.SysUser;
import com.ruoyi.system.api.feignClient.SysUserClient;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
@@ -34,9 +36,12 @@
import java.math.BigDecimal;
import java.math.BigInteger;
import java.math.RoundingMode;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.Arrays;
import java.util.List;
import static com.ruoyi.common.core.context.SecurityContextHolder.getUserId;
/**
 * <p>
@@ -97,6 +102,11 @@
    @ApiOperation(tags = {"后台-计费策略", "管理后台-站点管理"},value = "添加计费策略")
    @PostMapping(value = "/add")
    public AjaxResult<Integer> add(@RequestBody TAccountingStrategyDTO dto) {
        if(null != dto.getSiteId()){
            dto.setAuditStatus(3);
        }
        Long userId = SecurityUtils.getLoginUser().getUserid();
        dto.setUserId(userId);
        accountingStrategyService.save(dto);
        // 添加明细
        dto.getAccountingStrategyDetails().forEach(detail -> detail.setAccountingStrategyId(dto.getId()));
@@ -132,10 +142,14 @@
        TAccountingStrategyVO accountingStrategyVO = new TAccountingStrategyVO();
        BeanUtils.copyProperties(accountingStrategy,accountingStrategyVO);
        // 查询用户信息
        String firstUserName = sysUserClient.getSysUser(accountingStrategy.getFirstUserId()).getData().getNickName();
        String twoUserName = sysUserClient.getSysUser(accountingStrategy.getTwoUserId()).getData().getNickName();
        accountingStrategyVO.setFirstUserName(firstUserName);
        accountingStrategyVO.setTwoUserName(twoUserName);
        if(null != accountingStrategy.getFirstUserId()){
            String firstUserName = sysUserClient.getSysUser(accountingStrategy.getFirstUserId()).getData().getNickName();
            accountingStrategyVO.setFirstUserName(firstUserName);
        }
        if(null != accountingStrategy.getTwoUserId()){
            String twoUserName = sysUserClient.getSysUser(accountingStrategy.getTwoUserId()).getData().getNickName();
            accountingStrategyVO.setTwoUserName(twoUserName);
        }
        return AjaxResult.ok(accountingStrategyVO);
    }
@@ -180,8 +194,6 @@
    @PostMapping(value = "/auth/pageList")
    public AjaxResult<PageInfo<TAccountingStrategyVO>> authPageList(@RequestBody TAccountingStrategyQuery query) {
        return AjaxResult.ok(accountingStrategyService.pageList(query));
    }
@@ -189,7 +201,7 @@
    @PostMapping(value = "/auth/pass")
    public AjaxResult<PageInfo<TAccountingStrategyVO>> authPass(@RequestBody SteategyPassDto steategyPassDto) {
        TAccountingStrategy byId = accountingStrategyService.getById(steategyPassDto.getId());
        Long userId = tokenService.getLoginUser().getSysUser().getUserId();
        Long userId = SecurityUtils.getLoginUser().getUserid();
        if (byId.getAuditStatus()==1&&byId.getFirstUserId()!=userId){
            return AjaxResult.error("您不是一级审核人员,无法审核");
        }
@@ -201,22 +213,35 @@
                byId.setAuditStatus(2);
                byId.setFirstRemark(steategyPassDto.getRemark());
                byId.setFirstAuditTime(LocalDateTime.now());
            }else if (byId.getAuditStatus()==2){
                byId.setAuditStatus(3);
                byId.setTwoRemark(steategyPassDto.getRemark());
                byId.setTwoAuditTime(LocalDateTime.now());
            }
        }else {
            byId.setAuditStatus(4);
        }
        accountingStrategyService.updateById(byId);
        return AjaxResult.success();
    }
    @ApiOperation(tags = {"后台-申请表单-计费模板审核"},value = "删除")
    @DeleteMapping(value = "/delete")
    public R delete(String ids) {
        String[] split = ids.split(",");
        for (String s : split) {
            accountingStrategyService.removeById(s);
        }
        return R.ok();
    }
    /**
ruoyi-service/ruoyi-chargingPile/src/main/java/com/ruoyi/chargingPile/controller/TApplyChargingPileController.java
@@ -1,19 +1,33 @@
package com.ruoyi.chargingPile.controller;
import com.alibaba.excel.EasyExcelFactory;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.write.builder.ExcelWriterBuilder;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.chargingPile.api.dto.ApplyChargingQuery;
import com.ruoyi.chargingPile.api.dto.ApplyChargingRemarkDto;
import com.ruoyi.chargingPile.api.model.TApplyChargingPile;
import com.ruoyi.chargingPile.service.TApplyChargingPileService;
import com.ruoyi.common.core.constant.Constants;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.core.web.domain.AjaxResult;
import com.ruoyi.common.log.annotation.Log;
import com.ruoyi.common.log.enums.BusinessType;
import com.ruoyi.common.log.enums.OperatorType;
import com.ruoyi.common.security.service.TokenService;
import io.swagger.annotations.ApiOperation;
import org.apache.commons.codec.CharEncoding;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.List;
/**
 * <p>
@@ -40,12 +54,20 @@
     * 建桩申请
     */
    @Log(title = "建桩申请", businessType = BusinessType.INSERT,operatorType = OperatorType.MOBILE)
    @ApiOperation(tags = {"小程序-建桩申请","后台-申请表单-申请建桩"},value = "建桩申请")
    @ApiOperation(tags = {"小程序-建桩申请"},value = "建桩申请")
    @PostMapping(value = "/add")
    public AjaxResult<Boolean> add(@RequestBody TApplyChargingPile dto) {
        // 用户id
        Long userId = tokenService.getLoginUserApplet().getUserId();
        dto.setAppUserId(userId);
        return AjaxResult.ok(applyChargingPileService.save(dto));
    }
    @Log(title = "建桩申请", businessType = BusinessType.INSERT,operatorType = OperatorType.MOBILE)
    @ApiOperation(tags = {"后台-申请表单-申请建桩"},value = "建桩申请")
    @PostMapping(value = "/manage/add")
    public AjaxResult<Boolean> manageAdd(@RequestBody TApplyChargingPile dto) {
        return AjaxResult.ok(applyChargingPileService.save(dto));
    }
@@ -58,6 +80,38 @@
        return AjaxResult.ok(page);
    }
    @ApiOperation(tags = {"后台-申请表单-申请建桩"},value = "导出")
    @PutMapping(value = "/export")
    public R export(HttpServletResponse response) {
        try {
        response.setCharacterEncoding(Constants.UTF8);
        response.setContentType("application/vnd.ms-excel");
        response.setHeader("Access-Control-Expose-Headers", "Content-disposition");
        response.setHeader("Content-Disposition", "attachment;filename=" +
                URLEncoder.encode("申请建桩数据", CharEncoding.UTF_8) + ".xlsx");
        } catch (UnsupportedEncodingException e) {
            return R.fail("excel导出失败!");
        }
        try {
            List<TApplyChargingPile> list = applyChargingPileService.lambdaQuery().list();
            // excel模板封装
            ExcelWriterBuilder excelWriterBuilder = EasyExcelFactory.write(response.getOutputStream());
            InputStream stream = Thread.currentThread().getContextClassLoader().getResourceAsStream("template/" +"充电桩申请记录" + ".xlsx");
            // 自动释放资源
            try (ExcelWriter excelWriter = excelWriterBuilder.withTemplate(stream).build()) {
                WriteSheet writeSheet = EasyExcelFactory.writerSheet().build();
                excelWriter.fill(list, writeSheet);
                excelWriter.finish();
            } catch (Exception e) {
                return R.fail("excel导出失败!");
            }
        } catch (IOException e) {
            return R.fail("excel导出失败!");
        }
        return R.ok();
    }
    @ApiOperation(tags = {"后台-申请表单-申请建桩"},value = "详情")
    @PostMapping(value = "/detail/{id}")
@@ -74,6 +128,18 @@
        applyChargingPileService.updateById(byId);
        return AjaxResult.success();
    }
    @ApiOperation(tags = {"后台-申请表单-申请建桩"},value = "删除")
    @DeleteMapping(value = "/remark")
    public AjaxResult remark(String ids) {
        String[] split = ids.split(",");
        for (String s : split) {
            applyChargingPileService.removeById(s);
        }
        return AjaxResult.success();
    }
ruoyi-service/ruoyi-chargingPile/src/main/java/com/ruoyi/chargingPile/controller/TChargingGunController.java
@@ -114,8 +114,8 @@
     */
    @Log(title = "结束充电", businessType = BusinessType.UPDATE,operatorType = OperatorType.MANAGE)
    @ApiOperation(tags = {"后台-充电枪"},value = "结束充电")
    @PostMapping(value = "/stopCharging")
    public AjaxResult<String> stopCharging() {
    @PutMapping(value = "/stopCharging")
    public AjaxResult<String> stopCharging(@RequestParam("id") Integer id) {
        // TODO 硬件 结束充电
        return AjaxResult.success();
    }
ruoyi-service/ruoyi-chargingPile/src/main/java/com/ruoyi/chargingPile/controller/TChargingPileController.java
@@ -83,7 +83,7 @@
    @ResponseBody
    @GetMapping("/pageChargingPileList")
    @ApiOperation(value = "获取充电桩列表数据", tags = {"管理后台-充电桩信息"})
    public AjaxResult<PageInfo<PageChargingPileListDTO>> pageChargingPileList(@RequestBody PageChargingPileList page){
    public AjaxResult<PageInfo<PageChargingPileListDTO>> pageChargingPileList(PageChargingPileList page){
        PageInfo<PageChargingPileListDTO> list = chargingPileService.pageChargingPileList(page);
        return AjaxResult.success(list);
    }
@@ -162,5 +162,18 @@
        PageInfo<ChargingGunMonitoring> chargingGunMonitoring = chargingPileService.getChargingGunMonitoring(query);
        return AjaxResult.success(chargingGunMonitoring);
    }
    /**
     * 根据id获取充电桩信息
     * @param id
     * @return
     */
    @ResponseBody
    @PostMapping("/getChargingPileById/{id}")
    public R<TChargingPile> getChargingPileById(@PathVariable Integer id){
        TChargingPile chargingPile = chargingPileService.getById(id);
        return R.ok(chargingPile);
    }
}
ruoyi-service/ruoyi-chargingPile/src/main/java/com/ruoyi/chargingPile/mapper/TChargingPileMapper.java
@@ -39,7 +39,7 @@
     * @param id
     * @return
     */
    TChargingPile getChargingPile(Integer id);
    TChargingPile getChargingPile(@Param("id") Integer id);
    /**
     * 获取站点下充电桩及充电枪列表
ruoyi-service/ruoyi-chargingPile/src/main/java/com/ruoyi/chargingPile/service/impl/PartnerServiceImpl.java
@@ -328,7 +328,6 @@
        Partner partner = this.getById(partnerId);
        GetPermissionConfigurationDTO dto = new GetPermissionConfigurationDTO();
        dto.setPermissionRemarks(partner.getPermissionRemarks());
        Map<String, Object> menu = new HashMap<>();
        List<TPartnerSite> list = partnerSiteService.list(new LambdaQueryWrapper<TPartnerSite>()
                .eq(TPartnerSite::getPartnerId, partnerId)
                .eq(TPartnerSite::getSiteId, siteId));
@@ -337,7 +336,9 @@
                .eq(TSiteMenu::getParentId, 0)
                .eq(TSiteMenu::getVisible, 0)
                .eq(TSiteMenu::getStatus, 0).orderByAsc(TSiteMenu::getOrderNum));
        List<Map<String, Object>> list1 = new ArrayList<>();
        for (TSiteMenu tSiteMenu : tSiteMenus) {
            Map<String, Object> menu = new HashMap<>();
            menu.put("id", tSiteMenu.getMenuId());
            menu.put("name", tSiteMenu.getMenuName());
            menu.put("path", tSiteMenu.getPath());
@@ -347,18 +348,21 @@
                    .eq(TSiteMenu::getParentId, tSiteMenu.getMenuId())
                    .eq(TSiteMenu::getVisible, 0)
                    .eq(TSiteMenu::getStatus, 0).orderByAsc(TSiteMenu::getOrderNum));
            Map<String, Object> child = new HashMap<>();
            List<Map<String, Object>> list2 = new ArrayList<>();
            for (TSiteMenu siteMenu : tSiteMenus1) {
                Map<String, Object> child = new HashMap<>();
                child.put("id", siteMenu.getMenuId());
                child.put("name", siteMenu.getMenuName());
                child.put("path", siteMenu.getPath());
                child.put("type", siteMenu.getMenuType());
                child.put("selected", collect.contains(siteMenu.getMenuId()));
                list2.add(child);
            }
            menu.put("selected", child.keySet().size() > 0 ? true : false);
            menu.put("child", child);
            menu.put("selected", list2.size() > 0 ? true : false);
            menu.put("child", list2);
            list1.add(menu);
        }
        dto.setMenu(menu);
        dto.setMenu(list1);
        return dto;
    }
    
ruoyi-service/ruoyi-chargingPile/src/main/java/com/ruoyi/chargingPile/service/impl/TAccountingStrategyServiceImpl.java
@@ -36,6 +36,14 @@
        for (TAccountingStrategyVO tAccountingStrategyVO : list) {
            SysUser data = sysUserClient.getSysUser(tAccountingStrategyVO.getUserId()).getData();
            tAccountingStrategyVO.setUserName(data.getNickName());
            if (tAccountingStrategyVO.getFirstUserId()!=null){
                SysUser first = sysUserClient.getSysUser(tAccountingStrategyVO.getUserId()).getData();
                tAccountingStrategyVO.setFirstUserName(first==null?null:first.getNickName());
            }
            if (tAccountingStrategyVO.getTwoUserId()!=null){
                SysUser two = sysUserClient.getSysUser(tAccountingStrategyVO.getUserId()).getData();
                tAccountingStrategyVO.setTwoUserName(two==null?null:two.getNickName());
            }
        }
        pageInfo.setRecords(list);
        return pageInfo;
ruoyi-service/ruoyi-chargingPile/src/main/resources/logback.xml
@@ -58,11 +58,11 @@
    </appender>
    <!-- 系统模块日志级别控制  -->
    <logger name="com.ruoyi" level="info" />
    <logger name="com.ruoyi" level="debug" />
    <!-- Spring日志级别控制  -->
    <logger name="org.springframework" level="warn" />
    <logger name="org.springframework" level="debug" />
    <root level="info">
    <root level="debug">
        <appender-ref ref="console" />
    </root>
    
ruoyi-service/ruoyi-chargingPile/src/main/resources/mapper/chargingPile/SiteMapper.xml
@@ -52,6 +52,7 @@
        a.id,
        a.code,
        a.name,
        a.partner_id as partnerId,
        b.name as partnerName,
        a.site_type as siteType,
        a.business_category as businessCategory,
ruoyi-service/ruoyi-chargingPile/src/main/resources/mapper/chargingPile/TChargingGunMapper.xml
@@ -34,29 +34,29 @@
    </sql>
    <select id="pageList" resultType="com.ruoyi.chargingPile.api.vo.TChargingGunVO">
        SELECT
        tas.id, tas.code, tas.site_id, tas.partner_id, tas.charging_pile_id, tas.`name`, tas.`type`, tas.status, tas.charge_mode, tas.accounting_strategy_id,
        tas.upper_rated_voltage, tas.lower_limit_of_rated_voltage, tas.rated_current, tas.rated_power, tas.parking_number, tas.parking_status,
        tas.parking_lock_state, tas.national_standard, tas.remark, tas.create_time, tas.del_flag,tcg.name AS strategyName
        tcg.id, tcg.code, tcg.site_id, tcg.partner_id, tcg.charging_pile_id, tcg.`name`, tcg.`type`, tcg.status, tcg.charge_mode, tcg.accounting_strategy_id,
        tcg.upper_rated_voltage, tcg.lower_limit_of_rated_voltage, tcg.rated_current, tcg.rated_power, tcg.parking_number, tcg.parking_status,
        tcg.parking_lock_state, tcg.national_standard, tcg.remark, tcg.create_time, tcg.del_flag,tas.name AS strategyName
        FROM t_charging_gun tcg
        LEFT JOIN t_accounting_strategy tas ON tas.id = tcg.accounting_strategy_id
        <where>
            <if test="query.siteId != null">
                AND tas.site_id = #{query.siteId}
                AND tcg.site_id = #{query.siteId}
            </if>
            <if test="query.chargingPileId != null">
                AND tas.charging_pile_id = #{query.chargingPileId}
                AND tcg.charging_pile_id = #{query.chargingPileId}
            </if>
            <if test="query.name != null and query.name != ''">
                AND tas.`name` LIKE concat('%',#{query.name},'%')
                AND tcg.`name` LIKE concat('%',#{query.name},'%')
            </if>
            <if test="query.type != null">
                AND tas.`type` = #{query.type}
                AND tcg.`type` = #{query.type}
            </if>
            <if test="query.status != null">
                AND tas.status = #{query.status}
                AND tcg.status = #{query.status}
            </if>
            <if test="query.chargeMode != null">
                AND tas.chargeMode = #{query.chargeMode}
                AND tcg.charge_mode = #{query.chargeMode}
            </if>
            <if test="null != siteIds">
                and tcg.site_id in
@@ -64,9 +64,9 @@
                    #{item}
                </foreach>
            </if>
            AND tas.del_flag = ${@com.ruoyi.common.core.enums.DelFlagEnum@NO.getCode()}
            AND tcg.del_flag = ${@com.ruoyi.common.core.enums.DelFlagEnum@NO.getCode()}
        </where>
        ORDER BY tas.create_time DESC
        ORDER BY tcg.create_time DESC
    </select>
</mapper>
ruoyi-service/ruoyi-chargingPile/src/main/resources/mapper/chargingPile/TChargingPileMapper.xml
@@ -31,22 +31,22 @@
    
    <select id="pageChargingPileList" resultType="com.ruoyi.chargingPile.api.dto.PageChargingPileListDTO">
        select
        id,
        code,
        name,
        number,
        type,
        a.id,
        a.code,
        a.name,
        a.number,
        a.type,
        a.site_id as siteId,
        b.name as siteName,
        a.partner_id as partnerId,
        c.name as partnerName,
        a.manufacturer,
        a.equipment_type as equipmentType,
        a.ratedPower
        a.rated_power as ratedPower
        from t_charging_pile a
        left join t_site b on (a.site_id = b.id)
        left join t_partner c on (a.partner_id = c.id)
        where del_flag = 0
        where a.del_flag = 0
        <if test="null != item.siteId">
            and a.site_id = #{item.siteId}
        </if>
@@ -74,6 +74,7 @@
        from t_charging_pile a
        left join t_site b on (a.site_id = b.id)
        left join t_partner c on (a.partner_id = c.id)
        where a.id = #{id}
    </select>
    <select id="getChargingGunList" resultType="com.ruoyi.chargingPile.api.vo.TChargingPileVO">
        select id,code, `name`, `number` from t_charging_pile
ruoyi-service/ruoyi-order/pom.xml
@@ -33,6 +33,10 @@
            <groupId>com.ruoyi</groupId>
            <artifactId>ruoyi-api-chargingPile</artifactId>
        </dependency>
        <dependency>
            <groupId>com.ruoyi</groupId>
            <artifactId>ruoyi-api-payment</artifactId>
        </dependency>
        <!-- SpringCloud Alibaba Nacos -->
ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/controller/TChargingOrderController.java
@@ -2,7 +2,9 @@
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.ruoyi.chargingPile.api.vo.SiteVO;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.redis.service.RedisService;
import com.ruoyi.common.security.service.TokenService;
import com.ruoyi.common.core.web.domain.AjaxResult;
import com.ruoyi.common.core.web.page.BasePage;
@@ -10,20 +12,27 @@
import com.ruoyi.order.api.model.TChargingOrder;
import com.ruoyi.order.api.model.TOrderEvaluate;
import com.ruoyi.order.api.query.TChargingCountQuery;
import com.ruoyi.order.api.vo.ChargingOrderVO;
import com.ruoyi.order.dto.GetMyChargingOrderList;
import com.ruoyi.order.dto.GetNoInvoicedOrder;
import com.ruoyi.order.dto.MyChargingOrderInfo;
import com.ruoyi.order.dto.MyChargingOrderList;
import com.ruoyi.order.dto.OrderEvaluateVo;
import com.ruoyi.order.dto.*;
import com.ruoyi.order.service.TChargingOrderService;
import com.ruoyi.order.service.TOrderEvaluateService;
import com.ruoyi.order.service.TOrderEvaluateTagService;
import com.ruoyi.payment.api.feignClient.WxPaymentClient;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import org.springframework.beans.factory.annotation.Autowired;
import io.swagger.annotations.ApiOperation;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.List;
import java.util.Map;
import java.util.List;
@@ -49,6 +58,23 @@
    private TOrderEvaluateService orderEvaluateService;
    @Autowired
    private TOrderEvaluateTagService orderEvaluateTagService;
    @Resource
    private WxPaymentClient wxPaymentClient;
    @Resource
    private RedisService redisService;
    @ResponseBody
    @PostMapping(value = "/chargingOrder")
    @ApiOperation(value = "充电桩订单列表", tags = {"管理后台-订单管理"})
    public AjaxResult<PageInfo<ChargingOrderVO>> chargingOrder(@RequestBody OrderEvaluateVo dto){
        return AjaxResult.success();
    }
    @ResponseBody
    @PostMapping(value = "/addEvaluate")
@@ -140,10 +166,96 @@
    @ResponseBody
    @GetMapping(value = "/getNoInvoicedOrder")
    @ApiOperation(value = "获取未开票的订单数据", tags = {"小程序-充电发票"})
    public AjaxResult<List<MyChargingOrderList>> getNoInvoicedOrder(@RequestBody GetNoInvoicedOrder query){
    public AjaxResult<List<MyChargingOrderList>> getNoInvoicedOrder(GetNoInvoicedOrder query){
        List<MyChargingOrderList> list = chargingOrderService.getNoInvoicedOrder(query);
        return AjaxResult.success(list);
    }
    
    @ResponseBody
    @PostMapping(value = "/paymentChargingOrder")
    @ApiOperation(value = "支付充电充值费用", tags = {"小程序-扫一扫"})
    public AjaxResult paymentChargingOrder(AddChargingOrder addChargingOrder){
        return chargingOrderService.paymentChargingOrder(addChargingOrder);
    }
    /**
     * 充电充值支付回调
     * @param request
     */
    @ResponseBody
    @PostMapping(value = "/chargingOrderWXCallback")
    public void chargingOrderWXCallback(HttpServletRequest request){
        Map<String, Object> data = wxPaymentClient.payNotify(request).getData();
        if(null != data){
            String out_trade_no = data.get("out_trade_no").toString();
            String transaction_id = data.get("transaction_id").toString();
            String attach = data.get("attach").toString();
            AjaxResult ajaxResult = chargingOrderService.chargingOrderCallback(1, out_trade_no, transaction_id, attach);
            if(ajaxResult.isSuccess()){
                wxPaymentClient.ack();
            }
        }
    }
    /**
     * 支付宝支付成功后的回调
     * @param request
     */
    @ResponseBody
    @PostMapping(value = "/chargingOrderALICallback")
    public void chargingOrderALICallback(HttpServletRequest request){
        Map<String, Object> data = wxPaymentClient.payNotify(request).getData();
        if(null != data){
            String out_trade_no = data.get("out_trade_no").toString();
            String transaction_id = data.get("transaction_id").toString();
            String attach = data.get("attach").toString();
            AjaxResult ajaxResult = chargingOrderService.chargingOrderCallback(2, out_trade_no, transaction_id, attach);
            if(ajaxResult.isSuccess()){
            }
        }
    }
    @ResponseBody
    @GetMapping(value = "/preChargeCheck/{id}")
    @ApiOperation(value = "获取安全检测数据", tags = {"小程序-扫一扫"})
    @ApiImplicitParams({
            @ApiImplicitParam(name = "id", value = "充电枪id", required = true)
    })
    public AjaxResult<PreChargeCheck> preChargeCheck(@PathVariable Integer id){
        String key = "AQJC_" + id;
        Object cacheObject = redisService.getCacheObject(key);
        return AjaxResult.success(cacheObject);
    }
    @ResponseBody
    @GetMapping(value = "/getChargingDetails/{id}")
    @ApiOperation(value = "获取充电中页面数据", tags = {"小程序-扫一扫"})
    @ApiImplicitParams({
            @ApiImplicitParam(name = "id", value = "充电枪id", required = true)
    })
    public AjaxResult<ChargingDetails> getChargingDetails(@PathVariable Integer id){
        ChargingDetails chargingDetails = chargingOrderService.getChargingDetails(id);
        return AjaxResult.success(chargingDetails);
    }
    @ResponseBody
    @PutMapping(value = "/stopCharging/{id}")
    @ApiOperation(value = "手动停止充电", tags = {"小程序-扫一扫"})
    @ApiImplicitParams({
            @ApiImplicitParam(name = "id", value = "订单id", required = true)
    })
    public AjaxResult stopCharging(@PathVariable String id){
        return chargingOrderService.stopCharging(id);
    }
}
ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/controller/TExchangeOrderController.java
@@ -1,4 +1,6 @@
package com.ruoyi.order.controller;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
@@ -14,10 +16,10 @@
import com.ruoyi.order.dto.ExchangeOrderGoodsInfo;
import com.ruoyi.order.dto.GetMyExchangeOrder;
import com.ruoyi.order.dto.MyExchangeOrderList;
import com.ruoyi.order.service.TChargingOrderService;
import com.ruoyi.order.service.TExchangeOrderService;
import com.ruoyi.order.service.TShoppingOrderService;
import com.ruoyi.order.service.TVipOrderService;
import com.ruoyi.common.core.dto.ExchangeDto;
import com.ruoyi.common.core.utils.OrderCodeUtil;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.util.StringUtils;
@@ -25,6 +27,7 @@
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;
@@ -144,9 +147,45 @@
        return R.ok(strings);
    }
    @PostMapping("/getById")
    public R<Long> getById(@RequestParam("goodId") Integer goodId,@RequestParam("userId")  Long userId,@RequestParam("goodType") Integer goodType){
        if (goodType==1){
            return R.ok(exchangeOrderService.lambdaQuery().eq(TExchangeOrder::getGoodsId,goodId).eq(TExchangeOrder::getAppUserId,userId).ne(TExchangeOrder::getStatus,4).count());
        }else {
            return R.ok(exchangeOrderService.lambdaQuery().eq(TExchangeOrder::getCouponId,goodId).eq(TExchangeOrder::getAppUserId,userId).ne(TExchangeOrder::getStatus,4).count());
        }
    }
    @PostMapping("/create")
    public R<Long> exchangeCreate(@RequestBody ExchangeDto exchangeDto){
        TExchangeOrder tExchangeOrder = new TExchangeOrder();
        tExchangeOrder.setCode(OrderCodeUtil.getOrderCode("DH"));
        tExchangeOrder.setAppUserId(exchangeDto.getUserId());
        tExchangeOrder.setOrderType(exchangeDto.getGoodType());
        if (exchangeDto.getGoodType()==1) {
            tExchangeOrder.setGoodsId(exchangeDto.getGoodId());
        }else {
            tExchangeOrder.setCouponId(exchangeDto.getGoodId());
        }
        tExchangeOrder.setPurchaseQuantity(1);
        tExchangeOrder.setAppUserAddressId(Long.valueOf(exchangeDto.getAddressId()));
        tExchangeOrder.setPoints(exchangeDto.getPoint());
        tExchangeOrder.setRemark("");
        if (exchangeDto.getGoodType()==1){
            tExchangeOrder.setStatus(0);
        }else {
            tExchangeOrder.setStatus(3);
        }
        tExchangeOrder.setCreateTime(LocalDateTime.now());
        tExchangeOrder.setDelFlag(false);
        exchangeOrderService.save(tExchangeOrder);
        return R.ok();
    }
    
    
    
ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/dto/AddChargingOrder.java
New file
@@ -0,0 +1,26 @@
package com.ruoyi.order.dto;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.math.BigDecimal;
/**
 * @author zhibing.pu
 * @Date 2024/8/20 13:54
 */
@Data
@ApiModel
public class AddChargingOrder {
    @ApiModelProperty("充电枪id")
    public Integer id;
    @ApiModelProperty("支付金额")
    private BigDecimal paymentAmount;
    @ApiModelProperty("优惠券id")
    private Long appUserCouponId;
    @ApiModelProperty("车辆id")
    private Long appUserCarId;
    @ApiModelProperty("支付方式(1=微信,2=支付宝)")
    private Integer paymentType;
}
ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/dto/ChargingDetails.java
New file
@@ -0,0 +1,42 @@
package com.ruoyi.order.dto;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.math.BigDecimal;
/**
 * @author zhibing.pu
 * @Date 2024/8/21 14:09
 */
@Data
@ApiModel
public class ChargingDetails {
    @ApiModelProperty("订单id")
    private String id;
    @ApiModelProperty("充电电流")
    private BigDecimal chargeCurrent;
    @ApiModelProperty("充电电压")
    private BigDecimal ChargeVoltage;
    @ApiModelProperty("充电功率")
    private BigDecimal ChargePower;
    @ApiModelProperty("完成比例")
    private Integer completionRatio;
    @ApiModelProperty("续航")
    private BigDecimal endurance;
    @ApiModelProperty("剩余充电时间")
    private Long remainingChargeTime;
    @ApiModelProperty("充电枪名")
    private String name;
    @ApiModelProperty("订单编号")
    private String code;
    @ApiModelProperty("已充电度数")
    private BigDecimal chargedDegrees;
    @ApiModelProperty("已充电时间")
    private Long chargedTime;
    @ApiModelProperty("充电费用")
    private BigDecimal ChargingCost;
    @ApiModelProperty("充电状态(0=未知,1=等待中,2=启动中,3=充电中,4=停止中,5=已结束)")
    private Integer status;
}
ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/dto/MyChargingOrderList.java
@@ -21,6 +21,8 @@
    private String title;
    @ApiModelProperty("充电度数")
    private BigDecimal chargingDegree;
    @ApiModelProperty("充电枪id")
    private Integer chargingGunId;
    @ApiModelProperty("充电桩枪名称")
    private String name;
    @ApiModelProperty("结束方式(0=异常终止,1=主动终止,2=满电终止,3=费用不足终止)")
ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/dto/PreChargeCheck.java
New file
@@ -0,0 +1,20 @@
package com.ruoyi.order.dto;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
 * @author zhibing.pu
 * @Date 2024/8/21 13:47
 */
@Data
@ApiModel
public class PreChargeCheck {
    @ApiModelProperty("安全连接检测")
    private Boolean secureConnectionDetection;
    @ApiModelProperty("绝缘检测")
    private Boolean insulationTesting;
    @ApiModelProperty("电子锁锁止")
    private Boolean electronicLockLock;
}
ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/service/TChargingOrderService.java
@@ -1,12 +1,10 @@
package com.ruoyi.order.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.ruoyi.common.core.web.domain.AjaxResult;
import com.ruoyi.common.core.web.page.BasePage;
import com.ruoyi.order.api.model.TChargingOrder;
import com.ruoyi.order.dto.GetMyChargingOrderList;
import com.ruoyi.order.dto.GetNoInvoicedOrder;
import com.ruoyi.order.dto.MyChargingOrderInfo;
import com.ruoyi.order.dto.MyChargingOrderList;
import com.ruoyi.order.dto.*;
import org.springframework.web.bind.annotation.RequestBody;
import java.util.List;
@@ -45,4 +43,39 @@
     * @return
     */
    List<MyChargingOrderList> getNoInvoicedOrder(GetNoInvoicedOrder query);
    /**
     * 充电支付
     * @param addChargingOrder
     * @return
     */
    AjaxResult paymentChargingOrder(AddChargingOrder addChargingOrder);
    /**
     * 支付成功后的回调处理逻辑
     * @param paymentType 支付方式:1=微信,2=支付宝
     * @param out_trade_no 业务流水号
     * @param transaction_id 三方支付流水号
     * @param attach 附加数据
     * @return
     */
    AjaxResult chargingOrderCallback(Integer paymentType, String out_trade_no, String transaction_id, String attach);
    /**
     * 获取充电中的详情
     * @param id
     * @return
     */
    ChargingDetails getChargingDetails(Integer id);
    /**
     * 停止充电
     * @param id
     * @return
     */
    AjaxResult stopCharging(String id);
}
ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/service/impl/TChargingOrderServiceImpl.java
@@ -3,24 +3,33 @@
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.account.api.feignClient.AppUserCarClient;
import com.ruoyi.account.api.feignClient.AppUserClient;
import com.ruoyi.account.api.feignClient.AppUserVipDetailClient;
import com.ruoyi.account.api.model.TAppUser;
import com.ruoyi.account.api.model.TAppUserCar;
import com.ruoyi.account.api.model.TAppUserVipDetail;
import com.ruoyi.account.api.vo.GetAppUserVipDetail;
import com.ruoyi.chargingPile.api.feignClient.ChargingGunClient;
import com.ruoyi.chargingPile.api.feignClient.ChargingPileClient;
import com.ruoyi.chargingPile.api.feignClient.SiteClient;
import com.ruoyi.chargingPile.api.model.Site;
import com.ruoyi.chargingPile.api.model.TChargingGun;
import com.ruoyi.chargingPile.api.model.TChargingPile;
import com.ruoyi.common.core.web.domain.AjaxResult;
import com.ruoyi.common.core.web.page.BasePage;
import com.ruoyi.common.redis.service.RedisService;
import com.ruoyi.common.security.service.TokenService;
import com.ruoyi.common.security.utils.SecurityUtils;
import com.ruoyi.order.api.model.TChargingOrder;
import com.ruoyi.order.api.model.TChargingOrderAccountingStrategy;
import com.ruoyi.order.dto.GetMyChargingOrderList;
import com.ruoyi.order.dto.GetNoInvoicedOrder;
import com.ruoyi.order.dto.MyChargingOrderInfo;
import com.ruoyi.order.dto.MyChargingOrderList;
import com.ruoyi.order.dto.*;
import com.ruoyi.order.mapper.TChargingOrderMapper;
import com.ruoyi.order.service.TChargingOrderAccountingStrategyService;
import com.ruoyi.order.service.TChargingOrderService;
import com.ruoyi.payment.api.feignClient.WxPaymentClient;
import com.ruoyi.payment.api.vo.NotifyV3PayDecodeRespBody;
import com.ruoyi.payment.api.vo.PaymentOrder;
import io.seata.spring.annotation.GlobalTransactional;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
@@ -29,6 +38,7 @@
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;
/**
 * <p>
@@ -56,6 +66,21 @@
    @Resource
    private TokenService tokenService;
    
    @Resource
    private WxPaymentClient wxPaymentClient;
    @Resource
    private ChargingPileClient chargingPileClient;
    @Resource
    private AppUserClient appUserClient;
    @Resource
    private AppUserVipDetailClient appUserVipDetailClient;
    @Resource
    private RedisService redisService;
    
    
    
@@ -80,6 +105,7 @@
            myChargingOrderList.setTitle(site.getName());
            myChargingOrderList.setChargingDegree(tChargingOrder.getChargingCapacity());
            String name = chargingGunClient.getAllName(tChargingOrder.getChargingGunId()).getData();
            myChargingOrderList.setChargingGunId(tChargingOrder.getChargingGunId());
            myChargingOrderList.setName(name);
            myChargingOrderList.setEndMode(tChargingOrder.getEndMode());
            BigDecimal payMoney = tChargingOrder.getStatus() < 4 ? tChargingOrder.getRechargeAmount() : tChargingOrder.getPaymentAmount();
@@ -167,4 +193,197 @@
        }
        return list;
    }
    /**
     * 充电支付
     * @param addChargingOrder
     * @return
     */
    @Override
    @GlobalTransactional(rollbackFor = Exception.class)
    public AjaxResult paymentChargingOrder(AddChargingOrder addChargingOrder) {
        Long userId = tokenService.getLoginUserApplet().getUserId();
        TAppUser appUser = appUserClient.getUserById(userId).getData();
        //查询待支付状态的订单,没有支付则删除订单
        List<TChargingOrder> list = this.list(new LambdaQueryWrapper<TChargingOrder>().eq(TChargingOrder::getAppUserId, userId)
                .eq(TChargingOrder::getRechargePaymentStatus, 1).eq(TChargingOrder::getDelFlag, 0));
        for (TChargingOrder tChargingOrder : list) {
            Integer rechargePaymentType = tChargingOrder.getRechargePaymentType();
            if(1 == rechargePaymentType){
                NotifyV3PayDecodeRespBody data = wxPaymentClient.queryOrderInfo(tChargingOrder.getCode()).getData();
                if(null != data){
                    String trade_state = data.getTrade_state();
                    //支付失败,删除无效的订单
                    if(trade_state.equals("REFUND") || trade_state.equals("NOTPAY") || trade_state.equals("REVOKED") || trade_state.equals("PAYERROR")){
                        this.removeById(tChargingOrder.getId());
                    }
                }
            }
            if(2 == rechargePaymentType){
            }
        }
        //检查当前枪是否是正在使用中
        TChargingOrder one = this.getOne(new LambdaQueryWrapper<TChargingOrder>().eq(TChargingOrder::getChargingGunId, addChargingOrder.getId())
                .in(TChargingOrder::getStatus, Arrays.asList(1, 2, 3)).eq(TChargingOrder::getDelFlag, 0));
        if(null != one){
            return AjaxResult.error("充电枪正在充电桩中,启动失败");
        }
        //查询当前充电枪是否有正在支付的订单
        one = this.getOne(new LambdaQueryWrapper<TChargingOrder>().eq(TChargingOrder::getChargingGunId, addChargingOrder.getId())
                .eq(TChargingOrder::getAppUserCarId, userId).eq(TChargingOrder::getRechargePaymentStatus, 1).eq(TChargingOrder::getDelFlag, 0));
        if(null != one){
            //查询三方支付数据,支付中直接结束三方订单
            if(1 == one.getRechargePaymentType()){
                NotifyV3PayDecodeRespBody data = wxPaymentClient.queryOrderInfo(one.getCode()).getData();
                if(null != data){
                    String trade_state = data.getTrade_state();
                    //支付失败,删除无效的订单
                    if(trade_state.equals("REFUND") || trade_state.equals("NOTPAY") || trade_state.equals("REVOKED") || trade_state.equals("PAYERROR")){
                        this.removeById(one.getId());
                    }else{
                        //结束第三方支付,删除订单
                        wxPaymentClient.close(one.getCode());
                        this.removeById(one.getId());
                    }
                }
            }
            if(2 == one.getRechargePaymentType()){
            }
        }
        //构建新的待支付订单
        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmssSSS");
        TChargingOrder chargingOrder = new TChargingOrder();
        String code = "CD" + Math.random() * 1000 + sdf.format(new Date());
        chargingOrder.setCode(code);
        chargingOrder.setOrderType(1);
        chargingOrder.setOrderClassification(1);
        chargingOrder.setAppUserId(userId);
        chargingOrder.setAppUserCarId(addChargingOrder.getAppUserCarId());
        TChargingGun tChargingGun = chargingGunClient.getChargingGunById(addChargingOrder.getId()).getData();
        chargingOrder.setSiteId(tChargingGun.getSiteId());
        chargingOrder.setChargingPileId(tChargingGun.getChargingPileId());
        chargingOrder.setChargingGunId(addChargingOrder.getId());
        chargingOrder.setRechargePaymentType(addChargingOrder.getPaymentType());
        chargingOrder.setRechargePaymentStatus(1);
        chargingOrder.setRechargeAmount(addChargingOrder.getPaymentAmount());
        chargingOrder.setAppCouponId(addChargingOrder.getAppUserCouponId());
        if(null != appUser.getVipId()){
            GetAppUserVipDetail getAppUserVipDetail = new GetAppUserVipDetail();
            getAppUserVipDetail.setAppUserId(appUser.getId());
            getAppUserVipDetail.setVipId(appUser.getVipId());
            TAppUserVipDetail vipDetail = appUserVipDetailClient.getAppUserVipDetail(getAppUserVipDetail).getData();
            if(null != vipDetail){
                Integer chargeNum = vipDetail.getChargeNum();
                if(0 > chargeNum){
                    chargingOrder.setVipDiscount(vipDetail.getDiscount());
                    BigDecimal discountAmount = addChargingOrder.getPaymentAmount().multiply((new BigDecimal(10)
                            .subtract(vipDetail.getDiscount())).divide(new BigDecimal(10))).setScale(4, BigDecimal.ROUND_HALF_EVEN);
                    chargingOrder.setVipDiscountAmount(discountAmount);
                }
            }
        }
        this.save(chargingOrder);
        //会员优惠折扣将其计入增加充电时长(增加总充电金额)
        //如果充电总金额未使用完,则需要退回费用=(原金额/总金额)*(总金额-实际充电金额)
        if(1 == addChargingOrder.getPaymentType()){
            PaymentOrder paymentOrder = new PaymentOrder();
            paymentOrder.setCode(chargingOrder.getCode());
            paymentOrder.setAmount(addChargingOrder.getPaymentAmount());
            paymentOrder.setOpenId(appUser.getWxOpenid());
            paymentOrder.setDescription("充电充值");
            paymentOrder.setNotifyUrl("/order/t-charging-order/chargingOrderWXCallback");
            Map<String, Object> data = wxPaymentClient.orderPay(paymentOrder).getData();
            return AjaxResult.success(data);
        }
        if(2 == addChargingOrder.getPaymentType()){
        }
        throw new RuntimeException("无效的支付方式");
    }
    /**
     * 支付成功后的回调处理逻辑
     * @param paymentType 支付方式:1=微信,2=支付宝
     * @param out_trade_no 业务流水号
     * @param transaction_id 三方支付流水号
     * @param attach 附加数据
     * @return
     */
    @Override
    public AjaxResult chargingOrderCallback(Integer paymentType, String out_trade_no, String transaction_id, String attach) {
        TChargingOrder chargingOrder = this.getOne(new LambdaQueryWrapper<TChargingOrder>().eq(TChargingOrder::getCode, out_trade_no));
        if(chargingOrder.getRechargePaymentStatus() == 2){
            return AjaxResult.success();
        }
        chargingOrder.setRechargePaymentStatus(2);
        chargingOrder.setRechargeSerialNumber(transaction_id);
        this.updateById(chargingOrder);
        //添加安全检测数据到缓存中,每步安全检测完成后需要更新缓存数据
        PreChargeCheck preChargeCheck = new PreChargeCheck();
        preChargeCheck.setElectronicLockLock(false);
        preChargeCheck.setInsulationTesting(false);
        preChargeCheck.setSecureConnectionDetection(false);
        String key = "AQJC_" + chargingOrder.getChargingGunId();
        redisService.setCacheObject(key, preChargeCheck);
        //开始检测充电桩状,将检查状态写入缓存。检测完成后开始开启充电
        //充电结束后需要计算退款剩余金额
        // todo 需要完善
        return AjaxResult.success();
    }
    /**
     * 获取充电中的详情
     * @param id
     * @return
     */
    @Override
    public ChargingDetails getChargingDetails(Integer id) {
        Long userId = tokenService.getLoginUserApplet().getUserId();
        TChargingOrder one = this.getOne(new LambdaQueryWrapper<TChargingOrder>().eq(TChargingOrder::getAppUserId, userId).eq(TChargingOrder::getChargingGunId, id)
                .in(TChargingOrder::getStatus, Arrays.asList(1, 2, 3)).eq(TChargingOrder::getRechargePaymentStatus, 2).eq(TChargingOrder::getDelFlag, 0));
        if(null == one){
            return null;
        }
        // todo 需要完善充电实时数据
        ChargingDetails chargingDetails = new ChargingDetails();
        chargingDetails.setId(one.getId().toString());
        TChargingGun chargingGun = chargingGunClient.getChargingGunById(id).getData();
        TChargingPile chargingPile = chargingPileClient.getChargingPileById(chargingGun.getChargingPileId()).getData();
        Site site = siteClient.getSiteByIds(Arrays.asList(chargingPile.getSiteId())).getData().get(0);
        chargingDetails.setName(site.getName() + "-" + chargingPile.getName());
        chargingDetails.setCode(one.getCode());
        chargingDetails.setStatus(one.getStatus());
        return chargingDetails;
    }
    /**
     * 停止充电操作
     * @param id 订单id
     * @return
     */
    @Override
    public AjaxResult stopCharging(String id) {
        TChargingOrder chargingOrder = this.getById(id);
        Integer status = chargingOrder.getStatus();
        if(status == 4 || status == 5){
            return AjaxResult.error("不能重复操作");
        }
        chargingOrder.setStatus(4);
        chargingOrder.setEndMode(1);
        this.updateById(chargingOrder);
        //调用硬件停止充电,停止成功后开始计算费用退款
        // todo 待完善
        return AjaxResult.success();
    }
}
ruoyi-service/ruoyi-other/pom.xml
@@ -117,6 +117,10 @@
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>io.netty</groupId>
            <artifactId>netty-all</artifactId>
        </dependency>
    </dependencies>
    <build>
ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/RuoYiOtherApplication.java
@@ -3,6 +3,7 @@
import com.ruoyi.common.security.annotation.EnableCustomConfig;
import com.ruoyi.common.security.annotation.EnableRyFeignClients;
import com.ruoyi.common.swagger.annotation.EnableCustomSwagger2;
import com.ruoyi.other.webSocket.NettyServer;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@@ -23,6 +24,7 @@
public class RuoYiOtherApplication {
    public static void main(String[] args) {
        SpringApplication.run(RuoYiOtherApplication.class, args);
        new NettyServer().bind();
        System.out.println("(♥◠‿◠)ノ゙  基础模块启动成功   ლ(´ڡ`ლ)゙  \n" +
                " .-------.       ____     __        \n" +
                " |  _ _   \\      \\   \\   /  /    \n" +
ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/controller/TActivityController.java
@@ -12,6 +12,8 @@
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.Arrays;
/**
 * <p>
 *  前端控制器
@@ -34,8 +36,8 @@
    }
    @GetMapping("/delete")
    @ApiOperation(tags = {"管理后台-活动管理"},value = "活动删除")
    public AjaxResult delete(Integer id) {
        activityService.removeById(id);
    public AjaxResult delete(String ids) {
        activityService.removeBatchByIds(Arrays.asList(ids.split(",")));
        return AjaxResult.success();
    }
    @PostMapping("/updateVip")
ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/controller/TAdvertisingController.java
@@ -18,6 +18,7 @@
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.Arrays;
import java.util.List;
/**
@@ -42,8 +43,8 @@
    }
    @GetMapping("/delete")
    @ApiOperation(tags = {"管理后台-广告管理"},value = "广告删除")
    public AjaxResult delete(Integer id) {
        advertisingService.removeById(id);
    public AjaxResult delete(String ids) {
        advertisingService.removeBatchByIds(Arrays.asList(ids.split(",")));
        return AjaxResult.success();
    }
    @PostMapping("/updateVip")
ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/controller/TCouponController.java
@@ -28,6 +28,7 @@
import javax.annotation.Resource;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
@@ -60,8 +61,8 @@
    @GetMapping("/delete")
    @ApiOperation(tags = {"管理后台-优惠券管理"},value = "优惠券删除")
    public AjaxResult delete(Integer id) {
        tCouponService.removeById(id);
    public AjaxResult delete(String ids) {
        tCouponService.removeBatchByIds(Arrays.asList(ids.split(",")));
        return AjaxResult.success();
    }
ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/controller/TEnterpriseUserApplicationController.java
@@ -4,11 +4,13 @@
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.account.api.dto.EnterpriseQuery;
import com.ruoyi.account.api.feignClient.AppUserClient;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.core.web.domain.AjaxResult;
import com.ruoyi.common.log.annotation.Log;
import com.ruoyi.common.log.enums.BusinessType;
import com.ruoyi.common.security.service.TokenService;
import com.ruoyi.other.api.domain.TEnterpriseUserApplication;
import com.ruoyi.other.query.PassDto;
import com.ruoyi.other.service.TEnterpriseUserApplicationService;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
@@ -45,7 +47,7 @@
     * 添加计费策略管理
     */
    @Log(title = "添加集团用户申请", businessType = BusinessType.INSERT)
    @ApiOperation(tags = {"小程序-集团用户","后台-申请表单-集团用户"},value = "添加集团用户申请")
    @ApiOperation(tags = {"小程序-集团用户"},value = "添加集团用户申请")
    @PostMapping(value = "/add")
    public AjaxResult<Boolean> add(@Validated @RequestBody TEnterpriseUserApplication dto) {
        // 用户id
@@ -53,31 +55,40 @@
        return AjaxResult.ok(enterpriseUserApplicationService.save(dto));
    }
    @Log(title = "添加集团用户申请", businessType = BusinessType.INSERT)
    @ApiOperation(tags = {"后台-申请表单-集团用户"},value = "后台添加集团用户申请")
    @PostMapping(value = "/back/add")
    public AjaxResult<Boolean> backAdd(@Validated @RequestBody TEnterpriseUserApplication dto) {
        // 用户id
        return AjaxResult.ok(enterpriseUserApplicationService.save(dto));
    }
    @ApiOperation(tags = {"后台-申请表单-集团用户"},value = "集团用户列表")
    @PostMapping(value = "/page")
    public AjaxResult<Page<TEnterpriseUserApplication>> page(EnterpriseQuery enterpriseQuery) {
    public R<Page<TEnterpriseUserApplication>> page(EnterpriseQuery enterpriseQuery) {
        // 用户id
        if (enterpriseQuery.getPhone()!=null) {
            List<Long> userIds = appUserClient.getUserIdsByPhone(enterpriseQuery.getPhone()).getData();
            if (userIds.isEmpty()){
                return AjaxResult.warn("未查询到用户");
                return R.ok();
            }
            Page<TEnterpriseUserApplication> page = enterpriseUserApplicationService.lambdaQuery().in(TEnterpriseUserApplication::getAppUserId, userIds).page(Page.of(enterpriseQuery.getPageCurr(), enterpriseQuery.getPageSize()));
        return AjaxResult.ok(page);
        return R.ok(page);
        }else {
            Page<TEnterpriseUserApplication> page = enterpriseUserApplicationService.lambdaQuery().page(Page.of(enterpriseQuery.getPageCurr(), enterpriseQuery.getPageSize()));
            return AjaxResult.ok(page);
            return R.ok(page);
        }
    }
    @ApiOperation(tags = {"后台-申请表单-集团用户"},value = "备注")
    @GetMapping(value = "/remark")
    public AjaxResult pass(Integer id,String remark) {
    @PostMapping(value = "/remark")
    public AjaxResult pass(@RequestBody PassDto passDto) {
        // 用户id
        TEnterpriseUserApplication byId = enterpriseUserApplicationService.getById(id);
        byId.setRemark(remark);
        TEnterpriseUserApplication byId = enterpriseUserApplicationService.getById(passDto.getId());
        byId.setRemark(passDto.getRemark());
        enterpriseUserApplicationService.updateById(byId);
        return AjaxResult.success();
    }
ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/controller/TGoodsController.java
@@ -1,6 +1,9 @@
package com.ruoyi.other.controller;
import java.time.LocalDateTime;
import com.ruoyi.account.api.dto.GrantCouponDto;
import com.ruoyi.account.api.feignClient.AppCouponClient;
import com.ruoyi.account.api.feignClient.AppUserClient;
import com.ruoyi.account.api.model.TAppUser;
import com.ruoyi.common.core.domain.R;
@@ -9,19 +12,21 @@
import com.ruoyi.common.core.web.page.PageInfo;
import com.ruoyi.common.security.service.TokenService;
import com.ruoyi.order.api.feignClient.OrderClient;
import com.ruoyi.other.api.domain.TActivity;
import com.ruoyi.other.api.domain.TCoupon;
import com.ruoyi.other.api.domain.TGoods;
import com.ruoyi.common.core.dto.ExchangeDto;
import com.ruoyi.other.api.dto.AdvertisingDTO;
import com.ruoyi.other.api.dto.ExchangeDto;
import com.ruoyi.order.api.vo.ExchangeDto;
import com.ruoyi.other.api.dto.GoodsDTO;
import com.ruoyi.other.service.TActivityService;
import com.ruoyi.other.service.TAdvertisingService;
import com.ruoyi.other.service.TCouponService;
import com.ruoyi.other.service.TGoodsService;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import java.util.Arrays;
/**
 * <p>
@@ -44,6 +49,10 @@
    private AppUserClient appUserClient;
    @Resource
    private OrderClient orderClient;
    @Resource
    private AppCouponClient appCouponClient;
    @Resource
    private TCouponService couponService;
@@ -59,8 +68,8 @@
    @GetMapping("/delete")
    @ApiOperation(tags = {"管理后台-商品管理"},value = "商品删除")
    public AjaxResult delete(Integer id) {
        goodsService.removeById(id);
    public AjaxResult delete(String ids) {
        goodsService.removeBatchByIds(Arrays.asList(ids.split(",")));
        return AjaxResult.success();
    }
@@ -99,31 +108,51 @@
    @ApiOperation(tags = {"小程序-兑换商城"},value = "兑换商品")
    @PostMapping(value = "/app/shop")
    public AjaxResult<PageInfo<TGoods>> shop(@RequestBody ExchangeDto exchangeDto) {
        //查询当前商品信息
        TGoods good = goodsService.getById(exchangeDto.getGoodId());
        //检查当前用户积分是否够
        Long userId = tokenService.getLoginUserApplet().getUserId();
        Integer point = 0;
        if (exchangeDto.getGoodType()==1) {
            //查询当前商品信息
            TGoods good = goodsService.getById(exchangeDto.getGoodId());
            point = good.getRedeemPoints();
            //检查当前用户是否到达兑换上限
            Long count = orderClient.getExchangeById(exchangeDto.getGoodId(), userId,exchangeDto.getGoodType()).getData();
            if (good.getLimitExchangeTimes() != -1 && count >= good.getLimitExchangeTimes()) {
                return AjaxResult.error("当前用户已到达兑换上限");
            }
        TAppUser user = appUserClient.getUserById(userId).getData();
        if (user.getPoints()<good.getRedeemPoints()){
            return AjaxResult.error("当前用户积分不足");
             }
        }else {
            TCoupon coupon = couponService.getById(exchangeDto.getGoodId());
            point = coupon.getRedeemPoints();
            Long count = orderClient.getExchangeById(exchangeDto.getGoodId(), userId,exchangeDto.getGoodType()).getData();
            if (coupon.getInventoryQuantity() != -1 && count >= coupon.getInventoryQuantity()) {
                return AjaxResult.error("当前用户已到达兑换上限");
            }
        }
        //检查当前用户是否到达兑换上限
        Long count = orderClient.getExchangeById(exchangeDto.getGoodId(), userId).getData();
        if (good.getLimitExchangeTimes()!=-1&&count>=good.getLimitExchangeTimes()){
            return AjaxResult.error("当前用户已到达兑换上限");
        }
        exchangeDto.setPoint(point);
        //生成积分兑换成功的订单
        orderClient.exchangeCreate(exchangeDto);
        //如果是优惠卷,赠送优惠卷给用户
        if (exchangeDto.getGoodType()==2) {
            //如果是优惠卷,赠送优惠卷给用户
            GrantCouponDto grantCouponDto = new GrantCouponDto();
            grantCouponDto.setUserIds(userId.toString());
            grantCouponDto.setCouponId(exchangeDto.getGoodId());
            grantCouponDto.setWaysToObtain(1);
            grantCouponDto.setEndTime(LocalDateTime.now());
            appCouponClient.grantCoupon(grantCouponDto);
        }
        //生成消耗积分的记录
        return AjaxResult.success();
    }
    
ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/controller/TVipController.java
@@ -26,6 +26,7 @@
import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
@@ -53,8 +54,8 @@
    }
    @GetMapping("/delete")
    @ApiOperation(tags = {"管理后台-会员管理"},value = "会员删除")
    public AjaxResult delete(Integer id) {
        vipService.removeById(id);
    public AjaxResult delete(String ids) {
        vipService.removeBatchByIds(Arrays.asList(ids.split(",")));
        return AjaxResult.success();
    }
    @PostMapping("/updateVip")
ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/controller/WebSocketController.java
New file
@@ -0,0 +1,31 @@
package com.ruoyi.other.controller;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.other.webSocket.NettyChannelMap;
import com.ruoyi.other.webSocket.NettyWebSocketController;
import io.netty.channel.ChannelHandlerContext;
import org.springframework.web.bind.annotation.*;
/**
 * @author zhibing.pu
 * @Date 2024/8/21 17:31
 */
@RestController
@RequestMapping("/webSocket")
public class WebSocketController {
    /**
     * 发送websocket消息
     * @param userId
     * @param msg
     * @return
     */
    @ResponseBody
    @PostMapping("/send")
    public R send(@RequestParam("userId") Long userId, @RequestParam("msg") String msg){
        ChannelHandlerContext channel = NettyChannelMap.getData("Applets" + userId);
        return NettyWebSocketController.sendMsgToClient(channel, msg);
    }
}
ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/query/PassDto.java
New file
@@ -0,0 +1,13 @@
package com.ruoyi.other.query;
import lombok.Data;
/**
 * @author zhibing.pu
 * @date 2024/8/20 18:25
 */
@Data
public class PassDto {
    Integer id;
    String remark;
}
ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/service/impl/TCouponServiceImpl.java
@@ -35,13 +35,15 @@
        for (TCoupon tCoupon : list) {
            couponIds.append(tCoupon.getId()).append(",");
        }
        if (StringUtils.hasLength(couponIds)){
            // 去除最后一个字符
            couponIds.deleteCharAt(couponIds.length()-1);
        }
        List<Integer> data = appCouponClient.getCountByCouponIds(couponIds.toString()).getData();
        for (int i = 0; i < list.size(); i++) {
            list.get(i).setCount(data.get(i));
        if (!list.isEmpty()){
            if (StringUtils.hasLength(couponIds)){
                // 去除最后一个字符
                couponIds.deleteCharAt(couponIds.length()-1);
            }
            List<Integer> data = appCouponClient.getCountByCouponIds(couponIds.toString()).getData();
            for (int i = 0; i < list.size(); i++) {
                list.get(i).setCount(data.get(i));
            }
        }
        pageInfo.setRecords(list);
        return pageInfo;
ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/webSocket/ChildChannelHandler.java
New file
@@ -0,0 +1,36 @@
package com.ruoyi.other.webSocket;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpServerCodec;
import io.netty.handler.ssl.SslHandler;
import io.netty.handler.stream.ChunkedWriteHandler;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLEngine;
public class ChildChannelHandler extends ChannelInitializer<SocketChannel> {
    @Override
    protected void initChannel(SocketChannel socketChannel) throws Exception {
        String path = "C:\\cert\\tomcat\\scs1723443475715_fxcx.yunyu666.com_server.jks";
//        String path = "/usr/local/server/apache-tomcat-80/conf/cert/6064978_okyueche.com.pfx";
        SSLContext sslContext = createSSLContext.createSSLContext("JKS"
                , path, "Pe7>4nS#st$dAnpp");
        //SSLEngine 此类允许使用ssl安全套接层协议进行安全通信
        SSLEngine engine = sslContext.createSSLEngine();
        engine.setUseClientMode(false);
        socketChannel.pipeline().addLast("ssl", new SslHandler(engine));
        // 设置30秒没有读到数据,则触发一个READER_IDLE事件。
        // pipeline.addLast(new IdleStateHandler(30, 0, 0));
        // HttpServerCodec:将请求和应答消息解码为HTTP消息
        socketChannel.pipeline().addLast("http-codec", new HttpServerCodec());
        // HttpObjectAggregator:将HTTP消息的多个部分合成一条完整的HTTP消息
        socketChannel.pipeline().addLast("aggregator", new HttpObjectAggregator(65536));
        // ChunkedWriteHandler:向客户端发送HTML5文件
        socketChannel.pipeline().addLast("http-chunked", new ChunkedWriteHandler());
        // 在管道中添加我们自己的接收数据实现方法
        socketChannel.pipeline().addLast("handler", new WebSocketHandler());
    }
}
ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/webSocket/Global.java
New file
@@ -0,0 +1,9 @@
package com.ruoyi.other.webSocket;
import io.netty.channel.group.ChannelGroup;
import io.netty.channel.group.DefaultChannelGroup;
import io.netty.util.concurrent.GlobalEventExecutor;
public class Global {
    public static ChannelGroup group = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
}
ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/webSocket/Method.java
New file
@@ -0,0 +1,33 @@
package com.ruoyi.other.webSocket;
/**
 * 即时通讯【通讯类型类】
 *
 * @author TaoNingBo
 * @version 1.0
 * @createDate 2016年6月14日
 */
public class Method {
    /**
     * 心跳【推送】
     */
    public static final String ok = "OK";
    /**
     * 心跳【接收】
     */
    public final static String ping = "PING";
    /**
     * 心跳【响应】
     */
    public final static String pong = "PONG";
    /**
     * 司机上传位置
     */
    public static final String location = "LOCATION";
}
ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/webSocket/NettyChannelMap.java
New file
@@ -0,0 +1,123 @@
package com.ruoyi.other.webSocket;
import io.netty.channel.ChannelHandlerContext;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
public class NettyChannelMap {
    public static Map<String, ChannelHandlerContext> ctxMap = new HashMap<>();//单点登录存储的通道
    protected static Map<String, ChannelHandlerContext> map = new ConcurrentHashMap<>();
    private NettyChannelMap() {
        // 放置外部实例化
    }
    /**
     * Get data from source.
     *
     * @param key
     * @return
     */
    public static ChannelHandlerContext getData(String key) {
        if (map == null) {
            map = new HashMap<String, ChannelHandlerContext>();
        }
        return map.get(key);
    }
    /**
     * Save data from source.
     *
     * @param key
     * @param val
     */
    public static synchronized void saveData(String key, ChannelHandlerContext val) {
        map.put(key, val);
    }
    /**
     * Determine whether the cache key contains the key.
     *
     * @param key
     * @return true|false
     * @author TaoNingBo
     */
    public static synchronized boolean containsKey(String key) {
        return map.containsKey(key);
    }
    /**
     * Determine whether the cache value contains the value.
     *
     * @param val
     * @return
     */
    public static synchronized boolean containsVal(ChannelHandlerContext val) {
        return map.containsValue(val);
    }
    /**
     * Remove the data resources.
     *
     * @param value
     */
    @SuppressWarnings("rawtypes")
    public static synchronized void remove(ChannelHandlerContext value) {
        Set<String> strings = map.keySet();
        for (String key : strings) {
            ChannelHandlerContext channelHandlerContext = map.get(key);
            String s = channelHandlerContext.channel().remoteAddress().toString();
            String s1 = value.channel().remoteAddress().toString();
            if (s.equals(s1)) {
                channelHandlerContext.close();//关闭通道
                map.remove(key);
            }
        }
    }
    public static synchronized void remove_(ChannelHandlerContext value) {
        Set<String> strings = ctxMap.keySet();
        for (String key : strings) {
            ChannelHandlerContext channelHandlerContext = ctxMap.get(key);
            String s = channelHandlerContext.channel().remoteAddress().toString();
            String s1 = value.channel().remoteAddress().toString();
            if (s.equals(s1)) {
                channelHandlerContext.close();//关闭通道
                ctxMap.remove(key);
            }
        }
    }
    /**
     * Remove the data resources.
     *
     * @param key
     * @author TaoNingBo
     */
    public static synchronized void remove(String key) {
        map.remove(key);
    }
    /**
     * Update the data resources.
     *
     * @param key
     * @param value
     */
    public static synchronized void update(String key, ChannelHandlerContext value) {
        map.put(key, value);
    }
    public static synchronized void update_(String key, ChannelHandlerContext value) {
        ctxMap.put(key, value);
    }
}
ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/webSocket/NettyMsg.java
New file
@@ -0,0 +1,94 @@
package com.ruoyi.other.webSocket;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandlerContext;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class NettyMsg {
    /**
     * 返回一个正确数据
     *
     * @param method
     * @param data
     * @return
     * @author TaoNingBo
     */
    public static String setMsg(String method, Map<String, Object> data) {
        StringBuffer json = new StringBuffer();
        json.append(getHeader(200, "SUCCESS", method));
        json.append(JSON.toJSONString(data));
        json.append("}");
        //return JSON.toJSONString(json);
        return json.toString();
    }
    /**
     * 返回一个正确数据
     *
     * @param method
     * @param data
     * @return
     */
    public static String setMsg(String method, List<Map<String, Object>> data) {
        StringBuffer json = new StringBuffer();
        json.append(getHeader(200, "SUCCESS", method));
        List<JSONObject> jsonList = new ArrayList<JSONObject>();
        for (Map<String, Object> map : data) {
            JSONObject dataJson = new JSONObject(map);
            jsonList.add(dataJson);
        }
        json.append(jsonList);
        json.append("}");
//        return JSON.toJSONString(json);
        return json.toString();
    }
    /**
     * 返回一个错误数据
     *
     * @param method
     * @param data
     * @return
     * @author TaoNingBo
     */
    public static String setErrMsg(String method, String data) {
        StringBuffer json = new StringBuffer();
        json.append(getHeader(-1, "FAILURE", method));
        json.append("\"" + data + "\"");
        json.append("}");
//        return JSON.toJSONString(json);
        return json.toString();
    }
    /**
     * 生成一个返回JSON的头
     *
     * @param code
     * @param msg
     * @param method
     * @return
     * @author TaoNingBo
     */
    private static String getHeader(int code, String msg, String method) {
        StringBuffer header = new StringBuffer();
        header.append("{");
        header.append("\"code\":\"" + code);
        header.append("\",\"msg\":\"" + msg);
        header.append("\",\"method\":\"" + method);
        header.append("\",\"data\":");
        return header.toString();
    }
}
ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/webSocket/NettyServer.java
New file
@@ -0,0 +1,77 @@
package com.ruoyi.other.webSocket;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.Channel;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import java.util.Timer;
import java.util.TimerTask;
/**
 * 即时通讯服务启动类
 *
 * @version 1.0
 * @date 2016年6月25日
 */
public class NettyServer {
    /**
     * 延迟启动设置
     * <p>
     * NettyServer启动方法.
     */
    public void bind() {
        final Thread thread = new Thread(new NettyRunnable());
        Timer timer = new Timer();
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                thread.start();
            }
        }, 1000 * 2);
    }
    /**
     * 即时通讯服务启动
     *
     * @version 1.0
     * @date 2016年6月24日
     */
    public class NettyRunnable implements Runnable {
        /**
         * 获取即时通讯启动端口
         */
        @Override
        public void run() {
            System.out.println("===========================Netty端口启动========");
            // Boss线程:由这个线程池提供的线程是boss种类的,用于创建、连接、绑定socket,
            // (有点像门卫)然后把这些socket传给worker线程池。
            // 在服务器端每个监听的socket都有一个boss线程来处理。在客户端,只有一个boss线程来处理所有的socket。
            EventLoopGroup bossGroup = new NioEventLoopGroup();
            // Worker线程:Worker线程执行所有的异步I/O,即处理操作
            EventLoopGroup workrGroup = new NioEventLoopGroup();
            try {
                // ServerBootstrap 启动NIO服务的辅助启动类,负责初始话netty服务器,并且开始监听端口的socket请求
                ServerBootstrap b = new ServerBootstrap();
                b.group(bossGroup, workrGroup);
                // 设置非阻塞,用它来建立新accept的连接,用于构造serversocketchannel的工厂类
                b.channel(NioServerSocketChannel.class);
                // ChildChannelHandler 对出入的数据进行的业务操作,其继承ChannelInitializer
                b.childHandler(new ChildChannelHandler());
                System.out.println("服务端开启等待客户端连接 ... ...");
                Channel ch = b.bind(9090).sync().channel();
                ch.closeFuture().sync();
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                bossGroup.shutdownGracefully();
                workrGroup.shutdownGracefully();
            }
        }
    }
}
ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/webSocket/NettyWebSocketController.java
New file
@@ -0,0 +1,154 @@
package com.ruoyi.other.webSocket;
import com.alibaba.fastjson.JSONObject;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.core.utils.StringUtils;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import java.util.Hashtable;
public class NettyWebSocketController {
    public static Hashtable<String, Hashtable<ChannelHandlerContext, String>> map = new Hashtable<String, Hashtable<ChannelHandlerContext, String>>();
    public static Hashtable<String, String> table;
    public static int i = 0;
    static {
        if (table == null) {
            table = new Hashtable<>();
        }
    }
    /**
     * 向客户端发送消息
     *
     * @param ctx
     * @param msg
     * @author TaoNingBo
     */
    public static R sendMsgToClient(ChannelHandlerContext ctx, String msg) {
        if (ctx != null && ctx.channel().isActive()) {
            ByteBuf buffer = Unpooled.copiedBuffer((msg).getBytes());
            ChannelFuture sync;
            try {
                sync = ctx.channel().writeAndFlush(new TextWebSocketFrame(msg)).sync();
                if (!sync.isSuccess()) {
                    boolean b = true;
                    for (int i = 0; i < 10; i++) {
                        ctx.wait(3000);
                        sync = ctx.channel().write(new TextWebSocketFrame(msg)).sync();
                        if (sync.isSuccess()) {
                            b = false;
                            break;
                        }
                        System.err.println("小程序-》推送不成功,将继续推送" + msg);
                    }
                    if (b) {
                        NettyChannelMap.remove(ctx);
                        return R.fail("无效的消息通道");
                    }
                }
                return R.ok();
            } catch (Exception e) {
                NettyChannelMap.remove(ctx);
                e.printStackTrace();
                return R.fail("发送消息失败:" + e.getMessage());
            }
        } else {
            NettyChannelMap.remove(ctx);
            return R.fail("无效的消息通道");
        }
    }
    //    **链接断开 将推送消息记录
    public static void sendMsgToClient(String cacheType, Integer id, String msg) {
        ChannelHandlerContext ctx = NettyChannelMap.getData(cacheType + id);
        if (ctx != null) {
            ChannelFuture sync;
            try {
                sync = ctx.channel().write(new TextWebSocketFrame(msg)).sync();
                if (!sync.isSuccess()) {
                    for (int i = 0; i < 10; i++) {
                        sync = ctx.channel().write(new TextWebSocketFrame(msg)).sync();
                        ;
                        if (!sync.isSuccess()) {
                            sync = ctx.channel().write(new TextWebSocketFrame(msg)).sync();
                            ;
                            System.err.println("推送不成功,将继续推送" + msg);
                            if (i == 9) {
                                table.put(cacheType + id, msg);
                                ctx.close();
                                System.err.println("推送发生异常,记录:" + msg);
                            }
                        } else {
                            break;
                        }
                    }
                }
            } catch (Exception e) {
                table.put(cacheType + id, msg);
                System.err.println("推送发生异常,记录:" + msg);
            }
        } else {
            table.put(cacheType + id, msg);
            System.err.println("链接断开,记录:id=" + cacheType + id + ",消息:" + msg);
        }
    }
    /**
     * 判断客户端要执行什么操作
     *
     * @param ctx
     * @param msg
     * @author TaoNingBo
     */
    public void JudgeOperation(ChannelHandlerContext ctx, String msg) {
        try {
            // 验证即时通讯命令是否正确有效
            if (StringUtils.isEmpty(msg)) {
                return;
            }
            String msgStr = msg.toString();
            if (msgStr.indexOf("{") == -1 || msgStr.indexOf("}") == -1 || msgStr.indexOf("code") == -1 || msgStr.indexOf("msg") == -1 || msgStr.indexOf("data") == -1 || msgStr.indexOf("method") == -1) {
                return;
            }
            // 获取socket信息,保存相应的socket
            JSONObject jsonMsg = JSONObject.parseObject(msg.toString());
            int code = jsonMsg.getIntValue("code");
            String message = jsonMsg.getString("msg");
            String method = jsonMsg.getString("method");
            if (code != 200 || !message.equals("SUCCESS")) {
                return;
            }
            JSONObject jsonCon = JSONObject.parseObject(jsonMsg.get("data").toString());
            if (null != ctx && ctx.channel().isActive()) {
                jsonMsg.put("method", Method.pong);
                sendMsgToClient(ctx, jsonMsg.toJSONString());
            }
            // ############################### 心跳  ############################
            // 心跳
            if (method.equals(Method.ping)) {
                String userId1 = jsonCon.getString("userId");
                if (StringUtils.isNotEmpty(userId1)) {
                    //存储业务使用的通道
                    if (null != ctx && ctx.channel().isActive()) {
                        NettyChannelMap.update("Applets" + userId1, ctx);
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/webSocket/WebSocketHandler.java
New file
@@ -0,0 +1,160 @@
package com.ruoyi.other.webSocket;
import com.alibaba.fastjson.JSONObject;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.*;
import io.netty.handler.codec.http.websocketx.*;
import io.netty.handler.timeout.IdleState;
import io.netty.handler.timeout.IdleStateEvent;
import io.netty.util.CharsetUtil;
import java.util.HashMap;
public class WebSocketHandler extends SimpleChannelInboundHandler<Object> {
    private static final String WEB_SOCKET_URL = "wss://localhost:9090/websocket";
    //用于websocket握手的处理类
    private WebSocketServerHandshaker handshaker;
    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
        ctx.flush();
    }
    /**
     * 心跳
     */
    @Override
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
        if (evt instanceof IdleStateEvent) {
            IdleStateEvent event = (IdleStateEvent) evt;
            if (event.state().equals(IdleState.READER_IDLE)) {
                //
            } else if (event.state().equals(IdleState.WRITER_IDLE)) {
                //
            } else if (event.state().equals(IdleState.ALL_IDLE)) {
                String msg = NettyMsg.setMsg(Method.ok, new HashMap<String, Object>());
                if (ctx != null && ctx.channel().isActive()) {
                    ctx.writeAndFlush(Unpooled.copiedBuffer((msg).getBytes()));
                }
            }
        }
//        super.userEventTriggered(ctx, evt);
    }
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        ctx.close();
    }
    private void handleHttpRequest(ChannelHandlerContext ctx, FullHttpRequest req) {
        // Http解码失败,向服务器指定传输的协议为Upgrade:websocket
        if (!req.getDecoderResult().isSuccess() || !("websocket").equals(req.headers().get("Upgrade"))) {
            sendHttpResponse(ctx, req, new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.BAD_REQUEST));
            return;
        }
        // 握手相应处理,创建websocket握手的工厂类,
        WebSocketServerHandshakerFactory wsFactory = new WebSocketServerHandshakerFactory(WEB_SOCKET_URL, null, false);
        // 根据工厂类和HTTP请求创建握手类
        handshaker = wsFactory.newHandshaker(req);
        if (handshaker == null) {
            // 不支持websocket
            WebSocketServerHandshakerFactory.sendUnsupportedWebSocketVersionResponse(ctx.channel());
        } else {
            // 通过它构造握手响应消息返回给客户端
            handshaker.handshake(ctx.channel(), req);
        }
    }
    private void handleWebSocketRequest(ChannelHandlerContext ctx, WebSocketFrame req) throws Exception {
        if (req instanceof CloseWebSocketFrame) {
            // 关闭websocket连接
            handshaker.close(ctx.channel(), (CloseWebSocketFrame) req.retain());
            return;
        }
        if (req instanceof PingWebSocketFrame) {
            ctx.channel().write(new PongWebSocketFrame(req.content().retain()));
            return;
        }
        if (!(req instanceof TextWebSocketFrame)) {
            throw new UnsupportedOperationException("当前只支持文本消息,不支持二进制消息");
        }
        if (ctx == null || this.handshaker == null || ctx.isRemoved()) {
            throw new Exception("尚未握手成功,无法向客户端发送WebSocket消息");
        }
        String requestmsg = ((TextWebSocketFrame) req).text();
        //给连接的客户端返回数据
        //返回心跳
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("code", 200);
        jsonObject.put("method", Method.ok);
        jsonObject.put("msg", "SUCCESS");
        jsonObject.put("data", new JSONObject());
        TextWebSocketFrame tws = new TextWebSocketFrame(jsonObject.toJSONString());
//        ctx.channel().writeAndFlush(tws);
        new NettyWebSocketController().JudgeOperation(ctx, requestmsg);//小程序心跳处理
        // 群发服务端心跳响应
        Global.group.writeAndFlush(new TextWebSocketFrame((tws).text()));
    }
    private void sendHttpResponse(ChannelHandlerContext ctx, FullHttpRequest req, FullHttpResponse res) {
        // BAD_REQUEST(400) 客户端请求错误返回的应答消息
        if (res.getStatus().code() != 200) {
            ByteBuf buf = Unpooled.copiedBuffer(res.getStatus().toString(), CharsetUtil.UTF_8);
            res.content().writeBytes(buf);
            buf.release();
        }
        //服务端向客户端发送数据
        ChannelFuture f = ctx.channel().writeAndFlush(res);
        // 非法连接直接关闭连接
        if (res.getStatus().code() != 200) {
            f.addListener(ChannelFutureListener.CLOSE);
        }
    }
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        Global.group.add(ctx.channel());
        System.err.println("客户端与服务器端开启");
    }
    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        Global.group.remove(ctx.channel());
        NettyChannelMap.remove(ctx);
        System.err.println("客户端与服务器链接关闭");
    }
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        if (msg instanceof FullHttpRequest) {
            // websocket连接请求
            handleHttpRequest(ctx, (FullHttpRequest) msg);
        } else if (msg instanceof WebSocketFrame) {
            // websocket业务处理
            handleWebSocketRequest(ctx, (WebSocketFrame) msg);
        }
    }
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception {
        if (msg instanceof FullHttpRequest) {
            // websocket连接请求
            handleHttpRequest(ctx, (FullHttpRequest) msg);
        } else if (msg instanceof WebSocketFrame) {
            // websocket业务处理
            handleWebSocketRequest(ctx, (WebSocketFrame) msg);
        }
    }
}
ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/webSocket/createSSLContext.java
New file
@@ -0,0 +1,32 @@
package com.ruoyi.other.webSocket;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import java.io.FileInputStream;
import java.io.InputStream;
import java.security.KeyStore;
public class createSSLContext {
    /**
     * 获取SSLContext
     *
     * @param type
     * @param path
     * @param password
     * @return
     * @throws Exception
     */
    public static SSLContext createSSLContext(String type, String path, String password) throws Exception {
        KeyStore ks = KeyStore.getInstance(type); /// "JKS"
        InputStream ksInputStream = new FileInputStream(path); /// 证书存放地址
        ks.load(ksInputStream, password.toCharArray());
        //KeyManagerFactory充当基于密钥内容源的密钥管理器的工厂。
        KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());//getDefaultAlgorithm:获取默认的 KeyManagerFactory 算法名称。
        kmf.init(ks, password.toCharArray());
        //SSLContext的实例表示安全套接字协议的实现,它充当用于安全套接字工厂或 SSLEngine 的工厂。
        SSLContext sslContext = SSLContext.getInstance("TLS");
        sslContext.init(kmf.getKeyManagers(), null, null);
        return sslContext;
    }
}
ruoyi-service/ruoyi-other/src/main/resources/mapper/other/TActivityMapper.xml
@@ -23,16 +23,16 @@
    <select id="pageList" resultType="com.ruoyi.other.api.domain.TActivity">
        select * from t_activity
        <where>
            <if test="req.name != null and req.name != ''">
                AND  `name` LIKE concat('%',#{req.name}, '%')
            <if test="req.title != null and req.title != ''">
                AND  `name` LIKE concat('%',#{req.title}, '%')
            </if>
            <if test="req.state == 1">
            <if test="req.startState == 1">
                AND  start_time >= NOW()
            </if>
            <if test="req.state == 2">
            <if test="req.startState == 2">
                AND  start_time &lt;= NOW() AND end_time >= NOW()
            </if>
            <if test="req.state == 3">
            <if test="req.startState == 3">
                AND  end_time &lt;= NOW()
            </if>
            AND del_flag = ${@com.ruoyi.common.core.enums.DelFlagEnum@NO.getCode()}
ruoyi-service/ruoyi-other/src/main/resources/mapper/other/TAdvertisingMapper.xml
@@ -24,16 +24,16 @@
    <select id="pageList" resultType="com.ruoyi.other.api.domain.TAdvertising">
        select * from t_advertising
        <where>
            <if test="req.name != null and req.name != ''">
                AND  `name` LIKE concat('%',#{req.name}, '%')
            <if test="req.title != null and req.title != ''">
                AND  `name` LIKE concat('%',#{req.title}, '%')
            </if>
            <if test="req.state == 1">
            <if test="req.startState == 1">
                AND  start_time >= NOW()
            </if>
            <if test="req.state == 2">
            <if test="req.startState == 2">
                AND  start_time &lt;= NOW() AND end_time >= NOW()
            </if>
            <if test="req.state == 3">
            <if test="req.startState == 3">
                AND  end_time &lt;= NOW()
            </if>
            AND del_flag = ${@com.ruoyi.common.core.enums.DelFlagEnum@NO.getCode()}
ruoyi-service/ruoyi-other/src/main/resources/mybatis-config.xml
@@ -10,7 +10,7 @@
        <setting name="cacheEnabled" value="true"/>
        <!-- 延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。默认 false  -->
        <!-- <setting name="lazyLoadingEnabled" value="true"/> -->
        <setting name="mapUnderscoreToCamelCase" value="false"/><!--是否将map下划线方式转为驼峰式命名-->
        <setting name="mapUnderscoreToCamelCase" value="true"/><!--是否将map下划线方式转为驼峰式命名-->
        <!-- 当开启时,任何方法的调用都会加载该对象的所有属性。默认 false,可通过select标签的 fetchType来覆盖-->
        <!-- <setting name="aggressiveLazyLoading" value="false"/>-->
        <!--  Mybatis 创建具有延迟加载能力的对象所用到的代理工具,默认JAVASSIST -->
ruoyi-service/ruoyi-payment/pom.xml
@@ -112,6 +112,11 @@
            <artifactId>joda-time</artifactId>
            <version>2.10.10</version>
        </dependency>
        <dependency>
            <groupId>com.ruoyi</groupId>
            <artifactId>ruoyi-api-payment</artifactId>
        </dependency>
    </dependencies>
    <build>
ruoyi-service/ruoyi-payment/src/main/java/com/ruoyi/payment/wx/controller/WxPayController.java
@@ -1,9 +1,12 @@
package com.ruoyi.payment.wx.controller;
import com.fasterxml.jackson.core.type.TypeReference;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.core.web.domain.AjaxResult;
import com.ruoyi.payment.api.vo.PaymentOrder;
import com.ruoyi.payment.wx.enums.RefundEnum;
import com.ruoyi.payment.wx.model.WxPaymentRefundModel;
import com.ruoyi.payment.wx.resp.NotifyV3PayDecodeRespBody;
import com.ruoyi.payment.wx.utils.WxV3Pay;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
@@ -13,6 +16,7 @@
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.math.BigDecimal;
import java.util.Map;
import java.util.Objects;
@@ -32,22 +36,22 @@
    /**
     * 按实际修改
     */
    @PostMapping("order")
    @PostMapping("orderPay")
    @ApiOperation("订单支付")
    public AjaxResult<Map<String, Object>> orderPay(@RequestParam Long orderId) {
    public R<Map<String, Object>> orderPay(@RequestBody PaymentOrder paymentOrder) {
        // 查询订单
        // 0元订单不走支付
        // 价格
        Integer totalPrice = 0;
        Integer totalPrice = paymentOrder.getAmount().multiply(new BigDecimal(100)).intValue();
        // 生成订单号
        String orderNo = "";
        String orderNo = paymentOrder.getCode();
        // 查询用户信息 用户openid
        String openId = "";
        String openId = paymentOrder.getOpenId();
        // 订单做修改
        // 调用支付方法
        Map<String, Object> result = wxV3Pay.jsApi(orderNo, totalPrice, openId,"");
        Map<String, Object> result = wxV3Pay.jsApi(orderNo, totalPrice, openId, paymentOrder.getNotifyUrl(),paymentOrder.getDescription());
        log.info("支付参数:{}", result);
        return AjaxResult.ok(result);
        return R.ok(result);
    }
    /**
@@ -80,31 +84,33 @@
     * 支付回调
     */
    @PostMapping("pay/notify")
    public void payNotify(HttpServletRequest request) throws IOException {
    public R<Map<String, Object>> payNotify(HttpServletRequest request) throws Exception {
        try {
            Map<String, Object> params = wxV3Pay.verifyNotify(request, new TypeReference<Map<String, Object>>() {
            });
            Map<String, Object> params = wxV3Pay.verifyNotify(request, new TypeReference<Map<String, Object>>() {});
            log.info("支付回调:{}", params);
            // 商户订单号
            String tradeNo = params.get("out_trade_no").toString();
            // 交易状态
            String trade_state = params.get("trade_state").toString();
            // 交易状态描述
            String trade_state_desc = params.get("trade_state_desc").toString();
            // 微信支付订单号
            String transaction_id = params.get("transaction_id").toString();
            // 支付完成时间
            // 时间不对的话,可以调用  WxTimeUtils.toRfc3339Date(success_time)转换一下
            String success_time = params.get("success_time").toString();
            // 附加数据
            Integer attach = Integer.parseInt(params.get("attach").toString());
            //  TODO 业务处理
            return R.ok(params);
        } catch (Exception e) {
            log.error("支付回调异常:{}", e, e);
            wxV3Pay.ack(false, e.getMessage());
            return R.fail("回调异常");
        }
    }
    /**
     * 支付回调成功后
     */
    @PostMapping("pay/ack")
    public void ack(){
        try {
            wxV3Pay.ack();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
    /**
     * 退款回调
@@ -138,5 +144,25 @@
        }
    }
    /**
     * 查询订单信息
     * @param orderId
     * @return
     */
    @PostMapping("query/queryOrderInfo")
    public R<NotifyV3PayDecodeRespBody> queryOrderInfo(@RequestParam("orderId") String orderId){
        NotifyV3PayDecodeRespBody query = wxV3Pay.query(orderId);
        return R.ok(query);
    }
    /**
     * 关闭订单
     * @param outTradeNo
     */
    @PostMapping("pay/close")
    public void close(@RequestParam("outTradeNo") String outTradeNo){
        wxV3Pay.close(outTradeNo);
    }
}
ruoyi-service/ruoyi-payment/src/main/java/com/ruoyi/payment/wx/model/WxCloseOrderModel.java
New file
@@ -0,0 +1,22 @@
package com.ruoyi.payment.wx.model;
import lombok.*;
import java.util.List;
/**
 * @author xiaochen
 * @ClassName WxPaymentRefundModel
 * @Description
 */
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Getter
@Setter
@ToString
public class WxCloseOrderModel {
    private String mchid;
    private String out_trade_no;
}
ruoyi-service/ruoyi-payment/src/main/java/com/ruoyi/payment/wx/utils/WxAbstractPay.java
@@ -2,6 +2,7 @@
import com.fasterxml.jackson.core.type.TypeReference;
import com.ruoyi.common.core.utils.WebUtils;
import com.ruoyi.payment.wx.model.WxCloseOrderModel;
import com.ruoyi.payment.wx.model.WxPaymentInfoModel;
import com.ruoyi.payment.wx.model.WxPaymentRefundModel;
import com.ruoyi.payment.wx.resp.NotifyV3PayDecodeRespBody;
@@ -210,7 +211,7 @@
     * @param mchid
     * @return
     */
    public abstract NotifyV3PayDecodeRespBody query(String out_trade_no, String mchid);
    public abstract NotifyV3PayDecodeRespBody query(String out_trade_no);
    /**
@@ -220,6 +221,10 @@
     * @return
     */
    public abstract Map<String, Object> refund(WxPaymentRefundModel refundModel);
    public abstract String close(String out_trade_no);
    /**
     * 订单退款
@@ -329,5 +334,30 @@
            writer.close();
        }
    }
    /**
     * 关闭订单
     * @param httpClient
     * @param uri
     * @param httpReadTimeoutMs
     * @param httpConnectTimeoutMs
     * @param closeModel
     * @return
     */
    public String close(CloseableHttpClient httpClient,
                                      String uri,
                                      int httpReadTimeoutMs,
                                      int httpConnectTimeoutMs,
                                     WxCloseOrderModel closeModel) {
        String reqBody = WxJsonUtils.toJsonString(closeModel);
        //请求URL
        HttpEntityEnclosingRequestBase httpPost = requestPost(
                uri
                , httpReadTimeoutMs
                , httpConnectTimeoutMs, reqBody);
        String repBody = result(httpClient, httpPost);
        return repBody;
    }
}
ruoyi-service/ruoyi-payment/src/main/java/com/ruoyi/payment/wx/utils/WxV3Pay.java
@@ -5,6 +5,7 @@
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.ruoyi.payment.wx.model.WeixinProperties;
import com.ruoyi.payment.wx.model.WxCloseOrderModel;
import com.ruoyi.payment.wx.model.WxPaymentInfoModel;
import com.ruoyi.payment.wx.model.WxPaymentRefundModel;
import com.ruoyi.payment.wx.resp.NotifyV3PayDecodeRespBody;
@@ -120,12 +121,13 @@
     * @author xiaochen
     * @date 2022-03-22 12:47
     */
    public Map<String, Object> jsApi(String tradeNo, Integer amount, String openid, String description) {
    public Map<String, Object> jsApi(String tradeNo, Integer amount, String openid, String notify_url, String description) {
        WxPaymentInfoModel requestBody = WxPaymentInfoModel.builder()
                .mchid(this.config.getMchId())
                .appid(this.config.getAppId())
                .description(description)
                .out_trade_no(tradeNo)
                .notify_url(notify_url)
//                .attach("")
                .amount(WxPaymentInfoModel.Amount.builder().total(amount).build())
                .payer(WxPaymentInfoModel.Payer.builder().openid(openid).build())
@@ -169,15 +171,14 @@
     * 订单查询
     *
     * @param out_trade_no
     * @param mchid
     * @return com.abl.biz.center.payment.wx.v3.NotifyV3PayDecodeRespBody
     * @author xiaochen
     * @date 2021-12-20 16:47
     */
    @Override
    public NotifyV3PayDecodeRespBody query(String out_trade_no, String mchid) {
    public NotifyV3PayDecodeRespBody query(String out_trade_no) {
        String url =
                String.format("/v3/pay/transactions/out-trade-no/%s", out_trade_no) + String.format("?mchid=%s", mchid);
                String.format("/v3/pay/transactions/out-trade-no/%s", out_trade_no) + String.format("?mchid=%s", this.getConfig().getMchId());
        return query(this.httpClient, this.config.getHttpReadTimeoutMs(), this.config.getHttpConnectTimeoutMs(), url);
    }
@@ -193,5 +194,18 @@
//        refundModel.setNotify_url(this.config.getV3().getNotifyRefundUrl());
        return refund(this.httpClient, "/v3/refund/domestic/refunds", this.config.getHttpReadTimeoutMs(), this.config.getHttpConnectTimeoutMs(), refundModel);
    }
    /**
     * 关闭订单
     * @param out_trade_no
     * @return
     */
    @Override
    public String close(String out_trade_no) {
        String uri = String.format("/v3/pay/transactions/out-trade-no/%s/close", out_trade_no);
        WxCloseOrderModel wxCloseOrderModel = new WxCloseOrderModel();
        wxCloseOrderModel.setMchid(this.config.getMchId());
        return close(this.httpClient, uri, this.config.getHttpReadTimeoutMs(), this.config.getHttpConnectTimeoutMs(), wxCloseOrderModel);
    }
}