luodangjia
2024-08-17 0c0ca0349e7392cd08d8bb1417f51c320e77088d
Merge remote-tracking branch 'origin/master'

# Conflicts:
# ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/controller/TAppUserController.java
# ruoyi-service/ruoyi-chargingPile/src/main/java/com/ruoyi/chargingPile/controller/TAccountingStrategyController.java
143个文件已添加
115个文件已修改
9106 ■■■■■ 已修改文件
bin/clean.bat 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
bin/package.bat 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
bin/run-auth.bat 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
bin/run-gateway.bat 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
bin/run-modules-file.bat 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
bin/run-modules-gen.bat 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
bin/run-modules-job.bat 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
bin/run-modules-system.bat 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
bin/run-monitor.bat 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-api/ruoyi-api-account/src/main/java/com/ruoyi/account/api/dto/CouponListDto.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-api/ruoyi-api-account/src/main/java/com/ruoyi/account/api/factory/AppUserAddressFallbackFactory.java 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-api/ruoyi-api-account/src/main/java/com/ruoyi/account/api/factory/AppUserCarFallbackFactory.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-api/ruoyi-api-account/src/main/java/com/ruoyi/account/api/factory/AppUserFallbackFactory.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-api/ruoyi-api-account/src/main/java/com/ruoyi/account/api/factory/AppUserIntegralChangeFallbackFactory.java 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-api/ruoyi-api-account/src/main/java/com/ruoyi/account/api/feignClient/AppUserAddressClient.java 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-api/ruoyi-api-account/src/main/java/com/ruoyi/account/api/feignClient/AppUserCarClient.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-api/ruoyi-api-account/src/main/java/com/ruoyi/account/api/feignClient/AppUserClient.java 7 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-api/ruoyi-api-account/src/main/java/com/ruoyi/account/api/feignClient/AppUserIntegralChangeClient.java 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-api/ruoyi-api-account/src/main/java/com/ruoyi/account/api/vo/CarListVO.java 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-api/ruoyi-api-account/src/main/java/com/ruoyi/account/api/vo/CouponListVOVO.java 4 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-api/ruoyi-api-account/src/main/java/com/ruoyi/account/api/vo/ExchangeRecordVO.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-api/ruoyi-api-account/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-api/ruoyi-api-chargingPile/src/main/java/com/ruoyi/chargingPile/api/factory/ChargingGunFallbackFactory.java 41 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-api/ruoyi-api-chargingPile/src/main/java/com/ruoyi/chargingPile/api/factory/ChargingPileFallbackFactory.java 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-api/ruoyi-api-chargingPile/src/main/java/com/ruoyi/chargingPile/api/feignClient/ChargingGunClient.java 34 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-api/ruoyi-api-chargingPile/src/main/java/com/ruoyi/chargingPile/api/feignClient/ChargingPileClient.java 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-api/ruoyi-api-chargingPile/src/main/java/com/ruoyi/chargingPile/api/feignClient/ParkingLotClient.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-api/ruoyi-api-chargingPile/src/main/java/com/ruoyi/chargingPile/api/vo/SiteInfoVO.java 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-api/ruoyi-api-chargingPile/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-api/ruoyi-api-order/src/main/java/com/ruoyi/order/api/factory/ChargingOrderAccountingStrategyFallbackFactory.java 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-api/ruoyi-api-order/src/main/java/com/ruoyi/order/api/factory/ChargingOrderFallbackFactory.java 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-api/ruoyi-api-order/src/main/java/com/ruoyi/order/api/feignClient/ChargingOrderAccountingStrategyClient.java 16 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-api/ruoyi-api-order/src/main/java/com/ruoyi/order/api/feignClient/ChargingOrderClient.java 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-api/ruoyi-api-order/src/main/java/com/ruoyi/order/api/feignClient/OrderClient.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-api/ruoyi-api-order/src/main/java/com/ruoyi/order/api/model/TChargingOrder.java 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-api/ruoyi-api-order/src/main/java/com/ruoyi/order/api/model/TOrderAppeal.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-api/ruoyi-api-order/src/main/java/com/ruoyi/order/api/model/TOrderEvaluate.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-api/ruoyi-api-order/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-api/ruoyi-api-other/src/main/java/com/ruoyi/other/api/domain/TCoupon.java 9 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-api/ruoyi-api-other/src/main/java/com/ruoyi/other/api/domain/THtml.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-api/ruoyi-api-other/src/main/java/com/ruoyi/other/api/domain/TVip.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-api/ruoyi-api-other/src/main/java/com/ruoyi/other/api/factory/CouponFallbackFactory.java 34 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-api/ruoyi-api-other/src/main/java/com/ruoyi/other/api/factory/GoodsFallbackFactory.java 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-api/ruoyi-api-other/src/main/java/com/ruoyi/other/api/factory/IntegralRuleFallbackFactory.java 43 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-api/ruoyi-api-other/src/main/java/com/ruoyi/other/api/feignClient/CouponClient.java 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-api/ruoyi-api-other/src/main/java/com/ruoyi/other/api/feignClient/GoodsClient.java 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-api/ruoyi-api-other/src/main/java/com/ruoyi/other/api/feignClient/IntegralRuleClient.java 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-api/ruoyi-api-other/src/main/java/com/ruoyi/other/api/feignClient/RoleSiteClient.java 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-api/ruoyi-api-other/src/main/java/com/ruoyi/other/api/feignClient/TEvaluationTagClient.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-api/ruoyi-api-other/src/main/java/com/ruoyi/other/api/feignClient/UserSiteClient.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-api/ruoyi-api-other/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-api/ruoyi-api-system/src/main/java/com/ruoyi/system/api/domain/SysRole.java 46 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-api/ruoyi-api-system/src/main/java/com/ruoyi/system/api/domain/SysUser.java 4 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-api/ruoyi-api-system/src/main/java/com/ruoyi/system/api/feignClient/SysRoleClient.java 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-api/ruoyi-api-system/src/main/java/com/ruoyi/system/api/feignClient/SysUserClient.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-api/ruoyi-api-system/src/main/java/com/ruoyi/system/api/model/LoginUserApplet.java 56 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-api/ruoyi-api-system/src/main/java/com/ruoyi/system/api/model/TAppUserLoginInfo.java 79 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-auth/src/main/java/com/ruoyi/auth/controller/TokenController.java 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-auth/src/main/resources/bootstrap.yml 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/constant/CacheConstants.java 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/constant/SecurityConstants.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/constant/TokenConstants.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/exception/user/UserAppletException.java 48 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/JwtUtils.java 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/ObsUploadUtil.java 78 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-security/src/main/java/com/ruoyi/common/security/handler/GlobalExceptionHandler.java 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-security/src/main/java/com/ruoyi/common/security/service/TokenService.java 64 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-gateway/src/main/java/com/ruoyi/gateway/config/RouterFunctionConfiguration.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-gateway/src/main/java/com/ruoyi/gateway/filter/SignFilter.java 26 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/pom.xml 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/controller/SysRoleController.java 4 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/controller/SysUserController.java 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysOperLogMapper.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysDeptServiceImpl.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysMenuServiceImpl.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysOperLogServiceImpl.java 54 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/resources/bootstrap.yml 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysRoleMapper.xml 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysUserMapper.xml 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-account/pom.xml 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/ali/Constant/AliConstant.java 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/ali/model/AliProperties.java 70 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/ali/tools/AliAppletTools.java 37 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/controller/AliLoginController.java 91 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/controller/TAppCouponController.java 64 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/controller/TAppUserAddressController.java 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/controller/TAppUserCarController.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/controller/TAppUserController.java 81 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/controller/TAppUserIntegralChangeController.java 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/controller/WxLoginController.java 67 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/service/TAppUserService.java 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/service/impl/TAppCouponServiceImpl.java 76 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/service/impl/TAppUserServiceImpl.java 58 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/wx/body/resp/AccessTokenRespBody.java 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/wx/body/resp/Code2SessionRespBody.java 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/wx/body/resp/RespBody.java 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/wx/body/resq/Code2SessionResqBody.java 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/wx/model/WeixinProperties.java 80 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/wx/pojo/AppletPhoneEncrypteData.java 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/wx/pojo/AppletUserDecodeData.java 52 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/wx/pojo/AppletUserEncrypteData.java 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/wx/pojo/Watermark.java 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/wx/tools/JsonUtils.java 110 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/wx/tools/SHA1.java 36 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/wx/tools/WebUtils.java 48 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/wx/tools/WxAppletTools.java 125 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/wx/tools/WxCache.java 117 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/wx/tools/WxCacheTemplate.java 34 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/wx/tools/WxCaffineCache.java 122 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/wx/tools/WxException.java 55 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/wx/tools/WxJsonUtils.java 109 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/wx/tools/WxUtils.java 175 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-account/src/main/resources/bootstrap.yml 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-chargingPile/pom.xml 7 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-chargingPile/src/main/java/com/ruoyi/chargingPile/config/DataUpdateHandlerConfig.java 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-chargingPile/src/main/java/com/ruoyi/chargingPile/controller/PartnerController.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-chargingPile/src/main/java/com/ruoyi/chargingPile/controller/SiteController.java 74 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-chargingPile/src/main/java/com/ruoyi/chargingPile/controller/TAccountingStrategyController.java 79 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-chargingPile/src/main/java/com/ruoyi/chargingPile/controller/TChargingGunController.java 37 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-chargingPile/src/main/java/com/ruoyi/chargingPile/controller/TChargingPileController.java 30 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-chargingPile/src/main/java/com/ruoyi/chargingPile/controller/TChargingPileNotificationController.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-chargingPile/src/main/java/com/ruoyi/chargingPile/controller/TParkingLotController.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-chargingPile/src/main/java/com/ruoyi/chargingPile/dto/ChargingGunCountMonitoring.java 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-chargingPile/src/main/java/com/ruoyi/chargingPile/dto/ChargingGunMonitoring.java 38 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-chargingPile/src/main/java/com/ruoyi/chargingPile/dto/GetChargingGunMonitoring.java 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-chargingPile/src/main/java/com/ruoyi/chargingPile/mapper/SiteMapper.java 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-chargingPile/src/main/java/com/ruoyi/chargingPile/mapper/TChargingPileMapper.java 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-chargingPile/src/main/java/com/ruoyi/chargingPile/service/IPartnerService.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-chargingPile/src/main/java/com/ruoyi/chargingPile/service/ISiteService.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-chargingPile/src/main/java/com/ruoyi/chargingPile/service/TChargingPileService.java 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-chargingPile/src/main/java/com/ruoyi/chargingPile/service/impl/PartnerServiceImpl.java 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-chargingPile/src/main/java/com/ruoyi/chargingPile/service/impl/SiteServiceImpl.java 47 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-chargingPile/src/main/java/com/ruoyi/chargingPile/service/impl/TChargingPileServiceImpl.java 136 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-chargingPile/src/main/resources/bootstrap.yml 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-chargingPile/src/main/resources/mapper/chargingPile/SiteMapper.xml 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-chargingPile/src/main/resources/mapper/chargingPile/TChargingPileMapper.xml 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-integration/pom.xml 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-integration/src/main/java/com/ruoyi/integration/mongodb/model/AcquisitionBillingMode.java 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-integration/src/main/java/com/ruoyi/integration/mongodb/model/AcquisitionBillingModeReply.java 81 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-integration/src/main/java/com/ruoyi/integration/mongodb/model/BillingModeVerify.java 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-integration/src/main/java/com/ruoyi/integration/mongodb/model/BillingModeVerifyReply.java 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-integration/src/main/java/com/ruoyi/integration/mongodb/model/BmsAbort.java 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-integration/src/main/java/com/ruoyi/integration/mongodb/model/BmsDemandAndChargerExportation.java 36 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-integration/src/main/java/com/ruoyi/integration/mongodb/model/BmsInformation.java 36 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-integration/src/main/java/com/ruoyi/integration/mongodb/model/ChargingHandshake.java 39 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-integration/src/main/java/com/ruoyi/integration/mongodb/model/ChargingPileReturnsGroundLockData.java 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-integration/src/main/java/com/ruoyi/integration/mongodb/model/ChargingPileStartsCharging.java 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-integration/src/main/java/com/ruoyi/integration/mongodb/model/ClearOfflineCard.java 44 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-integration/src/main/java/com/ruoyi/integration/mongodb/model/ClearOfflineCardReply.java 91 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-integration/src/main/java/com/ruoyi/integration/mongodb/model/ConfirmTransactionRecord.java 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-integration/src/main/java/com/ruoyi/integration/mongodb/model/EndCharge.java 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-integration/src/main/java/com/ruoyi/integration/mongodb/model/ErrorMessage.java 47 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-integration/src/main/java/com/ruoyi/integration/mongodb/model/GroundLockRealTimeData.java 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-integration/src/main/java/com/ruoyi/integration/mongodb/model/MotorAbort.java 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-integration/src/main/java/com/ruoyi/integration/mongodb/model/Online.java 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-integration/src/main/java/com/ruoyi/integration/mongodb/model/OnlineReply.java 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-integration/src/main/java/com/ruoyi/integration/mongodb/model/OpenOrCloseGroundLock.java 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-integration/src/main/java/com/ruoyi/integration/mongodb/model/ParameterSetting.java 36 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-integration/src/main/java/com/ruoyi/integration/mongodb/model/Ping.java 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-integration/src/main/java/com/ruoyi/integration/mongodb/model/PlatformConfirmationCharging.java 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-integration/src/main/java/com/ruoyi/integration/mongodb/model/PlatformRemoteUpdate.java 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-integration/src/main/java/com/ruoyi/integration/mongodb/model/PlatformRemoteUpdateReply.java 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-integration/src/main/java/com/ruoyi/integration/mongodb/model/PlatformRestart.java 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-integration/src/main/java/com/ruoyi/integration/mongodb/model/PlatformRestartReply.java 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-integration/src/main/java/com/ruoyi/integration/mongodb/model/PlatformStartCharging.java 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-integration/src/main/java/com/ruoyi/integration/mongodb/model/PlatformStartChargingReply.java 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-integration/src/main/java/com/ruoyi/integration/mongodb/model/PlatformStopCharging.java 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-integration/src/main/java/com/ruoyi/integration/mongodb/model/PlatformStopChargingReply.java 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-integration/src/main/java/com/ruoyi/integration/mongodb/model/Pong.java 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-integration/src/main/java/com/ruoyi/integration/mongodb/model/QueryOfflineCard.java 44 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-integration/src/main/java/com/ruoyi/integration/mongodb/model/QueryOfflineCardReply.java 67 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-integration/src/main/java/com/ruoyi/integration/mongodb/model/ReadRealTimeMonitoringData.java 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-integration/src/main/java/com/ruoyi/integration/mongodb/model/SetupBillingModel.java 80 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-integration/src/main/java/com/ruoyi/integration/mongodb/model/SetupBillingModelReply.java 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-integration/src/main/java/com/ruoyi/integration/mongodb/model/SynchronizeOfflineCard.java 36 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-integration/src/main/java/com/ruoyi/integration/mongodb/model/SynchronizeOfflineCardReply.java 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-integration/src/main/java/com/ruoyi/integration/mongodb/model/TimingSetting.java 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-integration/src/main/java/com/ruoyi/integration/mongodb/model/TimingSettingReply.java 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-integration/src/main/java/com/ruoyi/integration/mongodb/model/TransactionRecord.java 53 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-integration/src/main/java/com/ruoyi/integration/mongodb/model/UpdateBalance.java 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-integration/src/main/java/com/ruoyi/integration/mongodb/model/UpdateBalanceReply.java 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-integration/src/main/java/com/ruoyi/integration/mongodb/model/UploadRealTimeMonitoringData.java 40 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-integration/src/main/java/com/ruoyi/integration/mongodb/model/WorkingParameterSetting.java 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-integration/src/main/java/com/ruoyi/integration/mongodb/model/WorkingParameterSettingReply.java 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-integration/src/main/java/com/ruoyi/integration/mongodb/service/OnlineReplyService.java 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-integration/src/main/java/com/ruoyi/integration/mongodb/service/OnlineService.java 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-integration/src/main/java/com/ruoyi/integration/mongodb/service/impl/OnlineReplyServiceImpl.java 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-integration/src/main/java/com/ruoyi/integration/mongodb/service/impl/OnlineServiceImpl.java 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-integration/src/main/java/com/ruoyi/integration/rocket/base/BaseMessage.java 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-integration/src/main/java/com/ruoyi/integration/rocket/configuration/EnvironmentIsolationConfig.java 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-integration/src/main/java/com/ruoyi/integration/rocket/configuration/RocketEnhanceProperties.java 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-integration/src/main/java/com/ruoyi/integration/rocket/configuration/RocketMQEnhanceAutoConfiguration.java 61 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-integration/src/main/java/com/ruoyi/integration/rocket/configuration/RocketMQMessageConverter.java 46 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-integration/src/main/java/com/ruoyi/integration/rocket/constant/EnhanceMessageConstant.java 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-integration/src/main/java/com/ruoyi/integration/rocket/listener/EnhanceMemberMessageListener.java 66 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-integration/src/main/java/com/ruoyi/integration/rocket/produce/EnhanceProduceController.java 45 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-integration/src/main/java/com/ruoyi/integration/rocket/test/MemberMessage.java 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-integration/src/main/java/com/ruoyi/integration/rocket/util/EnhanceMessageHandler.java 151 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-integration/src/main/java/com/ruoyi/integration/rocket/util/RocketMQEnhanceTemplate.java 82 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-integration/src/main/resources/bootstrap.yml 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-order/pom.xml 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/controller/TChargingOrderAccountingStrategyController.java 20 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/controller/TChargingOrderController.java 62 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/controller/TExchangeOrderController.java 52 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/controller/TOrderAppealController.java 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/controller/TOrderEvaluateController.java 30 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/dto/ExchangeOrderGoodsInfo.java 54 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/dto/GetMyChargingOrderList.java 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/dto/GetMyExchangeOrder.java 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/dto/MyChargingOrderInfo.java 59 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/dto/MyChargingOrderList.java 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/dto/MyExchangeOrderList.java 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/dto/OrderEvaluateVo.java 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/mapper/TChargingOrderMapper.java 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/service/TChargingOrderAccountingStrategyService.java 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/service/TChargingOrderService.java 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/service/TExchangeOrderService.java 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/service/TOrderEvaluateService.java 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/service/impl/TChargingOrderAccountingStrategyServiceImpl.java 37 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/service/impl/TChargingOrderServiceImpl.java 120 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/service/impl/TExchangeOrderServiceImpl.java 120 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/service/impl/TOrderEvaluateServiceImpl.java 107 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-order/src/main/resources/bootstrap.yml 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-order/src/main/resources/mapper/order/TChargingOrderMapper.xml 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-other/pom.xml 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/config/DataSourceProxyConfig.java 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/controller/OssController.java 80 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/controller/TCouponController.java 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/controller/TEvaluationTagController.java 22 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/controller/TGoodsController.java 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/controller/TIntegralRuleController.java 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/controller/TInvoiceTypeController.java 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/controller/TRoleSiteController.java 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/controller/TUserSiteController.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/controller/TVipController.java 10 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-other/src/main/resources/bootstrap.yml 4 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-other/src/main/resources/mapper/other/TCouponMapper.xml 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-other/src/main/resources/mapper/other/TVipMapper.xml 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-payment/pom.xml 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-payment/src/main/java/com/ruoyi/payment/wx/config/WxConfig.java 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-payment/src/main/java/com/ruoyi/payment/wx/controller/WxPayController.java 142 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-payment/src/main/java/com/ruoyi/payment/wx/enums/RefundEnum.java 53 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-payment/src/main/java/com/ruoyi/payment/wx/enums/TradeStateEnum.java 41 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-payment/src/main/java/com/ruoyi/payment/wx/exception/WxException.java 55 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-payment/src/main/java/com/ruoyi/payment/wx/model/V3.java 71 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-payment/src/main/java/com/ruoyi/payment/wx/model/WeixinProperties.java 101 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-payment/src/main/java/com/ruoyi/payment/wx/model/WxPaymentInfoModel.java 201 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-payment/src/main/java/com/ruoyi/payment/wx/model/WxPaymentRefundModel.java 84 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-payment/src/main/java/com/ruoyi/payment/wx/pojo/AppletUserDecodeData.java 52 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-payment/src/main/java/com/ruoyi/payment/wx/pojo/Watermark.java 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-payment/src/main/java/com/ruoyi/payment/wx/resp/NotifyV3PayDecodeRespBody.java 222 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-payment/src/main/java/com/ruoyi/payment/wx/utils/SHA1.java 39 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-payment/src/main/java/com/ruoyi/payment/wx/utils/WxAbstractPay.java 333 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-payment/src/main/java/com/ruoyi/payment/wx/utils/WxJsonUtils.java 73 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-payment/src/main/java/com/ruoyi/payment/wx/utils/WxTimeUtils.java 164 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-payment/src/main/java/com/ruoyi/payment/wx/utils/WxUtils.java 217 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-payment/src/main/java/com/ruoyi/payment/wx/utils/WxV3Pay.java 197 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-payment/src/main/resources/bootstrap.yml 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
bin/clean.bat
New file
@@ -0,0 +1,12 @@
@echo off
echo.
echo [ÐÅÏ¢] ÇåÀí¹¤³ÌtargetÉú³É·¾¶¡£
echo.
%~d0
cd %~dp0
cd ..
call mvn clean
pause
bin/package.bat
New file
@@ -0,0 +1,12 @@
@echo off
echo.
echo [ÐÅÏ¢] ´ò°üWeb¹¤³Ì£¬Éú³Éwar/jar°üÎļþ¡£
echo.
%~d0
cd %~dp0
cd ..
call mvn clean package -Dmaven.test.skip=true
pause
bin/run-auth.bat
New file
@@ -0,0 +1,14 @@
@echo off
echo.
echo [ÐÅÏ¢] Ê¹ÓÃJarÃüÁîÔËÐÐAuth¹¤³Ì¡£
echo.
cd %~dp0
cd ../ruoyi-auth/target
set JAVA_OPTS=-Xms512m -Xmx1024m -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=512m
java -Dfile.encoding=utf-8 %JAVA_OPTS% -jar ruoyi-auth.jar
cd bin
pause
bin/run-gateway.bat
New file
@@ -0,0 +1,14 @@
@echo off
echo.
echo [ÐÅÏ¢] Ê¹ÓÃJarÃüÁîÔËÐÐGateway¹¤³Ì¡£
echo.
cd %~dp0
cd ../ruoyi-gateway/target
set JAVA_OPTS=-Xms512m -Xmx1024m -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=512m
java -Dfile.encoding=utf-8 %JAVA_OPTS% -jar ruoyi-gateway.jar
cd bin
pause
bin/run-modules-file.bat
New file
@@ -0,0 +1,14 @@
@echo off
echo.
echo [ÐÅÏ¢] Ê¹ÓÃJarÃüÁîÔËÐÐModules-File¹¤³Ì¡£
echo.
cd %~dp0
cd ../ruoyi-modules/ruoyi-file/target
set JAVA_OPTS=-Xms512m -Xmx1024m -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=512m
java -Dfile.encoding=utf-8 %JAVA_OPTS% -jar ruoyi-modules-file.jar
cd bin
pause
bin/run-modules-gen.bat
New file
@@ -0,0 +1,14 @@
@echo off
echo.
echo [ÐÅÏ¢] Ê¹ÓÃJarÃüÁîÔËÐÐModules-Gen¹¤³Ì¡£
echo.
cd %~dp0
cd ../ruoyi-modules/ruoyi-gen/target
set JAVA_OPTS=-Xms512m -Xmx1024m -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=512m
java -Dfile.encoding=utf-8 %JAVA_OPTS% -jar ruoyi-modules-gen.jar
cd bin
pause
bin/run-modules-job.bat
New file
@@ -0,0 +1,14 @@
@echo off
echo.
echo [ÐÅÏ¢] Ê¹ÓÃJarÃüÁîÔËÐÐModules-Job¹¤³Ì¡£
echo.
cd %~dp0
cd ../ruoyi-modules/ruoyi-job/target
set JAVA_OPTS=-Xms512m -Xmx1024m -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=512m
java -Dfile.encoding=utf-8 %JAVA_OPTS% -jar ruoyi-modules-job.jar
cd bin
pause
bin/run-modules-system.bat
New file
@@ -0,0 +1,14 @@
@echo off
echo.
echo [ÐÅÏ¢] Ê¹ÓÃJarÃüÁîÔËÐÐModules-System¹¤³Ì¡£
echo.
cd %~dp0
cd ../ruoyi-modules/ruoyi-system/target
set JAVA_OPTS=-Xms512m -Xmx1024m -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=512m
java -Dfile.encoding=utf-8 %JAVA_OPTS% -jar ruoyi-modules-system.jar
cd bin
pause
bin/run-monitor.bat
New file
@@ -0,0 +1,14 @@
@echo off
echo.
echo [ÐÅÏ¢] Ê¹ÓÃJarÃüÁîÔËÐÐMonitor¹¤³Ì¡£
echo.
cd %~dp0
cd ../ruoyi-visual/ruoyi-monitor/target
set JAVA_OPTS=-Xms512m -Xmx1024m -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=512m
java -Dfile.encoding=utf-8 %JAVA_OPTS% -jar ruoyi-visual-monitor.jar
cd bin
pause
ruoyi-api/ruoyi-api-account/src/main/java/com/ruoyi/account/api/dto/CouponListDto.java
@@ -16,5 +16,7 @@
    private Long userId;
    @ApiModelProperty("预付价格")
    private BigDecimal payMoney;
    @ApiModelProperty("桩编号")
    private Integer number;
}
ruoyi-api/ruoyi-api-account/src/main/java/com/ruoyi/account/api/factory/AppUserAddressFallbackFactory.java
New file
@@ -0,0 +1,33 @@
package com.ruoyi.account.api.factory;
import com.ruoyi.account.api.feignClient.AppUserAddressClient;
import com.ruoyi.account.api.model.TAppUserAddress;
import com.ruoyi.account.api.model.TAppUserIntegralChange;
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 zhibing.pu
 * @Date 2024/8/16 11:02
 */
@Component
public class AppUserAddressFallbackFactory implements FallbackFactory<AppUserAddressClient> {
    private static final Logger log = LoggerFactory.getLogger(AppUserAddressFallbackFactory.class);
    @Override
    public AppUserAddressClient create(Throwable throwable) {
        log.error("用户地址调用失败:{}", throwable.getMessage());
        return new AppUserAddressClient(){
            @Override
            public R<TAppUserAddress> getAppUserAddressById(Long id) {
                return R.fail("根据id获取用户地址信息失败:" + throwable.getMessage());
            }
        };
    }
}
ruoyi-api/ruoyi-api-account/src/main/java/com/ruoyi/account/api/factory/AppUserCarFallbackFactory.java
@@ -28,7 +28,7 @@
        return new AppUserCarClient() {
            @Override
            public R<List<TAppUserCar>> getCarByIds(List<Integer> carIds) {
            public R<List<TAppUserCar>> getCarByIds(List<Long> carIds) {
                return R.fail("根据用户车辆id查询车辆信息失败:"+throwable.getMessage());
            }
        };
ruoyi-api/ruoyi-api-account/src/main/java/com/ruoyi/account/api/factory/AppUserFallbackFactory.java
@@ -36,6 +36,11 @@
            }
            @Override
            public R updateAppUser(TAppUser appUser) {
                return R.fail("修改用户失败:"+throwable.getMessage());
            }
            @Override
            public R<List<TAppUser>> getUserByIds(List<Long> appUserIds) {
                return R.fail("根据用户id查询用户信息失败:"+throwable.getMessage());
            }
ruoyi-api/ruoyi-api-account/src/main/java/com/ruoyi/account/api/factory/AppUserIntegralChangeFallbackFactory.java
New file
@@ -0,0 +1,32 @@
package com.ruoyi.account.api.factory;
import com.ruoyi.account.api.feignClient.AppUserIntegralChangeClient;
import com.ruoyi.account.api.model.TAppUserIntegralChange;
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 zhibing.pu
 * @Date 2024/8/16 11:02
 */
@Component
public class AppUserIntegralChangeFallbackFactory implements FallbackFactory<AppUserIntegralChangeClient> {
    private static final Logger log = LoggerFactory.getLogger(AppUserIntegralChangeFallbackFactory.class);
    @Override
    public AppUserIntegralChangeClient create(Throwable throwable) {
        log.error("账户积分变动调用失败:{}", throwable.getMessage());
        return new AppUserIntegralChangeClient(){
            @Override
            public R addAppUserIntegralChange(TAppUserIntegralChange appUserIntegralChange) {
                return R.fail("添加积分变动记录失败:" + throwable.getMessage());
            }
        };
    }
}
ruoyi-api/ruoyi-api-account/src/main/java/com/ruoyi/account/api/feignClient/AppUserAddressClient.java
New file
@@ -0,0 +1,26 @@
package com.ruoyi.account.api.feignClient;
import com.ruoyi.account.api.factory.AppUserAddressFallbackFactory;
import com.ruoyi.account.api.model.TAppUserAddress;
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.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
/**
 * @author zhibing.pu
 * @Date 2024/8/16 14:47
 */
@FeignClient(contextId = "AppUserAddressClient", value = ServiceNameConstants.ACCOUNT_SERVICE, fallbackFactory = AppUserAddressFallbackFactory.class)
public interface AppUserAddressClient {
    /**
     * 根据id获取用户地址信息
     * @param id
     * @return
     */
    @PostMapping("/t-app-user-address/getAppUserAddressById")
    R<TAppUserAddress> getAppUserAddressById(Long id);
}
ruoyi-api/ruoyi-api-account/src/main/java/com/ruoyi/account/api/feignClient/AppUserCarClient.java
@@ -27,5 +27,5 @@
     * @return
     */
    @PostMapping(value = "/t-app-user-car/getCarByIds")
    public R<List<TAppUserCar>> getCarByIds(@RequestBody List<Integer> carIds);
    public R<List<TAppUserCar>> getCarByIds(@RequestBody List<Long> carIds);
}
ruoyi-api/ruoyi-api-account/src/main/java/com/ruoyi/account/api/feignClient/AppUserClient.java
@@ -37,6 +37,11 @@
     * @param id
     * @return
     */
    @GetMapping(value = "/user/getUserById")
    @PostMapping(value = "/t-app-user/user/getUserById")
    R<TAppUser> getUserById(Long id);
    @PostMapping("/t-app-user/")
    R updateAppUser(TAppUser appUser);
}
ruoyi-api/ruoyi-api-account/src/main/java/com/ruoyi/account/api/feignClient/AppUserIntegralChangeClient.java
New file
@@ -0,0 +1,24 @@
package com.ruoyi.account.api.feignClient;
import com.ruoyi.account.api.factory.AppUserIntegralChangeFallbackFactory;
import com.ruoyi.account.api.model.TAppUserIntegralChange;
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/16 11:01
 */
@FeignClient(contextId = "AppUserIntegralChangeClient", value = ServiceNameConstants.ACCOUNT_SERVICE, fallbackFactory = AppUserIntegralChangeFallbackFactory.class)
public interface AppUserIntegralChangeClient {
    /**
     * 添加积分变动记录
     * @param appUserIntegralChange
     * @return
     */
    @PostMapping("/t-app-user-integral-change/addAppUserIntegralChange")
    R addAppUserIntegralChange(TAppUserIntegralChange appUserIntegralChange);
}
ruoyi-api/ruoyi-api-account/src/main/java/com/ruoyi/account/api/vo/CarListVO.java
New file
@@ -0,0 +1,21 @@
package com.ruoyi.account.api.vo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
@Data
@ApiModel(value = "CarListVO对象",description = "小程序-扫一扫选择车辆")
public class CarListVO {
    @ApiModelProperty(value = "图片")
    private String reviewRate;
    @ApiModelProperty(value = "车型-车牌号")
    private String evaluateCount;
    @ApiModelProperty(value = "车辆id")
    private Long driverId;
}
ruoyi-api/ruoyi-api-account/src/main/java/com/ruoyi/account/api/vo/CouponListVOVO.java
@@ -34,9 +34,7 @@
    private Integer isUse;
    @ApiModelProperty(value = "优惠券id")
    private Long couponId;
    @ApiModelProperty(value = "优惠券id")
    private BigDecimal payMoney;
    @ApiModelProperty(value = "最高折扣金额 用于判断哪一张优惠券优惠力度最大 前端忽略")
    @ApiModelProperty(value = "使用了优惠券后的服务费 用于判断哪一张优惠券优惠力度最大 前端忽略")
    private BigDecimal money;
    @ApiModelProperty(value = "当前用户是否为会员0否1是 前端忽略")
    private Integer isVip;
ruoyi-api/ruoyi-api-account/src/main/java/com/ruoyi/account/api/vo/ExchangeRecordVO.java
@@ -28,6 +28,10 @@
    private LocalDateTime endTime;
    @ApiModelProperty(value = "关联订单")
    private String orderId;
    @ApiModelProperty(value = "封面图")
    private String coverPicture;
    @ApiModelProperty(value = "详情图")
    private String detailsPicture;
    @ApiModelProperty(value = " 前端忽略 获取方式 1=积分兑换 2现金购买")
    private Integer waysToObtain;
    @ApiModelProperty(value = "优惠券id")
ruoyi-api/ruoyi-api-account/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
@@ -1,3 +1,5 @@
com.ruoyi.account.api.factory.AppUserFallbackFactory
com.ruoyi.account.api.factory.AppCouponFallbackFactory
com.ruoyi.account.api.factory.AppUserCarFallbackFactory
com.ruoyi.account.api.factory.AppUserIntegralChangeFallbackFactory
com.ruoyi.account.api.factory.AppUserAddressFallbackFactory
ruoyi-api/ruoyi-api-chargingPile/src/main/java/com/ruoyi/chargingPile/api/factory/ChargingGunFallbackFactory.java
New file
@@ -0,0 +1,41 @@
package com.ruoyi.chargingPile.api.factory;
import com.ruoyi.chargingPile.api.feignClient.ChargingGunClient;
import com.ruoyi.chargingPile.api.feignClient.ChargingPileClient;
import com.ruoyi.chargingPile.api.model.TChargingGun;
import com.ruoyi.chargingPile.api.model.TChargingPile;
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;
import java.util.List;
/**
 * 充电桩服务降级处理
 *
 * @author ruoyi
 */
@Component
public class ChargingGunFallbackFactory implements FallbackFactory<ChargingGunClient>
{
    private static final Logger log = LoggerFactory.getLogger(ChargingGunFallbackFactory.class);
    @Override
    public ChargingGunClient create(Throwable throwable) {
        log.error("充电枪调用失败:{}", throwable.getMessage());
        return new ChargingGunClient() {
            @Override
            public R<String> getAllName(Integer id) {
                return R.fail("根据id获取充电枪完整名称失败:" + throwable.getMessage());
            }
            @Override
            public R<TChargingGun> getChargingGunById(Integer id) {
                return R.fail("根据id获取充电枪失败:" + throwable.getMessage());
            }
        };
    }
}
ruoyi-api/ruoyi-api-chargingPile/src/main/java/com/ruoyi/chargingPile/api/factory/ChargingPileFallbackFactory.java
@@ -2,12 +2,14 @@
import com.ruoyi.chargingPile.api.feignClient.ChargingPileClient;
import com.ruoyi.chargingPile.api.model.TChargingPile;
import com.ruoyi.chargingPile.api.vo.SiteInfoVO;
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;
import java.math.BigDecimal;
import java.util.List;
/**
@@ -29,6 +31,16 @@
            public R<List<TChargingPile>> getChargingPileBySiteId(Integer siteId) {
                return R.fail("通过站点id获取充电桩列表失败:"+throwable.getMessage());
            }
            @Override
            public R<BigDecimal> getServiceMoney(String param) {
                return R.fail("根据会员折扣、预付金额 计算服务费失败:"+throwable.getMessage());
            }
            @Override
            public R<SiteInfoVO> getSiteInfoByNumber(String number) {
                return R.fail("通过桩编号获取电站信息失败:"+throwable.getMessage());
            }
        };
    }
}
ruoyi-api/ruoyi-api-chargingPile/src/main/java/com/ruoyi/chargingPile/api/feignClient/ChargingGunClient.java
New file
@@ -0,0 +1,34 @@
package com.ruoyi.chargingPile.api.feignClient;
import com.ruoyi.chargingPile.api.factory.ChargingGunFallbackFactory;
import com.ruoyi.chargingPile.api.model.TChargingGun;
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.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
/**
 * @author zhibing.pu
 * @Date 2024/8/15 19:36
 */
@FeignClient(contextId = "ChargingGunClient", value = ServiceNameConstants.CHARGINGPILE_SERVICE, fallbackFactory = ChargingGunFallbackFactory.class)
public interface ChargingGunClient {
    /**
     * 根据id获取充电枪完整名称
     * @param id
     * @return
     */
    @PostMapping("/t-charging-gun/getAllName")
    R<String> getAllName(Integer id);
    /**
     * 根据id获取充电枪
     * @param id
     * @return
     */
    @PostMapping("/t-charging-gun/getChargingGunById")
    R<TChargingGun> getChargingGunById(Integer id);
}
ruoyi-api/ruoyi-api-chargingPile/src/main/java/com/ruoyi/chargingPile/api/feignClient/ChargingPileClient.java
@@ -2,6 +2,7 @@
import com.ruoyi.chargingPile.api.factory.ChargingPileFallbackFactory;
import com.ruoyi.chargingPile.api.model.TChargingPile;
import com.ruoyi.chargingPile.api.vo.SiteInfoVO;
import com.ruoyi.common.core.constant.ServiceNameConstants;
import com.ruoyi.common.core.domain.R;
import io.swagger.annotations.ApiOperation;
@@ -9,6 +10,7 @@
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import java.math.BigDecimal;
import java.util.List;
/**
@@ -23,7 +25,16 @@
     */
    @ApiOperation(value = "管理后台-通过站点id获取充电桩列表 不分页")
    @PostMapping(value = "/t-charging-pile/getChargingPileBySiteId")
    public R<List<TChargingPile>> getChargingPileBySiteId(@RequestParam("siteId") Integer siteId);
    R<List<TChargingPile>> getChargingPileBySiteId(@RequestParam("siteId") Integer siteId);
    /**
     * 小程序远程调用 根据会员折扣、预付金额 计算服务费
     * @return
     */
    @PostMapping(value = "/t-accounting-strategy/getServiceMoney")
    R<BigDecimal> getServiceMoney(@RequestParam("param") String param);
    @PostMapping("/site/getSiteInfoByNumber")
    @ApiOperation(value = "扫一扫后通过桩编号获取电站信息", tags = {"小程序-扫一扫"})
    R<SiteInfoVO> getSiteInfoByNumber(@RequestParam("number") String number);
}
ruoyi-api/ruoyi-api-chargingPile/src/main/java/com/ruoyi/chargingPile/api/feignClient/ParkingLotClient.java
@@ -23,7 +23,7 @@
    /**
     * 通过站点id查询停车场信息
     */
    @GetMapping(value = "/t-parking-lot/getLotBySiteId")
    @PostMapping(value = "/t-parking-lot/getLotBySiteId")
    public R<TParkingLot> getLotBySiteId(@RequestParam("siteId") Integer siteId);
}
ruoyi-api/ruoyi-api-chargingPile/src/main/java/com/ruoyi/chargingPile/api/vo/SiteInfoVO.java
New file
@@ -0,0 +1,32 @@
package com.ruoyi.chargingPile.api.vo;
import com.ruoyi.chargingPile.api.model.Site;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.math.BigDecimal;
@Data
@ApiModel(value = "SiteVO对象", description = "小程序扫一扫-详情页面")
public class SiteInfoVO  {
    @ApiModelProperty(value = "电站名称")
    private String name;
    @ApiModelProperty(value = "桩编号")
    private String number;
    @ApiModelProperty(value = "普通电价")
    private BigDecimal electrovalence;
    @ApiModelProperty(value = "会员电价")
    private BigDecimal vipElectrovalence;
    @ApiModelProperty(value = "超时占位费说明")
    private String spaceChargeExplain;
    @ApiModelProperty(value = "上次选择车辆logo")
    private String carLogo;
    @ApiModelProperty(value = "上次选择车辆车牌号")
    private String licensePlate;
    @ApiModelProperty(value = "上次选择车辆品牌")
    private String vehicleBrand;
    @ApiModelProperty(value = "上次选择车辆id")
    private Long id;
}
ruoyi-api/ruoyi-api-chargingPile/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
@@ -1,3 +1,4 @@
com.ruoyi.chargingPile.api.factory.ChargingPileFallbackFactory
com.ruoyi.chargingPile.api.factory.SiteFallbackFactory
com.ruoyi.chargingPile.api.factory.ParkingLotFallbackFactory
com.ruoyi.chargingPile.api.factory.ChargingGunFallbackFactory
ruoyi-api/ruoyi-api-order/src/main/java/com/ruoyi/order/api/factory/ChargingOrderAccountingStrategyFallbackFactory.java
@@ -11,6 +11,7 @@
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
 * 充电订单服务降级处理
@@ -29,9 +30,14 @@
    
    
            @Override
            public R<List<List<Map<String, Object>>>> getTotalElectricQuantity(Integer days) {
            public R<List<List<Map<String, Object>>>> getTotalElectricQuantity(Integer days, Set<Integer> siteIds) {
                return R.fail("获取给定天数每天的充电量统计数据失败:" + throwable.getMessage());
            }
            @Override
            public R<List<Double>> getDailyChargingDegree(Integer days, Set<Integer> siteIds) {
                return R.fail("获取给定天数每天的充电度数失败:" + throwable.getMessage());
            }
        };
    }
}
ruoyi-api/ruoyi-api-order/src/main/java/com/ruoyi/order/api/factory/ChargingOrderFallbackFactory.java
@@ -39,6 +39,16 @@
            public R<Integer> getChargingCount(TChargingCountQuery req) {
                return R.fail("根据会员id和有效期查询有效期内享受充电折扣次数:" + throwable.getMessage());
            }
            @Override
            public R<TChargingOrder> getOrderDetailByGunId(Integer chargingGunId) {
                return R.fail("根据充电枪获取正在充电的订单:" + throwable.getMessage());
            }
            @Override
            public R<Long> getCar() {
                return R.fail("获取用户最近使用车辆充电的车辆id:" + throwable.getMessage());
            }
        };
    }
}
ruoyi-api/ruoyi-api-order/src/main/java/com/ruoyi/order/api/feignClient/ChargingOrderAccountingStrategyClient.java
@@ -6,9 +6,12 @@
import com.ruoyi.order.api.factory.ChargingOrderFallbackFactory;
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.RequestParam;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
 * @author zhibing.pu
@@ -23,6 +26,15 @@
     * @param days 天数
     * @return
     */
    @GetMapping("/t-charging-order-accounting-strategy/getTotalElectricQuantity")
    R<List<List<Map<String, Object>>>> getTotalElectricQuantity(Integer days);
    @PostMapping("/t-charging-order-accounting-strategy/getTotalElectricQuantity")
    R<List<List<Map<String, Object>>>> getTotalElectricQuantity(Integer days, @RequestParam("siteIds") Set<Integer> siteIds);
    /**
     * 获取给定天数每天的充电度数
     * @param days
     * @return
     */
    @PostMapping("/t-charging-order-getDailyChargingDegree-strategy/getUtilizationTrend")
    R<List<Double>> getDailyChargingDegree(Integer days, @RequestParam("siteIds") Set<Integer> siteIds);
}
ruoyi-api/ruoyi-api-order/src/main/java/com/ruoyi/order/api/feignClient/ChargingOrderClient.java
@@ -6,6 +6,7 @@
import com.ruoyi.order.api.model.TChargingOrder;
import com.ruoyi.order.api.query.TChargingCountQuery;
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;
@@ -29,4 +30,20 @@
     */
    @PostMapping(value = "/t-charging-order/getChargingCount")
    public R<Integer> getChargingCount(@RequestBody TChargingCountQuery req);
    /**
     * 根据充电枪获取正在充电的订单
     * @param chargingGunId 充电枪id
     * @return
     */
    @PostMapping(value = "/t-charging-order/getOrderDetailByGunId")
    R<TChargingOrder> getOrderDetailByGunId(@RequestParam("chargingGunId") Integer chargingGunId);
    /**
     * 获取用户最近使用车辆充电的车辆id
     * @return
     */
    @GetMapping(value = "/t-charging-order/getCar")
    public R<Long> getCar();
}
ruoyi-api/ruoyi-api-order/src/main/java/com/ruoyi/order/api/feignClient/OrderClient.java
@@ -7,6 +7,7 @@
import com.ruoyi.order.api.query.TActivityStatisticsQuery;
import com.ruoyi.order.api.vo.TActivityVO;
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;
@@ -36,4 +37,5 @@
     */
    @PostMapping(value = "/t-exchange-order/activityStatistics")
    public R<TActivityVO> activityStatistics(@RequestBody TActivityStatisticsQuery dto);
}
ruoyi-api/ruoyi-api-order/src/main/java/com/ruoyi/order/api/model/TChargingOrder.java
@@ -50,7 +50,7 @@
    @ApiModelProperty(value = "充电车辆 id")
    @TableField("app_user_car_id")
    private Integer appUserCarId;
    private Long appUserCarId;
    @ApiModelProperty(value = "站点 id")
    @TableField("site_id")
@@ -68,6 +68,14 @@
    @TableField("charging_gun_id")
    private Integer chargingGunId;
    @ApiModelProperty(value = "充电总度数")
    @TableField("charging_capacity")
    private BigDecimal chargingCapacity;
    @ApiModelProperty(value = "充电功率")
    @TableField("charging_power")
    private BigDecimal chargingPower;
    @ApiModelProperty(value = "充电开始时间")
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
    @TableField("start_time")
@@ -82,6 +90,10 @@
    @TableField("status")
    private Integer status;
    @ApiModelProperty(value = "结束方式(0=异常终止,1=主动终止,2=满电终止,3=费用不足终止)")
    @TableField("end_mode")
    private Integer endMode;
    @ApiModelProperty(value = "充值金额")
    @TableField("recharge_amount")
    private BigDecimal rechargeAmount;
ruoyi-api/ruoyi-api-order/src/main/java/com/ruoyi/order/api/model/TOrderAppeal.java
@@ -28,7 +28,7 @@
    private static final long serialVersionUID = 1L;
    @ApiModelProperty(value = "主键")
    @TableId(value = "id", type = IdType.AUTO)
    @TableId(value = "id", type = IdType.NONE)
    private Long id;
    @ApiModelProperty(value = "订单类型(1=充电订单,2=购物订单,3=兑换订单,4=会员订单)")
ruoyi-api/ruoyi-api-order/src/main/java/com/ruoyi/order/api/model/TOrderEvaluate.java
@@ -45,7 +45,7 @@
    @ApiModelProperty(value = "充电车辆 id")
    @TableField("app_user_car_id")
    private Integer appUserCarId;
    private Long appUserCarId;
    @ApiModelProperty(value = "评分")
    @TableField("mark")
ruoyi-api/ruoyi-api-order/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
@@ -1,3 +1,4 @@
com.ruoyi.order.api.factory.ChargingOrderFallbackFactory
com.ruoyi.order.api.factory.OrderFallbackFactory
com.ruoyi.order.api.factory.ExchangeOrderFallbackFactory
com.ruoyi.order.api.factory.ChargingOrderAccountingStrategyFallbackFactory
ruoyi-api/ruoyi-api-other/src/main/java/com/ruoyi/other/api/domain/TCoupon.java
@@ -39,6 +39,14 @@
    @TableField("name")
    private String name;
    @ApiModelProperty(value = "封面图")
    @TableField("cover_picture")
    private String coverPicture;
    @ApiModelProperty(value = "详情图片")
    @TableField("details_picture")
    private String detailsPicture;
    @ApiModelProperty(value = "优惠方式(1=满减,2=抵扣)")
    @TableField("preferential_mode")
    private Integer preferentialMode;
@@ -102,7 +110,6 @@
    @ApiModelProperty(value = "说明")
    @TableField("remark")
    private String remark;
    @ApiModelProperty(value = "状态(1=上架,2=下架)")
    @TableField("status")
    private Integer status;
ruoyi-api/ruoyi-api-other/src/main/java/com/ruoyi/other/api/domain/THtml.java
@@ -24,7 +24,7 @@
@EqualsAndHashCode(callSuper = false)
@TableName("t_html")
@ApiModel(value="THtml对象", description="")
public class THtml extends BasePojo {
public class THtml  {
    private static final long serialVersionUID = 1L;
ruoyi-api/ruoyi-api-other/src/main/java/com/ruoyi/other/api/domain/TVip.java
@@ -137,7 +137,7 @@
    @ApiModelProperty(value = "内部会员折扣")
    @TableField("discount")
    private BigDecimal discount;
    @ApiModelProperty(value = "会员类型 1常规 2内部")
    @ApiModelProperty(value = "添加的会员类型 1常规 2内部")
    @TableField("type")
    private Integer type;
ruoyi-api/ruoyi-api-other/src/main/java/com/ruoyi/other/api/factory/CouponFallbackFactory.java
New file
@@ -0,0 +1,34 @@
package com.ruoyi.other.api.factory;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.other.api.domain.TCoupon;
import com.ruoyi.other.api.domain.TGoods;
import com.ruoyi.other.api.feignClient.CouponClient;
import com.ruoyi.other.api.feignClient.GoodsClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.openfeign.FallbackFactory;
import org.springframework.stereotype.Component;
/**
 * @author zhibing.pu
 * @Date 2024/8/16 13:48
 */
@Component
public class CouponFallbackFactory implements FallbackFactory<CouponClient> {
    private static final Logger log = LoggerFactory.getLogger(CouponFallbackFactory.class);
    @Override
    public CouponClient create(Throwable throwable) {
        log.error("优惠券调用失败:{}", throwable.getMessage());
        return new CouponClient() {
            @Override
            public R<TCoupon> getCouponById(Integer id) {
                return R.fail("根据id获取优惠券信息失败:" + throwable.getMessage());
            }
        };
    }
}
ruoyi-api/ruoyi-api-other/src/main/java/com/ruoyi/other/api/factory/GoodsFallbackFactory.java
New file
@@ -0,0 +1,32 @@
package com.ruoyi.other.api.factory;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.other.api.domain.TGoods;
import com.ruoyi.other.api.feignClient.GoodsClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.openfeign.FallbackFactory;
import org.springframework.stereotype.Component;
/**
 * @author zhibing.pu
 * @Date 2024/8/16 13:48
 */
@Component
public class GoodsFallbackFactory implements FallbackFactory<GoodsClient> {
    private static final Logger log = LoggerFactory.getLogger(GoodsFallbackFactory.class);
    @Override
    public GoodsClient create(Throwable throwable) {
        log.error("商品调用失败:{}", throwable.getMessage());
        return new GoodsClient() {
            @Override
            public R<TGoods> getGoodsById(Integer id) {
                return R.fail("根据id获取商品信息失败:" + throwable.getMessage());
            }
        };
    }
}
ruoyi-api/ruoyi-api-other/src/main/java/com/ruoyi/other/api/factory/IntegralRuleFallbackFactory.java
New file
@@ -0,0 +1,43 @@
package com.ruoyi.other.api.factory;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.account.api.dto.TagListQueryDto;
import com.ruoyi.account.api.dto.UnitListQueryDto;
import com.ruoyi.account.api.vo.CouponListVOVO;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.other.api.domain.TCompany;
import com.ruoyi.other.api.domain.TIntegralRule;
import com.ruoyi.other.api.domain.TUserTag;
import com.ruoyi.other.api.feignClient.IntegralRuleClient;
import com.ruoyi.other.api.feignClient.OtherClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.openfeign.FallbackFactory;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.Map;
/**
 * 商品服务降级处理
 *
 * @author ruoyi
 */
@Component
public class IntegralRuleFallbackFactory implements FallbackFactory<IntegralRuleClient>
{
    private static final Logger log = LoggerFactory.getLogger(IntegralRuleFallbackFactory.class);
    @Override
    public IntegralRuleClient create(Throwable throwable) {
        log.error("积分规则调用失败:{}", throwable.getMessage());
        return new IntegralRuleClient() {
            @Override
            public R<TIntegralRule> getSet() {
                return R.fail("获取积分设置失败:" + throwable.getMessage());
            }
        };
    }
}
ruoyi-api/ruoyi-api-other/src/main/java/com/ruoyi/other/api/feignClient/CouponClient.java
New file
@@ -0,0 +1,26 @@
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.domain.TCoupon;
import com.ruoyi.other.api.factory.CouponFallbackFactory;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
/**
 * @author zhibing.pu
 * @Date 2024/8/16 14:00
 */
@FeignClient(contextId = "CouponClient", value = ServiceNameConstants.OTHER_SERVICE, fallbackFactory = CouponFallbackFactory.class)
public interface CouponClient {
    /**
     * 根据id获取优惠券信息
     * @param id
     * @return
     */
    @PostMapping("/t-coupon/getCouponById")
    R<TCoupon> getCouponById(Integer id);
}
ruoyi-api/ruoyi-api-other/src/main/java/com/ruoyi/other/api/feignClient/GoodsClient.java
New file
@@ -0,0 +1,25 @@
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.domain.TGoods;
import com.ruoyi.other.api.factory.GoodsFallbackFactory;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
/**
 * @author zhibing.pu
 * @Date 2024/8/16 13:47
 */
@FeignClient(contextId = "GoodsClient", value = ServiceNameConstants.OTHER_SERVICE, fallbackFactory = GoodsFallbackFactory.class)
public interface GoodsClient {
    /**
     * 根据id获取商品信息
     * @param id
     * @return
     */
    @PostMapping("/t-goods/getGoodsById")
    R<TGoods> getGoodsById(Integer id);
}
ruoyi-api/ruoyi-api-other/src/main/java/com/ruoyi/other/api/feignClient/IntegralRuleClient.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.domain.TIntegralRule;
import com.ruoyi.other.api.factory.IntegralRuleFallbackFactory;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
/**
 * @author zhibing.pu
 * @Date 2024/8/16 10:54
 */
@FeignClient(contextId = "IntegralRuleClient", value = ServiceNameConstants.OTHER_SERVICE, fallbackFactory = IntegralRuleFallbackFactory.class)
public interface IntegralRuleClient {
    /**
     * 获取积分设置
     * @return
     */
    @PostMapping("/integral/getSet")
    R<TIntegralRule> getSet();
}
ruoyi-api/ruoyi-api-other/src/main/java/com/ruoyi/other/api/feignClient/RoleSiteClient.java
@@ -6,10 +6,7 @@
import com.ruoyi.other.api.factory.RoleSiteFallbackFactory;
import com.ruoyi.other.api.factory.UserSiteFallbackFactory;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@@ -26,8 +23,8 @@
     * @param roleId
     * @return
     */
    @GetMapping("/t-role-site/getSiteIds")
    R<List<Integer>> getSiteIds(Long roleId);
    @PostMapping("/t-role-site/getSiteIds/{roleId}")
    R<List<Integer>> getSiteIds(@PathVariable("roleId") Long roleId);
    
    
    /**
ruoyi-api/ruoyi-api-other/src/main/java/com/ruoyi/other/api/feignClient/TEvaluationTagClient.java
@@ -33,6 +33,6 @@
     * @param tagIds
     * @return
     */
    @GetMapping("/t-evaluation-tag/getListByIds")
    @PostMapping("/t-evaluation-tag/getListByIds")
    R<List<TEvaluationTag>> getListByIds(List<Integer> tagIds);
}
ruoyi-api/ruoyi-api-other/src/main/java/com/ruoyi/other/api/feignClient/UserSiteClient.java
@@ -26,7 +26,7 @@
     * @param userId
     * @return
     */
    @GetMapping("/t-user-site/getSiteIds")
    @PostMapping("/t-user-site/getSiteIds")
    R<List<Integer>> getSiteIds(Long userId);
    
    
ruoyi-api/ruoyi-api-other/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
@@ -3,3 +3,6 @@
com.ruoyi.other.api.factory.RoleSiteFallbackFactory
com.ruoyi.other.api.factory.TEvaluationTagFallbackFactory
com.ruoyi.other.api.factory.VipFallbackFactory
com.ruoyi.other.api.factory.IntegralRuleFallbackFactory
com.ruoyi.other.api.factory.GoodsFallbackFactory
com.ruoyi.other.api.factory.CouponFallbackFactory
ruoyi-api/ruoyi-api-system/src/main/java/com/ruoyi/system/api/domain/SysRole.java
@@ -38,23 +38,11 @@
    @TableField("role_key")
    private String roleKey;
    /** 角色排序 */
    @Excel(name = "角色排序")
    @TableField("role_sort")
    private Integer roleSort;
    /** 数据范围(1:所有数据权限;2:自定义数据权限;3:本部门数据权限;4:本部门及以下数据权限;5:仅本人数据权限) */
    @Excel(name = "数据范围", readConverterExp = "1=所有数据权限,2=自定义数据权限,3=本部门数据权限,4=本部门及以下数据权限,5=仅本人数据权限")
    @TableField("data_scope")
    private String dataScope;
    /** 菜单树选择项是否关联显示( 0:父子不互相关联显示 1:父子互相关联显示) */
    @TableField("menu_check_strictly")
    private boolean menuCheckStrictly;
    /** 部门树选择项是否关联显示(0:父子不互相关联显示 1:父子互相关联显示 ) */
    @TableField("dept_check_strictly")
    private boolean deptCheckStrictly;
    /** 角色状态(0正常 1停用) */
    @Excel(name = "角色状态", readConverterExp = "0=正常,1=停用")
@@ -137,17 +125,6 @@
        this.roleKey = roleKey;
    }
    @NotNull(message = "显示顺序不能为空")
    public Integer getRoleSort()
    {
        return roleSort;
    }
    public void setRoleSort(Integer roleSort)
    {
        this.roleSort = roleSort;
    }
    public String getDataScope()
    {
        return dataScope;
@@ -156,26 +133,6 @@
    public void setDataScope(String dataScope)
    {
        this.dataScope = dataScope;
    }
    public boolean isMenuCheckStrictly()
    {
        return menuCheckStrictly;
    }
    public void setMenuCheckStrictly(boolean menuCheckStrictly)
    {
        this.menuCheckStrictly = menuCheckStrictly;
    }
    public boolean isDeptCheckStrictly()
    {
        return deptCheckStrictly;
    }
    public void setDeptCheckStrictly(boolean deptCheckStrictly)
    {
        this.deptCheckStrictly = deptCheckStrictly;
    }
    public String getStatus()
@@ -252,10 +209,7 @@
            .append("roleId", getRoleId())
            .append("roleName", getRoleName())
            .append("roleKey", getRoleKey())
            .append("roleSort", getRoleSort())
            .append("dataScope", getDataScope())
            .append("menuCheckStrictly", isMenuCheckStrictly())
            .append("deptCheckStrictly", isDeptCheckStrictly())
            .append("status", getStatus())
            .append("delFlag", getDelFlag())
            .append("createBy", getCreateBy())
ruoyi-api/ruoyi-api-system/src/main/java/com/ruoyi/system/api/domain/SysUser.java
@@ -4,7 +4,9 @@
import java.util.List;
import javax.validation.constraints.*;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.annotations.ApiModelProperty;
import org.apache.commons.lang3.builder.ToStringBuilder;
@@ -28,7 +30,7 @@
    /** 用户ID */
    @Excel(name = "用户序号", cellType = ColumnType.NUMERIC, prompt = "用户编号")
    @TableField("user_id")
    @TableId(value = "user_id", type = IdType.AUTO)
    private Long userId;
    /** 部门ID */
ruoyi-api/ruoyi-api-system/src/main/java/com/ruoyi/system/api/feignClient/SysRoleClient.java
@@ -7,6 +7,7 @@
import com.ruoyi.system.api.model.GetSysRoleByIds;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import java.util.List;
@@ -17,7 +18,7 @@
@FeignClient(contextId = "SysRoleClient", value = ServiceNameConstants.SYSTEM_SERVICE, fallbackFactory = SysRoleFallbackFactory.class)
public interface SysRoleClient {
    
    @GetMapping("/role/getSysRoleByIds")
    @PostMapping("/role/getSysRoleByIds")
    public R<List<SysRole>> getSysRoleByIds(GetSysRoleByIds ids);
}
ruoyi-api/ruoyi-api-system/src/main/java/com/ruoyi/system/api/feignClient/SysUserClient.java
@@ -51,7 +51,7 @@
     * @param userId
     * @return
     */
    @GetMapping("/user/queryRoleByUserId/{userId}")
    @PostMapping("/user/queryRoleByUserId/{userId}")
    R<SysRole>  queryRoleByUserId(@PathVariable("userId") Long userId);
@@ -60,7 +60,7 @@
     * @param roleId
     * @return
     */
    @GetMapping("/user/queryRoleByRoleId/{roleId}")
    @PostMapping("/user/queryRoleByRoleId/{roleId}")
    R<SysRole>  queryRoleByRoleId(@PathVariable("roleId") Long roleId);
    /**
ruoyi-api/ruoyi-api-system/src/main/java/com/ruoyi/system/api/model/LoginUserApplet.java
New file
@@ -0,0 +1,56 @@
package com.ruoyi.system.api.model;
import lombok.Data;
import java.io.Serializable;
/**
 * 用户信息 小程序登录使用
 *
 * @author 无关风月
 */
@Data
public class LoginUserApplet implements Serializable {
    private static final long serialVersionUID = 1L;
    /**
     * 用户唯一标识
     */
    private String token;
    /**
     * 用户姓名
     */
    private String name;
    /**
     * 用户名id
     */
    private Long userId;
    /**
     * 用户手机号
     */
    private String phone;
    /**
     * 登录时间
     */
    private Long loginTime;
    /**
     * 过期时间
     */
    private Long expireTime;
    /**
     * 登录IP地址
     */
    private String ipaddr;
    /**
     * 头像
     */
    private String avatar;
    /**
     * 地址
     */
    private String address;
}
ruoyi-api/ruoyi-api-system/src/main/java/com/ruoyi/system/api/model/TAppUserLoginInfo.java
New file
@@ -0,0 +1,79 @@
package com.ruoyi.system.api.model;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.ruoyi.common.core.web.domain.BasePojo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.time.LocalDateTime;
/**
 * <p>
 *
 * </p>
 *
 * @author luodangjia
 * @since 2024-08-06
 */
@Data
public class TAppUserLoginInfo extends BasePojo {
    private static final long serialVersionUID = 1L;
    private Long id;
    @ApiModelProperty(value = "用户")
    private String name;
    @ApiModelProperty(value = "手机号")
    private String phone;
    @ApiModelProperty(value = "头像")
    private String avatar;
    @ApiModelProperty(value = "会员id")
    private Integer vipId;
    @ApiModelProperty(value = "会员到期时间")
    private LocalDateTime vipEndTime;
    @ApiModelProperty(value = "单位id")
    private Integer companyId;
    @ApiModelProperty(value = "身份证号")
    private String idCard;
    @ApiModelProperty(value = "认证状态(0=否,1=是)")
    private Integer authStatus;
    @ApiModelProperty(value = "微信openid")
    private String wxOpenid;
    @ApiModelProperty(value = "支付宝openid")
    private String aliOpenid;
    @ApiModelProperty(value = "积分")
    private Integer points;
    @ApiModelProperty(value = "省名称")
    private String province;
    @ApiModelProperty(value = "省区划代码")
    private String provinceCode;
    @ApiModelProperty(value = "市名称")
    private String city;
    @ApiModelProperty(value = "市区划代码")
    private String cityCode;
    @ApiModelProperty(value = "状态(1=正常,2=冻结,3=注销)")
    private Integer status;
    @ApiModelProperty(value = "最后一次登录时间")
    private LocalDateTime lastLoginTime;
}
ruoyi-auth/src/main/java/com/ruoyi/auth/controller/TokenController.java
@@ -3,15 +3,16 @@
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.ruoyi.system.api.domain.SysRole;
import com.ruoyi.system.api.domain.SysUser;
import com.ruoyi.system.api.feignClient.SysUserClient;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.*;
import com.ruoyi.auth.form.LoginBody;
import com.ruoyi.auth.form.RegisterBody;
import com.ruoyi.auth.service.SysLoginService;
@@ -30,6 +31,7 @@
 * 
 * @author ruoyi
 */
@Slf4j
@RestController
public class TokenController
{
@@ -53,11 +55,11 @@
        HashMap<String, Object> map = new HashMap<>();
        map.put("token",tokenService.createToken(userInfo));
        List<SysRole> roles = userInfo.getSysUser().getRoles();
        if(CollectionUtils.isEmpty(roles)){
            return R.fail("请关联角色!");
        }
//        if(CollectionUtils.isEmpty(roles)){
//            return R.fail("请关联角色!");
//        }
        map.put("roleName",roles.get(0).getRoleName());
//        map.put("roleName",roles.get(0).getRoleName());
        map.put("info",userInfo);
        // 修改用户最后登录时间
        SysUser sysUser = new SysUser();
@@ -67,10 +69,6 @@
        userClient.updateSysUser(sysUser);
        return R.ok(map);
    }
    @DeleteMapping("logout")
    public R<?> logout(HttpServletRequest request) {
@@ -86,6 +84,17 @@
        return R.ok();
    }
    @DeleteMapping("logoutApplet")
    public R<?> logoutApplet(HttpServletRequest request) {
        String token = SecurityUtils.getToken(request);
        if (StringUtils.isNotEmpty(token))
        {
            // 删除用户缓存记录
            AuthUtil.logoutByToken(token);
        }
        return R.ok();
    }
    @PostMapping("refresh")
    public R<?> refresh(HttpServletRequest request)
    {
ruoyi-auth/src/main/resources/bootstrap.yml
@@ -43,7 +43,7 @@
  enabled: true
  application-id: ${spring.application.name}
  tx-service-group: seata_tx_group    #此处配置自定义的seata事务分组名称
  enable-auto-data-source-proxy: true    #开启数据库代理
  enable-auto-data-source-proxy: false    #关闭数据库代理
  service:
    vgroup-mapping:
      seata_tx_group: default
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/constant/CacheConstants.java
@@ -11,6 +11,7 @@
     * 缓存有效期,默认720(分钟)
     */
    public final static long EXPIRATION = 720;
    public final static long EXPIRATION_APPLET = 7*24*60*60;
    /**
     * 缓存刷新时间,默认120(分钟)
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/constant/SecurityConstants.java
@@ -36,6 +36,10 @@
     * 用户标识
     */
    public static final String USER_KEY = "user_key";
    /**
     * 小程序登录用户标识
     */
    public static final String USER_APPLET_KEY = "user_applet_key";
    /**
     * 登录用户
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/constant/TokenConstants.java
@@ -25,7 +25,7 @@
    /**
     * 参数签名
     */
    public static final String SING = "sing";
    public static final String SIGN = "sign";
    
    /**
     * 参数随机字符串
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/exception/user/UserAppletException.java
New file
@@ -0,0 +1,48 @@
package com.ruoyi.common.core.exception.user;
/**
 * 学生端登录异常信息
 *
 * @author HJL
 * @version 1.0
 * @since 2024-05-24 11:35
 */
public class UserAppletException extends RuntimeException {
    private static final long serialVersionUID = 1L;
    /**
     * 错误提示
     */
    private String message;
    /**
     * 错误码
     */
    private int code;
    public UserAppletException() {
    }
    public UserAppletException(String message, Integer code) {
        this.message = message;
        this.code = code;
    }
    @Override
    public String getMessage() {
        return message;
    }
    public void setMessage(String message) {
        this.message = message;
    }
    public int getCode() {
        return code;
    }
    public void setCode(int code) {
        this.code = code;
    }
}
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/JwtUtils.java
@@ -64,6 +64,17 @@
    }
    /**
     * 小程序根据令牌获取用户标识
     *
     * @param token 令牌
     * @return 用户ID
     */
    public static String getUserKeyApplet(String token)
    {
        Claims claims = parseToken(token);
        return getValue(claims, SecurityConstants.USER_APPLET_KEY);
    }
    /**
     * 根据令牌获取用户ID
     * 
     * @param token 令牌
@@ -75,6 +86,7 @@
        return getValue(claims, SecurityConstants.DETAILS_USER_ID);
    }
    /**
     * 根据身份信息获取用户ID
     * 
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/ObsUploadUtil.java
New file
@@ -0,0 +1,78 @@
package com.ruoyi.common.core.utils;
import com.obs.services.ObsClient;
import com.obs.services.model.ObjectMetadata;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
import java.io.InputStream;
import java.util.UUID;
public class ObsUploadUtil {
    public static String endPoint = "obs.cn-southwest-2.myhuaweicloud.com";
    public static String accessKeyId = "LP9N1TLAYN8ERS1PVIYK";
    public static String accessKeySecret = "bV55lFHi1cG0SYBvnab8yIgDX6etKRSLh5j1gkPR";
    public static String bucketName = "haitunyingyu";
    public static String oss_domain = "https://haitunyingyu.obs.cn-southwest-2.myhuaweicloud.com/";
    // 创建ObsClient实例
    public static ObsClient obsClient = new ObsClient(accessKeyId, accessKeySecret, endPoint);
    public static String obsUpload(MultipartFile file) throws IOException{
        //CommonsMultipartFile file = (CommonsMultipartFile)multipartFile;
        String fileName = "";
        if(file!=null && !"".equals(file.getOriginalFilename()) && file.getOriginalFilename()!=null){
            InputStream content = file.getInputStream();//获得指定文件的输入流
            ObjectMetadata meta = new ObjectMetadata();// 创建上传Object的Metadata
            meta.setContentLength(file.getSize());  // 必须设置ContentLength
            String originalFilename = file.getOriginalFilename();
            if (originalFilename.contains("apk")){
                fileName = "bf2fe5c5499341e5bc0d56c0c7d5fb2e.apk";
                System.err.println("apk");
            }else{
                fileName =  UUID.randomUUID().toString().replaceAll("-","") + originalFilename.subSequence(originalFilename.lastIndexOf("."), originalFilename.length());
            }
            obsClient.putObject(bucketName,"admin/"+fileName,content,meta);// 上传Object.
            if(fileName != null && !"".equals(fileName)){
                System.out.println(fileName);
                fileName = oss_domain+"admin/"+fileName;
            }
        }
        return fileName;
    }
    /**
     * 删除某个Object
     *
     * @param bucketUrl
     * @return
     */
    public static boolean deleteObject(String bucketUrl) {
        try {
            bucketUrl=bucketUrl.replace(oss_domain+"web","");
            // 删除Object.
            obsClient.deleteObject(bucketName, bucketUrl);
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        } finally {
            //ossClient.shutdown();
        }
        return true;
    }
//     public static void createBucket(String bucketName)
//     {
//         //初始化 OSSClient
////          ossClient = new OssClient(endPoint, accessKeyId, accessKeySecret);
//
//         // 新建一个Bucket
//         Bucket bucket = ossClient.createBucket(bucketName);
//         System.out.println(bucket.getName());
//         System.out.println(bucket.getCreationDate());
//     }
//
//     public static void main(String[] args) {
//         OssUploadUtil.createBucket("ssfdfsd");
//    }
}
ruoyi-common/ruoyi-common-security/src/main/java/com/ruoyi/common/security/handler/GlobalExceptionHandler.java
@@ -3,6 +3,8 @@
import javax.naming.SizeLimitExceededException;
import javax.servlet.http.HttpServletRequest;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.core.exception.user.UserAppletException;
import org.apache.commons.fileupload.FileUploadBase;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -151,4 +153,12 @@
        log.error("上传文件异常 => : {}", e.getMessage());
        return AjaxResult.error("文件识别大小超出限制,允许的大小在" + maxFileSize);
    }
    /**
     * 学生端单点登录-异常信息
     */
    @ExceptionHandler(UserAppletException.class)
    public AjaxResult<String> studyLoginExceptionHandler(UserAppletException e) {
        return AjaxResult.error(e.getCode(), e.getMessage());
    }
}
ruoyi-common/ruoyi-common-security/src/main/java/com/ruoyi/common/security/service/TokenService.java
@@ -4,6 +4,9 @@
import java.util.Map;
import java.util.concurrent.TimeUnit;
import javax.servlet.http.HttpServletRequest;
import com.ruoyi.common.core.exception.user.UserAppletException;
import com.ruoyi.system.api.model.LoginUserApplet;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import com.ruoyi.common.core.constant.CacheConstants;
@@ -33,6 +36,7 @@
    protected static final long MILLIS_MINUTE = 60 * MILLIS_SECOND;
    private final static long expireTime = CacheConstants.EXPIRATION;
    private final static long expireAppletTime = CacheConstants.EXPIRATION_APPLET;
    private final static String ACCESS_TOKEN = CacheConstants.LOGIN_TOKEN_KEY;
@@ -64,7 +68,57 @@
        rspMap.put("expires_in", expireTime);
        return rspMap;
    }
    /**
     * 创建小程序令牌
     */
    public Map<String, Object> createTokenApplet(LoginUserApplet loginUser) {
        String token = IdUtils.fastUUID();
        Long userId = loginUser.getUserId();
        String name = loginUser.getName();
        loginUser.setToken(token);
        loginUser.setIpaddr(IpUtils.getIpAddr());
        refreshToken1(loginUser);
        // Jwt存储信息
        Map<String, Object> claimsMap = new HashMap<String, Object>();
        claimsMap.put(SecurityConstants.USER_APPLET_KEY, token);
        claimsMap.put(SecurityConstants.DETAILS_USER_ID, userId);
        claimsMap.put(SecurityConstants.DETAILS_USERNAME, name);
        // 接口返回信息
        Map<String, Object> rspMap = new HashMap<String, Object>();
        rspMap.put("access_token", JwtUtils.createToken(claimsMap));
        rspMap.put("expires_in", expireAppletTime);
        return rspMap;
    }
    public LoginUserApplet getLoginUserApplet() {
        LoginUserApplet loginUserAppletToken = getLoginUserAppletToken(ServletUtils.getRequest());
        if (loginUserAppletToken == null){
            throw new UserAppletException("登录失效,请重新登录!", 401);
        }
        return loginUserAppletToken;
    }
    public LoginUserApplet getLoginUserAppletToken(HttpServletRequest request) {
        // 获取请求携带的令牌
        String token = SecurityUtils.getToken(request);
        return getLoginUserApplet(token);
    }
    /**
     * 小程序 获取用户身份信息
     *
     * @return 用户信息
     */
    public LoginUserApplet getLoginUserApplet(String token) {
        LoginUserApplet user = null;
        try {
            if (StringUtils.isNotEmpty(token)) {
                String userKey = JwtUtils.getUserKeyApplet(token);
                user = redisService.getCacheObject(getTokenKey(userKey));
                return user;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return user;
    }
    /**
     * 获取用户身份信息
     *
@@ -161,7 +215,13 @@
        String userKey = getTokenKey(loginUser.getToken());
        redisService.setCacheObject(userKey, loginUser, expireTime, TimeUnit.MINUTES);
    }
    public void refreshToken1(LoginUserApplet dto) {
        dto.setLoginTime(System.currentTimeMillis());
        dto.setExpireTime(dto.getLoginTime() + expireTime * MILLIS_MINUTE);
        // 根据uuid将loginUser缓存
        String userKey = getTokenKey(dto.getToken());
        redisService.setCacheObject(userKey, dto, expireTime, TimeUnit.MINUTES);
    }
    private String getTokenKey(String token)
    {
        return ACCESS_TOKEN + token;
ruoyi-gateway/src/main/java/com/ruoyi/gateway/config/RouterFunctionConfiguration.java
@@ -31,7 +31,7 @@
    /**
     * 这里为支持的请求头,如果有自定义的header字段请自己添加
     */
    private static final String ALLOWED_HEADERS = "X-Requested-With, Content-Type, Authorization, credential, X-XSRF-TOKEN, token, username, client, request-origion";
    private static final String ALLOWED_HEADERS = "X-Requested-With, Content-Type, Authorization, credential, X-XSRF-TOKEN, token, username, client, request-origion, sign, nonce_str";
    private static final String ALLOWED_METHODS = "GET,POST,PUT,DELETE,OPTIONS,HEAD";
    private static final String ALLOWED_ORIGIN = "*";
    private static final String ALLOWED_EXPOSE = "*";
ruoyi-gateway/src/main/java/com/ruoyi/gateway/filter/SignFilter.java
@@ -67,16 +67,16 @@
        if(method != HttpMethod.POST){
            return chain.filter(exchange.mutate().request(mutate.build()).build());
        }
        String sing = request.getHeaders().getFirst(TokenConstants.SING);
        String sign = request.getHeaders().getFirst(TokenConstants.SIGN);
        String nonce_str = request.getHeaders().getFirst(TokenConstants.NONCE_STR);
//        if (parameter_signature && StringUtils.isEmpty(sing)) {
//            return unauthorizedResponse(exchange, "签名不能为空!");
//        }
//        if (parameter_signature && StringUtils.isEmpty(nonce_str)) {
//            return unauthorizedResponse(exchange, "签名不能为空!");
//        }
        if (parameter_signature && StringUtils.isEmpty(sign)) {
            return unauthorizedResponse(exchange, "签名不能为空!");
        }
        if (parameter_signature && StringUtils.isEmpty(nonce_str)) {
            return unauthorizedResponse(exchange, "签名不能为空!");
        }
        if(parameter_signature){
            return authSign(exchange, chain, sing, nonce_str);
            return authSign(exchange, chain, sign, nonce_str);
        }
        return chain.filter(exchange.mutate().request(mutate.build()).build());
    }
@@ -93,14 +93,14 @@
     * 校验签名
     * @return
     */
    private Mono<Void> authSign(ServerWebExchange exchange, GatewayFilterChain chain, String sing, String nonce_str){
    private Mono<Void> authSign(ServerWebExchange exchange, GatewayFilterChain chain, String sign, String nonce_str){
        return DataBufferUtils.join(exchange.getRequest().getBody())
                .flatMap(dataBuffer -> {
                    byte[] bytes = new byte[dataBuffer.readableByteCount()];
                    dataBuffer.read(bytes);
                    String bodyString = new String(bytes, StandardCharsets.UTF_8);
                    log.info("请求参数:{}", bodyString);
                    if(!authSign(JSON.parseObject(bodyString), sing, nonce_str)){
                    if(!authSign(JSON.parseObject(bodyString), sign, nonce_str)){
                        return unauthorizedResponse(exchange, "签名验证失败!");
                    }
                    DataBufferUtils.release(dataBuffer);
@@ -127,13 +127,14 @@
     * @return
     */
    private boolean authSign(JSONObject jsonStr, String sign, String nonce_str) {
        System.err.println("请求签名:" + sign);
        String signUrlEncode = localSignUrl(jsonStr, nonce_str);
        signUrlEncode = signUrlEncode.replaceAll("& #40;", "\\(")
                .replaceAll("& #41;", "\\)")
                .replaceAll("\\+", " ");
                .replaceAll("& #41;", "\\)");
        if(sign.equals(signUrlEncode)){
            return true;
        }
        System.err.println("签名值:" + signUrlEncode);
        return false;
    }
    
@@ -161,6 +162,7 @@
            }
        }
        String signUrl = sb.substring(0, sb.length() - 1);
        System.err.println("签名串:" + signUrl);
        return signUrlEncode(signUrl, key);
    }
    
ruoyi-modules/ruoyi-system/pom.xml
@@ -61,10 +61,10 @@
        </dependency>
        
        <!-- RuoYi Common DataSource -->
        <dependency>
            <groupId>com.ruoyi</groupId>
            <artifactId>ruoyi-common-datasource</artifactId>
        </dependency>
        <!--<dependency>-->
            <!--<groupId>com.ruoyi</groupId>-->
            <!--<artifactId>ruoyi-common-datasource</artifactId>-->
        <!--</dependency>-->
        
        <!-- RuoYi Common DataScope -->
        <dependency>
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/controller/SysRoleController.java
@@ -112,8 +112,10 @@
        for (SysRole record : page.getRecords()) {
            List<Integer> data = roleSiteClient.getSiteIds(record.getRoleId()).getData();
            List<Site> sites = siteClient.getSiteByIds(data).getData();
            if(null != sites){
            List<String> collect = sites.stream().map(Site::getName).collect(Collectors.toList());
            record.setSiteNames(collect);
            }
        }
        return AjaxResult.success(page);
    }
@@ -314,7 +316,7 @@
     * @return
     */
    @ResponseBody
    @GetMapping(value = "/getSysRoleByIds")
    @PostMapping(value = "/getSysRoleByIds")
    public R<List<SysRole>> getSysRoleByIds(@RequestBody GetSysRoleByIds ids){
        List<SysRole> sysRoleByIds = roleService.getSysRoleByIds(ids.getIds());
        return R.ok(sysRoleByIds);
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/controller/SysUserController.java
@@ -595,7 +595,7 @@
     * @param userId
     * @return
     */
    @GetMapping("/queryRoleByUserId/{userId}")
    @PostMapping("/queryRoleByUserId/{userId}")
    public R<SysRole> queryRoleByUserId(@PathVariable("userId") Long userId){
        SysUserRole one = sysUserRoleService.getOne(Wrappers.lambdaQuery(SysUserRole.class)
                .eq(SysUserRole::getUserId, userId)
@@ -611,7 +611,7 @@
    }
    @GetMapping("/queryRoleByRoleId/{roleId}")
    @PostMapping("/queryRoleByRoleId/{roleId}")
    public R<SysRole> queryRoleByRoleId(@PathVariable("roleId") Long roleId){
        return R.ok( roleService.getOne(Wrappers.lambdaQuery(SysRole.class)
                .eq(SysRole::getRoleId, roleId)
@@ -685,7 +685,9 @@
    @PostMapping("/addSysUser")
    @Transactional(rollbackFor = Exception.class)
    public R addSysUser(@RequestBody SysUser user){
        if(StringUtils.isEmpty(user.getUserName())){
        user.setUserName(user.getPhonenumber());
        }
        if(!org.springframework.util.StringUtils.hasLength(user.getNickName())){
            user.setNickName(user.getPhonenumber());
        }
@@ -697,7 +699,9 @@
        }
        user.setCreateBy(SecurityUtils.getUsername());
        user.setPassword(SecurityUtils.encryptPassword(user.getPassword()));
        if(null == user.getRoleType()){
        user.setRoleType(1);
        }
        userService.insertUser(user);
        SysUserRole sysUserRole = new SysUserRole();
        sysUserRole.setRoleId(user.getRoleId());
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysOperLogMapper.java
@@ -1,6 +1,8 @@
package com.ruoyi.system.mapper;
import java.util.List;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.ruoyi.system.api.domain.SysOperLog;
/**
@@ -8,8 +10,7 @@
 * 
 * @author ruoyi
 */
public interface SysOperLogMapper
{
public interface SysOperLogMapper extends BaseMapper<SysOperLog> {
    /**
     * 新增操作日志
     * 
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysDeptServiceImpl.java
@@ -112,7 +112,7 @@
    public List<Long> selectDeptListByRoleId(Long roleId)
    {
        SysRole role = roleMapper.selectRoleById(roleId);
        return deptMapper.selectDeptListByRoleId(roleId, role.isDeptCheckStrictly());
        return deptMapper.selectDeptListByRoleId(roleId, false);
    }
    /**
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysMenuServiceImpl.java
@@ -156,7 +156,7 @@
    public List<Long> selectMenuListByRoleId(Long roleId)
    {
        SysRole role = roleMapper.selectRoleById(roleId);
        return menuMapper.selectMenuListByRoleId(roleId, role.isMenuCheckStrictly());
        return menuMapper.selectMenuListByRoleId(roleId, false);
    }
    /**
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysOperLogServiceImpl.java
@@ -7,11 +7,14 @@
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.system.mapper.SysOperLogMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.ruoyi.system.api.domain.SysOperLog;
import com.ruoyi.system.service.ISysOperLogService;
import javax.annotation.Resource;
/**
 * 操作日志 服务层处理
@@ -19,8 +22,10 @@
 * @author ruoyi
 */
@Service
public class SysOperLogServiceImpl implements ISysOperLogService {
    @Autowired
public class SysOperLogServiceImpl extends ServiceImpl<SysOperLogMapper, SysOperLog> implements ISysOperLogService {
    @Resource
    private SysOperLogMapper operLogMapper;
    
    /**
@@ -73,50 +78,5 @@
    @Override
    public void cleanOperLog() {
        operLogMapper.cleanOperLog();
    }
    @Override
    public boolean saveBatch(Collection<SysOperLog> entityList, int batchSize) {
        return false;
    }
    @Override
    public boolean saveOrUpdateBatch(Collection<SysOperLog> entityList, int batchSize) {
        return false;
    }
    @Override
    public boolean updateBatchById(Collection<SysOperLog> entityList, int batchSize) {
        return false;
    }
    @Override
    public boolean saveOrUpdate(SysOperLog entity) {
        return false;
    }
    @Override
    public SysOperLog getOne(Wrapper<SysOperLog> queryWrapper, boolean throwEx) {
        return null;
    }
    @Override
    public Map<String, Object> getMap(Wrapper<SysOperLog> queryWrapper) {
        return null;
    }
    @Override
    public <V> V getObj(Wrapper<SysOperLog> queryWrapper, Function<? super Object, V> mapper) {
        return null;
    }
    @Override
    public BaseMapper<SysOperLog> getBaseMapper() {
        return null;
    }
    @Override
    public Class<SysOperLog> getEntityClass() {
        return null;
    }
}
ruoyi-modules/ruoyi-system/src/main/resources/bootstrap.yml
@@ -44,7 +44,7 @@
  enabled: true
  application-id: ${spring.application.name}
  tx-service-group: seata_tx_group    #此处配置自定义的seata事务分组名称
  enable-auto-data-source-proxy: true    #开启数据库代理
  enable-auto-data-source-proxy: false    #关闭数据库代理
  service:
    vgroup-mapping:
      seata_tx_group: default
ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysRoleMapper.xml
@@ -8,10 +8,7 @@
        <id     property="roleId"             column="role_id"               />
        <result property="roleName"           column="role_name"             />
        <result property="roleKey"            column="role_key"              />
        <result property="roleSort"           column="role_sort"             />
        <result property="dataScope"          column="data_scope"            />
        <result property="menuCheckStrictly"  column="menu_check_strictly"   />
        <result property="deptCheckStrictly"  column="dept_check_strictly"   />
        <result property="status"             column="status"                />
        <result property="delFlag"            column="del_flag"              />
        <result property="createBy"           column="create_by"             />
@@ -22,8 +19,8 @@
    </resultMap>
    
    <sql id="selectRoleVo">
        select distinct r.role_id, r.role_name, r.role_key, r.role_sort, r.data_scope, r.menu_check_strictly, r.dept_check_strictly,
            r.status, r.del_flag, r.create_time, r.remark ,r.carDataAuth,r.carTrainOperAuth,r.contractDataAuth
        select distinct r.role_id, r.role_name, r.role_key, r.data_scope,
            r.status, r.del_flag, r.create_time, r.remark
        from sys_role r
            left join sys_user_role ur on ur.role_id = r.role_id
            left join sys_user u on u.user_id = ur.user_id
@@ -53,7 +50,6 @@
        </if>
        <!-- 数据范围过滤 -->
        ${params.dataScope}
        order by r.role_sort
    </select>
    
    <select id="selectRolePermissionByUserId" parameterType="Long" resultMap="SysRoleResult">
@@ -167,9 +163,11 @@
    
    <select id="getSysRoleByIds" resultMap="SysRoleResult">
        <include refid="selectRoleVo"/>
        where id in
        <if test="null != ids and ids.size() > 0">
            where r.role_id in
        <foreach collection="ids" item="item" index="index" separator="," open="(" close=")">
            #{item}
        </foreach>
        </if>
    </select>
</mapper> 
ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysUserMapper.xml
@@ -43,7 +43,6 @@
        <id     property="roleId"       column="role_id"        />
        <result property="roleName"     column="role_name"      />
        <result property="roleKey"      column="role_key"       />
        <result property="roleSort"     column="role_sort"      />
        <result property="dataScope"     column="data_scope"    />
        <result property="status"       column="role_status"    />
    </resultMap>
@@ -51,7 +50,7 @@
    <sql id="selectUserVo">
        select u.user_id, u.dept_id, u.user_name, u.nick_name, u.email, u.avatar, u.phonenumber, u.password, u.sex, u.status, u.del_flag, u.login_ip, u.login_date, u.create_by, u.create_time, u.remark, 
        d.dept_id, d.parent_id, d.ancestors, d.dept_name, d.order_num, d.leader, d.status as dept_status,
        r.role_id, r.role_name, r.role_key, r.role_sort, r.data_scope, r.status as role_status ,u.roleType as roleType,u.objectId AS objectId
        r.role_id, r.role_name, r.role_key, r.data_scope, r.status as role_status ,u.roleType as roleType,u.objectId AS objectId
        from sys_user u
            left join sys_dept d on u.dept_id = d.dept_id
            left join sys_user_role ur on u.user_id = ur.user_id
ruoyi-service/ruoyi-account/pom.xml
@@ -137,6 +137,12 @@
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>com.alipay.sdk</groupId>
            <artifactId>alipay-sdk-java</artifactId>
            <version>4.38.10.ALL</version>
        </dependency>
    </dependencies>
    <build>
ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/ali/Constant/AliConstant.java
New file
@@ -0,0 +1,10 @@
package com.ruoyi.account.ali.Constant;
public class AliConstant {
    /**
     * 支付宝配置
     */
    public static final String GRANT_TYPE = "authorization_code";
}
ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/ali/model/AliProperties.java
New file
@@ -0,0 +1,70 @@
package com.ruoyi.account.ali.model;
import lombok.ToString;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/**
 * @author xiaochen
 * @ClassName ALiProperties
 * @Description
 * @date 2024-08-14 13:55
 */
@ToString
@Component
@ConfigurationProperties(prefix = "ali.conf")
public class AliProperties {
    /**
     * 商户私钥,您的PKCS8格式RSA2私钥
     */
    private String privateKey;
    /**
     * 支付宝公钥,查看地址:https://openhome.alipay.com/platform/keyManage.htm 对应APPID下的支付宝公钥。
     */
    private String alipayPublicKey;
    /**
     * 应用ID,您的APPID。
     */
    private String appId;
    /**
     * HTTP(S) 连接超时时间,单位毫秒
     *
     */
    public int getHttpConnectTimeoutMs() {
        return 6 * 1000;
    }
    /**
     * HTTP(S) 读数据超时时间,单位毫秒
     */
    public int getHttpReadTimeoutMs() {
        return 8 * 1000;
    }
    public String getPrivateKey() {
        return privateKey;
    }
    public void setPrivateKey(String privateKey) {
        this.privateKey = privateKey;
    }
    public String getAlipayPublicKey() {
        return alipayPublicKey;
    }
    public void setAlipayPublicKey(String alipayPublicKey) {
        this.alipayPublicKey = alipayPublicKey;
    }
    public String getAppId() {
        return appId;
    }
    public void setAppId(String appId) {
        this.appId = appId;
    }
}
ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/ali/tools/AliAppletTools.java
New file
@@ -0,0 +1,37 @@
package com.ruoyi.account.ali.tools;
import com.alipay.api.AlipayConfig;
import com.ruoyi.account.ali.model.AliProperties;
import lombok.extern.slf4j.Slf4j;
/**
 * @author xiaochen
 * @ClassName WxAppletTools
 * @Description
 * @date 2024-8-04 13:55
 */
@Slf4j
public class AliAppletTools {
    private static final String SERVER_URL = "https://openapi.alipay.com/gateway.do";
    private AliProperties aliProperties;
    public AliAppletTools(AliProperties aliProperties) {
        this.aliProperties = aliProperties;
    }
    /**
     * 初始化支付宝配置
     * @return
     */
    public AlipayConfig getAlipayConfig() {
        AlipayConfig alipayConfig = new AlipayConfig();
        alipayConfig.setServerUrl(SERVER_URL);
        alipayConfig.setAppId(aliProperties.getAppId());
        alipayConfig.setPrivateKey(aliProperties.getPrivateKey());
        alipayConfig.setAlipayPublicKey(aliProperties.getAlipayPublicKey());
        return alipayConfig;
    }
}
ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/controller/AliLoginController.java
New file
@@ -0,0 +1,91 @@
package com.ruoyi.account.controller;
import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
import com.alipay.api.AlipayConfig;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.diagnosis.DiagnosisUtils;
import com.alipay.api.request.AlipaySystemOauthTokenRequest;
import com.alipay.api.response.AlipaySystemOauthTokenResponse;
import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.ruoyi.account.ali.Constant.AliConstant;
import com.ruoyi.account.ali.model.AliProperties;
import com.ruoyi.account.ali.tools.AliAppletTools;
import com.ruoyi.account.api.model.TAppUser;
import com.ruoyi.account.service.TAppUserService;
import com.ruoyi.common.core.exception.ServiceException;
import com.ruoyi.common.core.web.domain.AjaxResult;
import com.ruoyi.common.security.service.TokenService;
import com.ruoyi.system.api.model.LoginUserApplet;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
/**
 * <p>
 *  支付宝小程序登录 前端控制器
 * </p>
 *
 * @author xiaochen
 * @since 2024-08-06
 */
@Slf4j
@RestController
@RequestMapping("/aliLogin")
public class AliLoginController {
    @Autowired
    private AliProperties aliProperties;
    @Autowired
    private TAppUserService appUserService;
    @Autowired
    private TokenService tokenService;
    @ApiOperation(value = "通过code获得openid",tags = {"支付宝小程序登录"})
    @GetMapping("/openIdByJsCode")
    public AjaxResult<Map<String, Object>> openIdByJsCode(@RequestParam(name = "code")@ApiParam(value = "code", required = true) String code) throws AlipayApiException {
        log.info("<<<<<<<<换取openid开始<<<<<<<<:{}", code);
        // 初始化SDK
        AlipayClient alipayClient = new DefaultAlipayClient(new AliAppletTools(aliProperties).getAlipayConfig());
        // 构造请求参数以调用接口
        AlipaySystemOauthTokenRequest request = new AlipaySystemOauthTokenRequest();
        // 设置授权码
        request.setCode(code);
        // 设置授权方式
        request.setGrantType(AliConstant.GRANT_TYPE);
        AlipaySystemOauthTokenResponse response = alipayClient.execute(request);
        TAppUser appUser = null;
        if (response.isSuccess()) {
            String openId = response.getOpenId();
            appUser = appUserService.getOne(Wrappers.lambdaQuery(TAppUser.class).eq(TAppUser::getAliOpenid, openId).last("limit 1"));
            if (Objects.isNull(appUser)) {
                appUser = new TAppUser();
                appUser.setWxOpenid(openId);
                appUserService.save(appUser);
            }
            log.info("支付宝小程序登录调用成功");
        } else {
             String diagnosisUrl = DiagnosisUtils.getDiagnosisUrl(response);
             log.warn("诊断结果:{}",diagnosisUrl);
             throw new ServiceException("支付宝小程序登录失败");
        }
        LoginUserApplet loginUserApplet = new LoginUserApplet();
        if(ObjectUtils.isNotNull(appUser)){
            loginUserApplet.setUserId(appUser.getId());
        }
        HashMap<String, Object> tokenInfos = new HashMap<>();
        tokenInfos.put("token",tokenService.createTokenApplet(loginUserApplet));
        tokenInfos.put("info",loginUserApplet);
        return AjaxResult.ok(tokenInfos);
    }
}
ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/controller/TAppCouponController.java
@@ -5,26 +5,31 @@
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.account.api.vo.CarListVO;
import com.ruoyi.account.api.vo.CouponListVOVO;
import com.ruoyi.account.api.vo.ExchangeRecordVO;
import com.ruoyi.account.service.TAppCouponService;
import com.ruoyi.account.service.TAppUserCarService;
import com.ruoyi.chargingPile.api.feignClient.ChargingPileClient;
import com.ruoyi.chargingPile.api.vo.SiteInfoVO;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.core.web.domain.AjaxResult;
import com.ruoyi.common.core.web.page.BasePage;
import com.ruoyi.common.core.web.page.PageInfo;
import com.ruoyi.common.security.service.TokenService;
import com.ruoyi.order.api.feignClient.ChargingOrderClient;
import com.ruoyi.other.api.domain.TCoupon;
import com.ruoyi.other.api.feignClient.OtherClient;
import io.swagger.annotations.ApiModelProperty;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
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 org.springframework.web.bind.annotation.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
/**
@@ -43,19 +48,66 @@
    @Autowired
    private OtherClient otherClient;
    @Autowired
    private TAppUserCarService appUserCarService;
    @Autowired
    private TokenService tokenService;
    @Autowired
    private ChargingPileClient chargingPileClient;
    @Autowired
    private ChargingOrderClient chargingOrderClient;
    /**
     * 查询用户可用优惠券数量
     * @return
     */
    @ApiOperation(value = "通过桩编号获取电站详情", tags = {"小程序-扫一扫"})
    @GetMapping(value = "/scan/siteInfo")
    public AjaxResult<SiteInfoVO> siteInfo(String number) {
        SiteInfoVO data = chargingPileClient.getSiteInfoByNumber(number).getData();
        List<TAppUserCar> cars = appUserCarService.list(new QueryWrapper<TAppUserCar>()
                .eq("app_user_id",tokenService.getLoginUserApplet().getUserId())
                .orderByDesc("create_time"));
        Long data1 = chargingOrderClient.getCar().getData();
        if (!cars.isEmpty()){
            if (data1 == -1){
                // 没有充电订单 展示最新添加的车辆
                data.setLicensePlate(cars.get(0).getLicensePlate());
                data.setVehicleBrand(cars.get(0).getVehicleBrand());
                data.setId(cars.get(0).getId());
            }else{
                for (TAppUserCar car : cars) {
                    if (car.getId().equals(data1)){
                        data.setLicensePlate(car.getLicensePlate());
                        data.setVehicleBrand(car.getVehicleBrand());
                        data.setId(car.getId());
                    }
                }
            }
        }
        return AjaxResult.ok(data);
    }
    /**
     * 查询用户可用优惠券数量
     * @param dto
     * @return
     */
    @ApiOperation(value = "选择优惠券分页查询", tags = {"小程序-扫一扫"})
    @PostMapping(value = "/scan/couponList")
    public AjaxResult<PageInfo<CouponListVOVO>> couponList(@RequestBody CouponListDto dto) {
        PageInfo<CouponListVOVO> res = tAppCouponService.couponList(dto);
        return AjaxResult.ok(res);
    }
    /**
     * 选择车辆列表 远程调用
     * @return
     */
    @ApiOperation(value = "选择车辆列表", tags = {"小程序-扫一扫"})
    @PostMapping(value = "/scan/carList")
    public R<List<TAppUserCar>> carList() {
        List<TAppUserCar> appUserId = appUserCarService.list(new QueryWrapper<TAppUserCar>()
                .eq("app_user_id",tokenService.getLoginUserApplet().getUserId()));
        return R.ok(appUserId);
    }
    /**
     * 管理后台远程调用 根据优惠券ids 查询对应的发放数量
ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/controller/TAppUserAddressController.java
@@ -4,6 +4,7 @@
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.ruoyi.account.api.model.TAppUserAddress;
import com.ruoyi.account.service.TAppUserAddressService;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.core.web.domain.AjaxResult;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
@@ -73,5 +74,17 @@
        return AjaxResult.ok(appUserAddressService.removeById(id));
    }
    /**
     * 根据id获取地址信息
     * @param id
     * @return
     */
    @PostMapping(value = "/getAppUserAddressById/{id}")
    public R<TAppUserAddress> getAppUserAddressById(@PathVariable Long id){
        TAppUserAddress userAddress = appUserAddressService.getById(id);
        return R.ok(userAddress);
    }
}
ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/controller/TAppUserCarController.java
@@ -34,7 +34,7 @@
     * @return
     */
    @PostMapping(value = "/t-app-user-car/getCarByIds")
    public R<List<TAppUserCar>> getCarByIds(@RequestBody List<Integer> carIds){
    public R<List<TAppUserCar>> getCarByIds(@RequestBody List<Long> carIds){
        return R.ok(appUserCarService.list(Wrappers.lambdaQuery(TAppUserCar.class).in(TAppUserCar::getId,carIds)));
    }
ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/controller/TAppUserController.java
@@ -4,19 +4,26 @@
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.account.api.dto.*;
import com.ruoyi.account.api.model.*;
import com.ruoyi.account.api.vo.CouponListVOVO;
import com.ruoyi.account.service.*;
import com.ruoyi.account.wx.body.resp.Code2SessionRespBody;
import com.ruoyi.account.wx.body.resq.Code2SessionResqBody;
import com.ruoyi.account.wx.model.WeixinProperties;
import com.ruoyi.account.wx.tools.WxAppletTools;
import com.ruoyi.account.util.GiveVipUtil;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.core.utils.bean.BeanUtils;
import com.ruoyi.common.core.web.domain.AjaxResult;
import com.ruoyi.common.core.web.domain.BasePojo;
import com.ruoyi.common.core.web.page.PageInfo;
import com.ruoyi.common.redis.service.RedisService;
import com.ruoyi.common.security.annotation.RequiresPermissions;
import com.ruoyi.common.security.service.TokenService;
import com.ruoyi.order.api.feignClient.ChargingOrderClient;
import com.ruoyi.order.api.feignClient.ExchangeOrderClient;
import com.ruoyi.order.api.model.TChargingOrder;
@@ -25,20 +32,23 @@
import com.ruoyi.other.api.domain.TCoupon;
import com.ruoyi.other.api.domain.TUserTag;
import com.ruoyi.other.api.feignClient.OtherClient;
import com.ruoyi.system.api.domain.SysRole;
import com.ruoyi.system.api.model.LoginUserApplet;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import org.apache.poi.ss.formula.functions.T;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.weaver.loadtime.Aj;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.client.RestTemplate;
import javax.annotation.Resource;
import java.time.Duration;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.*;
import java.util.stream.Collectors;
/**
@@ -49,6 +59,7 @@
 * @author luodangjia
 * @since 2024-08-06
 */
@Slf4j
@RestController
@RequestMapping("/t-app-user")
public class TAppUserController {
@@ -73,6 +84,14 @@
    @Resource
    private ExchangeOrderClient exchangeOrderClient;
    @Autowired
    private TokenService tokenService;
    @Autowired
    private RedisService redisService;
    @Autowired
    private WeixinProperties wxConfig;
    @Autowired
    private RestTemplate wxRestTemplate;
    @Resource
    private  TAppUserSignService signService;
@@ -82,6 +101,45 @@
    private TAppUserCarService carService;
    @ApiOperation(value = "查询当前用户是否为会员 0否1是", tags = {"小程序--查询当前用户是否为会员"})
    @PostMapping(value = "/getUserInfo")
    public AjaxResult<Integer> getUserInfo() {
        TAppUser byId = appUserService.getById(tokenService.getLoginUserApplet().getUserId());
        if (byId.getVipEndTime() == null){
            return AjaxResult.ok(0);
        }else if (byId.getVipEndTime().isAfter(LocalDateTime.now())){
            return AjaxResult.ok(1);
        }else{
            return AjaxResult.ok(0);
        }
    }
    @ApiOperation(value = "通过code获得openid,  1 --->对应的appid:wx4c405fa42539fc21  2---->对应的appid:wx02d9f6c92e6d3c86")
    @GetMapping("openId-by-jscode2session/{code}")
    public AjaxResult<Map<String, Object>> jscode2session(@PathVariable String code) {
        log.info("<<<<<<<<换取openid开始<<<<<<<<:{}", code);
        WxAppletTools appletTools = new WxAppletTools(wxRestTemplate, wxConfig);
        Code2SessionRespBody body = appletTools.getOpenIdByJscode2session(new Code2SessionResqBody().build(code));
        String openid = body.getOpenid();
        String sessionKey = body.getSessionKey();
        TAppUser appUser = appUserService.getOne(Wrappers.lambdaQuery(TAppUser.class).eq(TAppUser::getWxOpenid, openid).last("limit 1"));
        if (Objects.isNull(appUser)) {
            appUser = new TAppUser();
            appUser.setWxOpenid(openid);
            appUserService.save(appUser);
        }
        // 提前对sessionKey进行删除
        log.info("换取sessionKey:{}", sessionKey);
        // 将sessionKey进行存储,后续获取信息需要
        redisService.setCacheObject(openid, sessionKey);
        LoginUserApplet loginUserApplet = new LoginUserApplet();
        if(ObjectUtils.isNotNull(appUser)){
            loginUserApplet.setUserId(appUser.getId());
        }
        HashMap<String, Object> tokenInfos = new HashMap<>();
        tokenInfos.put("token",tokenService.createTokenApplet(loginUserApplet));
        tokenInfos.put("info",loginUserApplet);
        return AjaxResult.ok(tokenInfos);
    }
    @ApiOperation(value = "管理后台-根据手机号查询用户ids", tags = {"管理后台-活动费用统计"})
    @PostMapping(value = "/user/getUserIdsByPhone")
@@ -422,10 +480,23 @@
     * @param id
     * @return
     */
    @GetMapping(value = "/user/getUserById/{id}")
    @PostMapping(value = "/user/getUserById/{id}")
    public R<TAppUser> getUserById(@PathVariable Long id){
        TAppUser appUser = appUserService.getById(id);
        return R.ok(appUser);
    }
    /**
     * 修改用户信息
     * @param appUser
     * @return
     */
    @ResponseBody
    @PostMapping("/user/updateAppUser")
    public R updateAppUser(@RequestBody TAppUser appUser){
        appUserService.updateById(appUser);
        return R.ok();
    }
}
ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/controller/TAppUserIntegralChangeController.java
@@ -1,8 +1,15 @@
package com.ruoyi.account.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.ruoyi.account.api.model.TAppUserIntegralChange;
import com.ruoyi.account.service.TAppUserIntegralChangeService;
import com.ruoyi.common.core.domain.R;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
/**
 * <p>
@@ -16,5 +23,22 @@
@RequestMapping("/t-app-user-integral-change")
public class TAppUserIntegralChangeController {
    @Resource
    private TAppUserIntegralChangeService appUserIntegralChangeService;
    /**
     * 添加积分变动记录
     * @param appUserIntegralChange
     * @return
     */
    @ResponseBody
    @PostMapping("/addAppUserIntegralChange")
    public R addAppUserIntegralChange(@RequestBody TAppUserIntegralChange appUserIntegralChange){
        String code = Math.random() * 1000 + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmssSSS"));
        appUserIntegralChange.setCode(code);
        appUserIntegralChangeService.save(appUserIntegralChange);
        return R.ok();
    }
}
ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/controller/WxLoginController.java
New file
@@ -0,0 +1,67 @@
package com.ruoyi.account.controller;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.ruoyi.account.api.model.TAppUser;
import com.ruoyi.account.service.TAppUserService;
import com.ruoyi.account.wx.body.resp.Code2SessionRespBody;
import com.ruoyi.account.wx.body.resq.Code2SessionResqBody;
import com.ruoyi.account.wx.model.WeixinProperties;
import com.ruoyi.account.wx.pojo.AppletUserDecodeData;
import com.ruoyi.account.wx.pojo.AppletUserEncrypteData;
import com.ruoyi.account.wx.tools.WxAppletTools;
import com.ruoyi.account.wx.tools.WxUtils;
import com.ruoyi.common.core.exception.ServiceException;
import com.ruoyi.common.core.web.domain.AjaxResult;
import com.ruoyi.common.redis.service.RedisService;
import com.ruoyi.common.security.service.TokenService;
import com.ruoyi.system.api.model.LoginUserApplet;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.client.RestTemplate;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
/**
 * <p>
 *  微信小程序登录 前端控制器
 * </p>
 *
 * @author xiaochen
 * @since 2024-08-06
 */
@Slf4j
@RestController
@RequestMapping("/wxLogin")
public class WxLoginController {
    @Autowired
    private TAppUserService appUserService;
    @Autowired
    private WeixinProperties wxConfig;
    @Autowired
    private RestTemplate wxRestTemplate;
    @ApiOperation(value = "通过code获得openid,获取用户信息",tags = {"微信小程序登录"})
    @PostMapping("/openIdByJsCode")
    public AjaxResult<Map<String, Object>> openIdByJsCode(@RequestBody AppletUserEncrypteData data) {
        log.info("<<<<<<<<换取openid开始<<<<<<<<:{}", data.getCode());
        WxAppletTools appletTools = new WxAppletTools(wxRestTemplate, wxConfig);
        Code2SessionRespBody body = appletTools.getOpenIdByJscode2session(new Code2SessionResqBody().build(data.getCode()));
        String openid = body.getOpenid();
        String sessionKey = body.getSessionKey();
        // 用户信息解密 数据验签
        if (StringUtils.isNotBlank(data.getSignature())) {
            WxUtils.verifySignature(data.getRawData(), sessionKey, data.getSignature());
        }
        AppletUserDecodeData appletUserDecodeData = WxUtils.encryptedData(data.getEncryptedData(), sessionKey,  data.getIv());
        appletUserDecodeData.setOpenId(openid);
        return AjaxResult.ok(appUserService.login(appletUserDecodeData));
    }
}
ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/service/TAppUserService.java
@@ -2,6 +2,9 @@
import com.baomidou.mybatisplus.extension.service.IService;
import com.ruoyi.account.api.model.TAppUser;
import com.ruoyi.account.wx.pojo.AppletUserDecodeData;
import java.util.Map;
/**
 * <p>
@@ -13,4 +16,11 @@
 */
public interface TAppUserService extends IService<TAppUser> {
    /**
     * 微信小程序登录用户封装
     * @param appletUserDecodeData
     * @return
     */
    Map<String, Object> login(AppletUserDecodeData appletUserDecodeData);
}
ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/service/impl/TAppCouponServiceImpl.java
@@ -14,6 +14,7 @@
import com.ruoyi.account.service.TAppCouponService;
import com.ruoyi.account.service.TAppUserVipDetailService;
import com.ruoyi.chargingPile.api.feignClient.ChargingPileClient;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.core.utils.DateUtils;
import com.ruoyi.common.core.web.page.BasePage;
import com.ruoyi.common.core.web.page.PageInfo;
@@ -31,8 +32,11 @@
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.util.Comparator;
import java.util.Date;
import java.util.List;
@@ -139,9 +143,14 @@
        PageInfo<CouponListVOVO> pageInfo = new PageInfo<>(dto.getPageCurr(),dto.getPageSize());
        List<CouponListVOVO> list = this.baseMapper.couponList(pageInfo,dto.getUserId());
        TAppUser tAppUser = tAppUserMapper.selectById(dto.getUserId());
        Integer isVip = 0;// 是否能享受会员折扣
        Integer isVip = 0;// 是否能享受会员折扣 0否 1是普通会员 2内部会员
        // 会员折扣
        Double discount = 0.0;
        // 普通会员最高折扣金额
        Double discountMoney = 0.0;
        if (tAppUser.getVipEndTime() == null){
            // 不是会员
            isVip = 0;
        }else {
            // 判断会员有没有过期
            if (tAppUser.getVipEndTime().isAfter(java.time.LocalDateTime.now())){
@@ -161,20 +170,79 @@
                TVip data1 = vipClient.getInfo1(vipId).getData();
                Integer discountTimes = data1.getDiscountTimes();
                if (data>=discountTimes){
                    // 不能再享受会员折扣
                    //  折扣次数用光了 不能再享受会员折扣
                    isVip = 0;
                }else{
                    if (data1.getType() == 2){
                        // 如果内部会员 使用该折扣
                        discount = data1.getDiscount().doubleValue();
                        isVip = 2;
                    }else{
                        // 普通会员 最高折扣金额
                        discountMoney = data1.getMaximumDeduction().doubleValue();
                    isVip = 1;
                }
                }
            }else{
                // 会员过期 不能享受
                isVip = 0;
            }
        }
        for (CouponListVOVO couponListVOVO : list) {
            couponListVOVO.setValidityTime("有效期:"+format.format(couponListVOVO.getCreateTime())+" - "+format.format(couponListVOVO.getEndTime()));
        }
        List<CouponListVOVO> data = otherClient.getCouponInfoByCouponIds(list).getData();
        for (CouponListVOVO datum : data) {
        // 桩编号 + 是否是会员 + 内部会员折扣 + 普通会员最高折扣金额 + 预付金额
        String temp = dto.getNumber()+"-"+isVip+"-"+discount+"-"+discountMoney+"-"+dto.getPayMoney();
            // 根据当前时间段和预付金额计算服务费
        BigDecimal serviceMoney = chargingPileClient.getServiceMoney(temp).getData();
        for (CouponListVOVO datum : data) {
            // 判断优惠券类型
            switch (datum.getPreferentialMode()){
                case 1:
                    datum.setIsUse(1);
                    // 满减券 判断服务费是否满足满减条件
                    if (datum.getMeetTheConditions().compareTo(new BigDecimal(BigInteger.ZERO)) == 0){
                        // 无门槛优惠券
                        datum.setMoney(serviceMoney.subtract(datum.getDiscountAmount()));
                    }else if (serviceMoney.compareTo(datum.getMeetTheConditions())>=0){
                        // 满足条件
                        datum.setMoney(serviceMoney.subtract(datum.getDiscountAmount()));
                    }else{
                        // 不满足条件
                        datum.setMoney(serviceMoney);
                        datum.setIsUse(0);
        }
                    break;
                case 2:
                    datum.setIsUse(1);
                    // 折扣券 判断
                    if (datum.getMeetTheConditions().compareTo(new BigDecimal(BigInteger.ZERO)) == 0){
                        // 无门槛优惠券
                        datum.setMoney(serviceMoney.subtract(datum.getDiscountAmount()));
                    }else if (serviceMoney.compareTo(datum.getMeetTheConditions())>=0){
                        // 折扣后的服务费
                        BigDecimal divide = serviceMoney.multiply(datum.getDiscount()).divide(new BigDecimal(10), 2, BigDecimal.ROUND_HALF_UP);
                        // 计算折扣了多少钱 判断是否超过最高折扣金额
                        BigDecimal subtract = serviceMoney.subtract(divide);
                        if (subtract.compareTo(datum.getMaximumDiscountAmount())>0){
                            // 超过最高折扣金额 减去最高折扣金额
                            datum.setMoney(serviceMoney.subtract(datum.getMaximumDiscountAmount()));
                        }else{
                            // 没超过最高折扣金额
                            datum.setMoney(divide);
                        }
                    }else{
                        // 不满足条件
                        datum.setMoney(serviceMoney);
                        datum.setIsUse(0);
                    }
                    break;
            }
        }
        // 通过集合的money、isUse字段 将money、isUse从大到小排序 将不可用的排在最后
        data.sort(Comparator.comparing(CouponListVOVO::getMoney).reversed().thenComparing(CouponListVOVO::getIsUse));
        pageInfo.setRecords(data);
        return pageInfo;
    }
ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/service/impl/TAppUserServiceImpl.java
@@ -1,10 +1,23 @@
package com.ruoyi.account.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.account.api.model.TAppUser;
import com.ruoyi.account.mapper.TAppUserMapper;
import com.ruoyi.account.service.TAppUserService;
import com.ruoyi.account.wx.model.WeixinProperties;
import com.ruoyi.account.wx.pojo.AppletUserDecodeData;
import com.ruoyi.common.security.service.TokenService;
import com.ruoyi.system.api.model.LoginUserApplet;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
/**
 * <p>
@@ -16,5 +29,48 @@
 */
@Service
public class TAppUserServiceImpl extends ServiceImpl<TAppUserMapper, TAppUser> implements TAppUserService {
    @Autowired
    private TokenService tokenService;
    @Override
    public Map<String, Object> login(AppletUserDecodeData appletUserDecodeData) {
        // 通过手机号查询用户,是否已存在手动导入用户
        TAppUser appUser = this.getOne(Wrappers.lambdaQuery(TAppUser.class)
                .eq(TAppUser::getPhone, appletUserDecodeData.getPhoneNumber())
                .isNull(TAppUser::getWxOpenid)
                .last("LIMIT 1"));
        LambdaQueryWrapper<TAppUser> wrapper = Wrappers.lambdaQuery(TAppUser.class)
                .eq(TAppUser::getWxOpenid, appletUserDecodeData.getOpenId());
        if(Objects.isNull(appUser)){
            // 先使用openId和当前手机号进行查询
            wrapper.eq(TAppUser::getPhone, appletUserDecodeData.getPhoneNumber())
                    .last("LIMIT 1");
            appUser = this.getOne(wrapper);
            if(Objects.isNull(appUser)){
                appUser = new TAppUser();
                appUser.setPhone(appletUserDecodeData.getPhoneNumber());
            }
        }else {
            wrapper.last("LIMIT 1");
            // 删除小程序原有授权用户
            this.remove(wrapper);
        }
        appUser.setAvatar(appletUserDecodeData.getAvatarUrl());
        appUser.setCity(appletUserDecodeData.getCity());
        appUser.setName(appletUserDecodeData.getNickName());
        appUser.setProvince(appletUserDecodeData.getProvince());
        appUser.setWxOpenid(appletUserDecodeData.getOpenId());
        this.saveOrUpdate(appUser);
        LoginUserApplet loginUserApplet = new LoginUserApplet();
        if(ObjectUtils.isNotNull(appUser)){
            loginUserApplet.setUserId(appUser.getId());
            loginUserApplet.setName(appUser.getName());
            loginUserApplet.setPhone(appUser.getPhone());
            loginUserApplet.setAvatar(appUser.getAvatar());
            loginUserApplet.setAddress(appUser.getProvince()+appUser.getCity());
        }
        Map<String, Object> tokenInfos = new HashMap<>();
        tokenInfos.put("token",tokenService.createTokenApplet(loginUserApplet));
        tokenInfos.put("info",loginUserApplet);
        return tokenInfos;
    }
}
ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/wx/body/resp/AccessTokenRespBody.java
New file
@@ -0,0 +1,28 @@
package com.ruoyi.account.wx.body.resp;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import java.io.Serializable;
/**
 * AccessToken 全局唯一
 *
 * @author xiaochen
 */
@Data
public class AccessTokenRespBody extends RespBody implements Serializable {
    /**
     * 获取到的凭证
     */
    @JsonProperty("access_token")
    private String accessToken;
    /**
     * 凭证有效时间,单位:秒
     */
    @JsonProperty("expires_in")
    private int expiresIn;
}
ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/wx/body/resp/Code2SessionRespBody.java
New file
@@ -0,0 +1,29 @@
package com.ruoyi.account.wx.body.resp;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
/**
 * @author xiaochen
 * @ClassName Code2SessionRespBody
 * @Description
 * @date 2021-07-28 12:35
 */
@Data
public class Code2SessionRespBody extends RespBody {
    /**
     * 用户唯一标识
     */
    @JsonProperty("openid")
    private String openid;
    /**
     * 会话密钥
     */
    @JsonProperty("session_key")
    private String sessionKey;
    /**
     * 用户在开放平台的唯一标识符,若当前小程序已绑定到微信开放平台帐号下会返回,详见 UnionID 机制说明。
     */
    @JsonProperty("unionid")
    private String unionid;
}
ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/wx/body/resp/RespBody.java
New file
@@ -0,0 +1,19 @@
package com.ruoyi.account.wx.body.resp;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
/**
 * @author xiaochen
 * @ClassName RespBody
 * @Description
 * @date 2021-07-28 11:44
 */
@Data
public class RespBody {
    @JsonProperty("errcode")
    private Integer errorCode;
    @JsonProperty("errmsg")
    private String errorMsg;
}
ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/wx/body/resq/Code2SessionResqBody.java
New file
@@ -0,0 +1,21 @@
package com.ruoyi.account.wx.body.resq;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
/**
 * @author xiaochen
 * @ClassName Code2SessionResqBody
 * @Description
 * @date 2021-07-28 11:47
 */
@Data
public class Code2SessionResqBody {
    @JsonProperty("js_code")
    private String jsCode;
    public Code2SessionResqBody build(String jsCode) {
        this.jsCode = jsCode;
        return this;
    }
}
ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/wx/model/WeixinProperties.java
New file
@@ -0,0 +1,80 @@
package com.ruoyi.account.wx.model;
import lombok.ToString;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.NestedConfigurationProperty;
import org.springframework.stereotype.Component;
/**
 * @author xiaochen
 * @ClassName WeixinProperties
 * @Description
 * @date 2024-08-14 13:55
 */
@ToString
@Component
@ConfigurationProperties(prefix = "wx.conf")
public class WeixinProperties {
    /**
     * 默认开启
     */
    private boolean enabled = true;
    /**
     * 获取 App ID
     *
     * @return App ID
     */
    private String appId;
    /**
     * 获取 Mch ID
     *
     * @return Mch ID
     */
    private String mchId;
    /**
     * 获取 secret ID
     *
     * @return secret ID
     */
    private String secretId;
    public String getSecretId() {
        return secretId;
    }
    public void setSecretId(String secretId) {
        this.secretId = secretId;
    }
    /**
     * HTTP(S) 连接超时时间,单位毫秒
     *
     */
    public int getHttpConnectTimeoutMs() {
        return 6 * 1000;
    }
    /**
     * HTTP(S) 读数据超时时间,单位毫秒
     */
    public int getHttpReadTimeoutMs() {
        return 8 * 1000;
    }
    public String getAppId() {
        return appId;
    }
    public void setAppId(String appId) {
        this.appId = appId;
    }
    public String getMchId() {
        return mchId;
    }
    public void setMchId(String mchId) {
        this.mchId = mchId;
    }
}
ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/wx/pojo/AppletPhoneEncrypteData.java
New file
@@ -0,0 +1,19 @@
package com.ruoyi.account.wx.pojo;
import lombok.Data;
/**
 * @author xiaochen
 * @ClassName AppletUserDecodeData
 * @Description
 * @date 2021-08-13 17:46
 * 小程序加密数据体
 *
 */
@Data
public class AppletPhoneEncrypteData {
    private String encryptedData;
    private String openid;
    private String unionid;
    private String iv;
}
ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/wx/pojo/AppletUserDecodeData.java
New file
@@ -0,0 +1,52 @@
package com.ruoyi.account.wx.pojo;
import lombok.Data;
/**
 * @author xiaochen
 * @ClassName AppletUserDecodeData
 * @Description
 * 用户主体信息部分
 * {
 *     "openId": "OPENID",
 *     "nickName": "NICKNAME",
 *     "gender": GENDER,
 *     "city": "CITY",
 *     "province": "PROVINCE",
 *     "country": "COUNTRY",
 *     "avatarUrl": "AVATARURL",
 *     "unionId": "UNIONID",
 *     "watermark":
 *     {
 *         "appid":"APPID",
 *         "timestamp":TIMESTAMP
 *     }
 * }
 * 电话部分
 * {
 *     "phoneNumber": "13580006666",
 *     "purePhoneNumber": "13580006666",
 *     "countryCode": "86",
 *     "watermark":
 *     {
 *         "appid":"APPID",
 *         "timestamp": TIMESTAMP
 *     }
 * }
 *
 */
@Data
public class AppletUserDecodeData {
    private String openId;
    private String unionId;
    private String nickName;
    private int gender;
    private String city;
    private String province;
    private String country;
    private String avatarUrl;
    private Watermark watermark;
    private String phoneNumber;
    private String purePhoneNumber;
    private String countryCode;
}
ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/wx/pojo/AppletUserEncrypteData.java
New file
@@ -0,0 +1,17 @@
package com.ruoyi.account.wx.pojo;
import lombok.Data;
/**
 * @author xiaochen
 * @ClassName AppletUserDecodeData
 * @Description
 * 小程序加密数据体
 *
 */
@Data
public class AppletUserEncrypteData extends AppletPhoneEncrypteData {
    private String rawData;
    private String signature;
    private String code;
}
ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/wx/pojo/Watermark.java
New file
@@ -0,0 +1,9 @@
package com.ruoyi.account.wx.pojo;
import lombok.Data;
@Data
public class Watermark {
    private String appid;
    private String timestamp;
}
ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/wx/tools/JsonUtils.java
New file
@@ -0,0 +1,110 @@
package com.ruoyi.account.wx.tools;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.hotel.config.JacksonConfig;
import com.hotel.exception.ServiceException;
import lombok.extern.slf4j.Slf4j;
import java.io.IOException;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
/**
 * Json转换工具类
 * 参考:https://blog.csdn.net/weixin_38413579/article/details/82562634
 * @author madman
 */
@Slf4j
public final class JsonUtils {
    private static final ObjectMapper OM = new ObjectMapper();
    private static final JavaTimeModule timeModule = new JavaTimeModule();
    /**
     * 转换LocalDateTime
     */
    static class LocalDateTimeSerializer extends JsonSerializer<LocalDateTime> {
        @Override
        public void serialize(LocalDateTime localDateTime, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
            jsonGenerator.writeString(localDateTime.format(DateTimeFormatter.ofPattern(JacksonConfig.dateTimeFormat)));
        }
    }
    /**
     * 转换LocalDate
     */
    static class LocalDateSerializer extends JsonSerializer<LocalDate> {
        @Override
        public void serialize(LocalDate localDate, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
            jsonGenerator.writeString(localDate.format(DateTimeFormatter.ofPattern(JacksonConfig.dateFormat)));
        }
    }
    /**
     * 设置 ObjectMapper
     *
     * @return
     */
    private static ObjectMapper getObjectMapper() {
        // 序列化
        timeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer());
        timeModule.addSerializer(LocalDate.class, new LocalDateSerializer());
        // 反序列化
        timeModule.addDeserializer(LocalDateTime.class,
                new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern(JacksonConfig.dateTimeFormat)));
        timeModule.addDeserializer(LocalDate.class,
                new LocalDateDeserializer(DateTimeFormatter.ofPattern(JacksonConfig.dateFormat)));
        // 允许对象忽略json中不存在的属性
        OM.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        OM.registerModule(timeModule);
        return OM;
    }
    /**
     * 将对象序列化
     */
    public static <T> String toJsonString(T obj) {
        try {
            ObjectMapper om = getObjectMapper();
            return om.writeValueAsString(obj);
        } catch (JsonProcessingException e) {
            log.error("转json字符串失败:{}", obj);
            return null;
        }
    }
    /**
     * 反序列化对象字符串
     */
    public static <T> T parseObject(String json, Class<T> clazz) {
        try {
            ObjectMapper om = getObjectMapper();
            return om.readValue(json, clazz);
        } catch (JsonProcessingException e) {
            throw new ServiceException("反序列化对象字符串失败");
        }
    }
    /**
     * 反序列化字符串成为对象
     */
    public static <T> T parseObject(String json, TypeReference<T> valueTypeRef) {
        try {
            ObjectMapper om = getObjectMapper();
            return om.readValue(json, valueTypeRef);
        } catch (JsonProcessingException e) {
            throw new ServiceException("反序列化字符串成为对象失败");
        }
    }
}
ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/wx/tools/SHA1.java
New file
@@ -0,0 +1,36 @@
package com.ruoyi.account.wx.tools;
import java.security.MessageDigest;
public class SHA1 {
    /**
     * 用SHA1算法生成安全签名
     *
     * @param str
     * @return
     * @throws WxException
     */
    public static String getSHA1(String str) throws WxException {
        try {
            // SHA1签名生成
            MessageDigest md = MessageDigest.getInstance("SHA-1");
            md.update(str.getBytes());
            byte[] digest = md.digest();
            StringBuffer hexstr = new StringBuffer();
            String shaHex;
            for (int i = 0; i < digest.length; i++) {
                shaHex = Integer.toHexString(digest[i] & 0xFF);
                if (shaHex.length() < 2) {
                    hexstr.append(0);
                }
                hexstr.append(shaHex);
            }
            return hexstr.toString();
        } catch (Exception e) {
            throw new WxException(WxException.ComputeSignatureError);
        }
    }
}
ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/wx/tools/WebUtils.java
New file
@@ -0,0 +1,48 @@
package com.ruoyi.account.wx.tools;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
/**
 * @Author xiaochen
 * @Date 2019/08/26 10:28 AM
 * @Description
 */
public final class WebUtils {
    private WebUtils() {
    }
    /**
     * 当前请求
     */
    public static HttpServletRequest request() {
        return contextHolder() == null ? null : contextHolder().getRequest();
    }
    /**
     * 当前响应
     */
    public static HttpServletResponse response() {
        return contextHolder() == null ? null : contextHolder().getResponse();
    }
    /**
     * 当前session
     */
    public static HttpSession session() {
        return request() == null ? null : request().getSession();
    }
    /**
     * 当前ServletRequest
     */
    public static ServletRequestAttributes contextHolder() {
        return (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
    }
}
ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/wx/tools/WxAppletTools.java
New file
@@ -0,0 +1,125 @@
package com.ruoyi.account.wx.tools;
import com.ruoyi.account.wx.body.resp.AccessTokenRespBody;
import com.ruoyi.account.wx.body.resp.Code2SessionRespBody;
import com.ruoyi.account.wx.body.resq.Code2SessionResqBody;
import com.ruoyi.account.wx.model.WeixinProperties;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.StringUtils;
import org.springframework.web.client.RestTemplate;
import java.text.MessageFormat;
/**
 * @author xiaochen
 * @ClassName WxAppletTools
 * @Description
 * @date 2024-8-04 13:55
 */
@Slf4j
public class WxAppletTools {
    private final static String ACCESSTOKEN_CACHE_KEY = "accessToken";
    /**
     * 请求参数
     * 属性    类型    默认值    必填    说明
     * appid    string        是    小程序 appId
     * secret    string        是    小程序 appSecret
     * js_code    string        是    登录时获取的 code
     * grant_type    string        是    授权类型,此处只需填写 authorization_cod
     * <p>
     * 返回值:
     * <p>
     * 属性    类型    说明
     * openid    string    用户唯一标识
     * session_key    string    会话密钥
     * unionid    string    用户在开放平台的唯一标识符,若当前小程序已绑定到微信开放平台帐号下会返回,详见 UnionID 机制说明。
     * errcode    number    错误码
     * errmsg    string    错误信息
     */
    private static final String JSCODE_2_SESSION_URL = "https://api.weixin.qq.com/sns/jscode2session?appid={0}&secret={1}&js_code={2}&grant_type=authorization_code";
    /**
     * 请求参数
     * 属性    类型    默认值    必填    说明
     * grant_type    string        是    填写 client_credential
     * appid    string        是    小程序唯一凭证,即 AppID,可在「微信公众平台 - 设置 - 开发设置」页中获得。(需要已经成为开发者,且帐号没有异常状态)
     * secret    string        是    小程序唯一凭证密钥,即 AppSecret,获取方式同 appid
     * 返回值
     * Object
     * 返回的 JSON 数据包
     * <p>
     * 属性    类型    说明
     * access_token    string    获取到的凭证
     * expires_in    number    凭证有效时间,单位:秒。目前是7200秒之内的值。
     * errcode    number    错误码
     * errmsg    string    错误信息
     */
    public static String ACCESS_TOKEN_URL = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={0}&secret={1}";
    private WeixinProperties wxConfig;
    private RestTemplate wxRestTemplate;
    private WxCacheTemplate<String> wxCacheTemplate;
    public WxAppletTools(RestTemplate wxRestTemplate, WeixinProperties wxConfig, WxCaffineCache wxCacheTemplate) {
        this.wxRestTemplate = wxRestTemplate;
        this.wxCacheTemplate = wxCacheTemplate;
        this.wxConfig = wxConfig;
    }
    public WxAppletTools(RestTemplate wxRestTemplate, WeixinProperties wxConfig) {
        this.wxRestTemplate = wxRestTemplate;
        this.wxConfig = wxConfig;
    }
    /**
     * 自定义部分数据
     *
     * @param wxConfig
     * @return
     */
    public WxAppletTools build(WeixinProperties wxConfig) {
        this.wxConfig = wxConfig;
        return this;
    }
    /**
     * @param resqBody
     * @return
     */
    public Code2SessionRespBody getOpenIdByJscode2session(Code2SessionResqBody resqBody) {
        long start = System.currentTimeMillis();
        String requestUrl = MessageFormat.format(JSCODE_2_SESSION_URL, wxConfig.getAppId(), wxConfig.getSecretId(), resqBody.getJsCode());
        long end = System.currentTimeMillis();
        log.info("code换取sessionKey时间:{}", (end - start));
        String respBody = wxRestTemplate.getForEntity(requestUrl, String.class).getBody();
        end = System.currentTimeMillis();
        log.info("code换取sessionKey时间:{}", (end - start));
        log.info("Jscode2session:{}", respBody);
        Code2SessionRespBody code2SessionRespBody = WxJsonUtils.parseObject(respBody, Code2SessionRespBody.class);
        // 判断有误异常
        if (StringUtils.hasLength(code2SessionRespBody.getErrorMsg())) {
            // 抛出错误
            throw new WxException(code2SessionRespBody.getErrorCode() + ":" + code2SessionRespBody.getErrorMsg());
        }
        return code2SessionRespBody;
    }
    /**
     * @return
     */
    public String getAccessToken(String version) {
        String accessToken = wxCacheTemplate.getKey(ACCESSTOKEN_CACHE_KEY + version);
        if (StringUtils.hasLength(accessToken)) {
            return accessToken;
        }
        String requestUrl = MessageFormat.format(ACCESS_TOKEN_URL, wxConfig.getAppId(), wxConfig.getSecretId());
        String respBody = wxRestTemplate.getForEntity(requestUrl, String.class).getBody();
        AccessTokenRespBody accessTokenRespBody = WxJsonUtils.parseObject(respBody, AccessTokenRespBody.class);
        // 判断有误异常
        if (StringUtils.hasLength(accessTokenRespBody.getErrorMsg())) {
            // 抛出错误
            throw new WxException(accessTokenRespBody.getErrorCode() + ":" + accessTokenRespBody.getErrorMsg());
        }
        wxCacheTemplate.setKey(ACCESSTOKEN_CACHE_KEY + version, accessTokenRespBody.getAccessToken());
        return accessTokenRespBody.getAccessToken();
    }
}
ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/wx/tools/WxCache.java
New file
@@ -0,0 +1,117 @@
package com.ruoyi.account.wx.tools;
import java.util.concurrent.TimeUnit;
/**
 * 缓存
 *
 * @author xiaochen
 */
class WxCache {
    /**
     * 缓存的初始化容量
     */
    private int initialCapacity = 50;
    /**
     * 缓存最大容量
     */
    private long maximumSize = 200L;
    /**
     * 缓存时长
     */
    private long duration = 7000L;
    /**
     * 时长单位,自动转换
     * 支持:
     * 时
     * 分
     * 秒
     * 天
     */
    private TimeUnit timeunit = TimeUnit.SECONDS;
    public int getInitialCapacity() {
        return initialCapacity;
    }
    public void setInitialCapacity(int initialCapacity) {
        this.initialCapacity = initialCapacity;
    }
    public long getMaximumSize() {
        return maximumSize;
    }
    public void setMaximumSize(long maximumSize) {
        this.maximumSize = maximumSize;
    }
    public long getDuration() {
        return duration;
    }
    public void setDuration(long duration) {
        this.duration = duration;
    }
    public TimeUnit getTimeunit() {
        return timeunit;
    }
    public void setTimeunit(TimeUnit timeunit) {
        this.timeunit = timeunit;
    }
    public static class Builder {
        private int initialCapacity;
        private long maximumSize;
        private long duration;
        private TimeUnit timeunit;
        public Builder setInitialCapacity(int initialCapacity) {
            this.initialCapacity = initialCapacity;
            return this;
        }
        public Builder setMaximumSize(long maximumSize) {
            this.maximumSize = maximumSize;
            return this;
        }
        public Builder setDuration(long duration) {
            this.duration = duration;
            return this;
        }
        public Builder setTimeUnit(TimeUnit timeunit) {
            this.timeunit = timeunit;
            return this;
        }
        public WxCache build() {
            return new WxCache(this);
        }
    }
    public static Builder options() {
        return new Builder();
    }
    private WxCache(Builder builder) {
        this.initialCapacity = 0 == builder.initialCapacity ? this.initialCapacity : builder.initialCapacity;
        this.maximumSize = 0L == builder.maximumSize ? this.maximumSize : builder.maximumSize;
        this.duration = 0L == builder.duration ? this.duration : builder.duration;
        this.timeunit = null == builder.timeunit ? this.timeunit : builder.timeunit;
    }
    @Override
    public String toString() {
        return "WxCache{" +
                "initialCapacity=" + initialCapacity +
                ", maximumSize=" + maximumSize +
                ", duration=" + duration +
                ", timeunit=" + timeunit +
                '}';
    }
}
ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/wx/tools/WxCacheTemplate.java
New file
@@ -0,0 +1,34 @@
package com.ruoyi.account.wx.tools;
/**
 * @author xiaochen
 * @ClassName WxCacheTemplate
 * @Description
 * @date 2021-01-11 11:27
 */
public interface WxCacheTemplate<T> {
    /**
     * 保存key
     *
     * @param key
     * @param value
     * @return
     */
     boolean setKey(String key, T value);
    /**
     * 获取缓存
     *
     * @param key
     * @return
     */
    T getKey(String key);
    /**
     * 删除
     *
     * @param key
     * @return
     */
    boolean delKey(String key);
}
ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/wx/tools/WxCaffineCache.java
New file
@@ -0,0 +1,122 @@
package com.ruoyi.account.wx.tools;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.StringUtils;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
/**
 * @author xiaochen
 * @ClassName AbstractCaffineCache
 * @Description
 * @date 2021-01-11 11:27
 */
@Slf4j
class WxCaffineCache<T>  implements WxCacheTemplate<T> {
    /**
     * 缓存环境
     */
    private String env = "wx";
    /**
     * 本地缓存实例
     */
    private LoadingCache<String, Object> loadingCache;
    /**
     * 构造函数
     *
     */
    public WxCaffineCache() {
        WxCache cache = WxCache.options().setTimeUnit(TimeUnit.SECONDS).build();
        // 构建本地缓存实例
        this.loadingCache = caffineCacheManage(cache);
    }
    @Override
    public boolean setKey(String key, T value) {
        if (Objects.isNull(this.loadingCache)) {
            return Boolean.FALSE;
        }
        if (StringUtils.hasLength(this.env)) {
            this.loadingCache.put(this.env + ":" + key, value);
        } else {
            this.loadingCache.put(key, value);
        }
        return Boolean.TRUE;
    }
    @Override
    public T getKey(String key) {
        if (Objects.isNull(this.loadingCache)) {
            return null;
        }
        try {
            if (StringUtils.hasLength(this.env)) {
                return (T) this.loadingCache.get(this.env + ":" + key);
            } else {
                return (T) this.loadingCache.get(key);
            }
        } catch (Exception e) {
            return null;
        }
    }
    @Override
    public boolean delKey(String key) {
        if (Objects.isNull(this.loadingCache)) {
            return Boolean.FALSE;
        }
        if (StringUtils.hasLength(this.env)) {
            this.loadingCache.invalidate(this.env + ":" + key);
        } else {
            this.loadingCache.invalidate(key);
        }
        return Boolean.TRUE;
    }
    /**
     * 缓存管理
     *
     * @param cache
     * @param <T>
     * @return
     */
    private static <T> LoadingCache<String, T> caffineCacheManage(WxCache cache) {
        log.info("初始化缓存的实体数据:{}", cache);
        if (Objects.isNull(cache)) {
            throw new NullPointerException("请实例化一个Cache对象!");
        }
        LoadingCache<String, T> localcache =
                // 构建本地缓存,调用链的方式
                // ,1000是设置缓存的初始化容量,maximumSize是设置缓存最大容量,当超过了最大容量,guava将使用LRU算法(最少使用算法),来移除缓存项
                // expireAfterAccess(12,TimeUnit.HOURS)设置缓存有效期为12个小时
                Caffeine.newBuilder().initialCapacity(cache.getInitialCapacity()).maximumSize(cache.getMaximumSize())
                        // 设置写缓存后n秒钟过期
                        // .expireAfterWrite(30, TimeUnit.SECONDS)
                        .expireAfterWrite(cache.getDuration(), cache.getTimeunit())
                        // 设置读写缓存后n秒钟过期,实际很少用到,类似于expireAfterWrite
                        //.expireAfterAccess(googleCache.getDuration(), googleCache.getTimeunit())
                        // 只阻塞当前数据加载线程,其他线程返回旧值
                        //.refreshAfterWrite(10, TimeUnit.SECONDS)
                        // 设置缓存的移除通知//用户手动移除EXPLICIT,
                        // //用户手动替换REPLACED,//被垃圾回收COLLECTED,//超时过期EXPIRED,//SIZE由于缓存大小限制
                        .removalListener(new RemovalListener<String, T>() {
                            @Override
                            public void onRemoval(String key, Object value, RemovalCause cause) {
                                log.info(key + ":" + value + ":" + cause.name());
                            }
                        })
                        // build里面要实现一个匿名抽象类
                        .build(new CacheLoader<String, T>() {
                            // 这个方法是默认的数据加载实现,get的时候,如果key没有对应的值,就调用这个方法进行加载。此处是没有默认值则返回null
                            @Override
                            public T load(String key) throws Exception {
                                return null;
                            }
                        });
        return localcache;
    }
}
ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/wx/tools/WxException.java
New file
@@ -0,0 +1,55 @@
package com.ruoyi.account.wx.tools;
/**
 * @author lihen
 */
public class WxException extends RuntimeException {
    private final static int OK = 0;
    private final static int ValidateSignatureError = -40001;
    private final static int ParseXmlError = -40002;
    public final static int ComputeSignatureError = -40003;
    private final static int IllegalAesKey = -40004;
    private final static int ValidateAppidError = -40005;
    private final static int EncryptAESError = -40006;
    private final static int DecryptAESError = -40007;
    private final static int IllegalBuffer = -40008;
    private int code;
    private static String getMessage(int code) {
        switch (code) {
            case ValidateSignatureError:
                return "签名验证错误";
            case ParseXmlError:
                return "xml解析失败";
            case ComputeSignatureError:
                return "sha加密生成签名失败";
            case IllegalAesKey:
                return "SymmetricKey非法";
            case ValidateAppidError:
                return "appid校验失败";
            case EncryptAESError:
                return "aes加密失败";
            case DecryptAESError:
                return "aes解密失败";
            case IllegalBuffer:
                return "解密后得到的buffer非法";
            default:
                return null;
        }
    }
    public int getCode() {
        return code;
    }
    WxException(int code) {
        super(getMessage(code));
        this.code = code;
    }
    public WxException(String message) {
        super(message);
    }
}
ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/wx/tools/WxJsonUtils.java
New file
@@ -0,0 +1,109 @@
package com.ruoyi.account.wx.tools;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import lombok.extern.slf4j.Slf4j;
import java.io.IOException;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
/**
 * Json转换工具类
 * 参考:https://blog.csdn.net/weixin_38413579/article/details/82562634
 * @author madman
 */
@Slf4j
public final class WxJsonUtils {
    public static final String dateFormat = "yyyy-MM-dd";
    public static final String dateTimeFormat = "yyyy-MM-dd HH:mm:ss";
    private static final ObjectMapper OM = new ObjectMapper();
    private static final JavaTimeModule timeModule = new JavaTimeModule();
    /**
     * 转换LocalDateTime
     */
    static class LocalDateTimeSerializer extends JsonSerializer<LocalDateTime> {
        @Override
        public void serialize(LocalDateTime localDateTime, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
            jsonGenerator.writeString(localDateTime.format(DateTimeFormatter.ofPattern(dateTimeFormat)));
        }
    }
    /**
     * 转换LocalDate
     */
    static class LocalDateSerializer extends JsonSerializer<LocalDate> {
        @Override
        public void serialize(LocalDate localDate, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
            jsonGenerator.writeString(localDate.format(DateTimeFormatter.ofPattern(dateFormat)));
        }
    }
    /**
     * 设置 ObjectMapper
     *
     * @return
     */
    private static ObjectMapper getObjectMapper() {
        // 序列化
        timeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer());
        timeModule.addSerializer(LocalDate.class, new LocalDateSerializer());
        // 反序列化
        timeModule.addDeserializer(LocalDateTime.class,
                new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern(dateTimeFormat)));
        timeModule.addDeserializer(LocalDate.class,
                new LocalDateDeserializer(DateTimeFormatter.ofPattern(dateFormat)));
        // 允许对象忽略json中不存在的属性
        OM.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        OM.registerModule(timeModule);
        return OM;
    }
    /**
     * 将对象序列化
     */
    public static <T> String toJsonString(T obj) {
        try {
            ObjectMapper om = getObjectMapper();
            return om.writeValueAsString(obj);
        } catch (JsonProcessingException e) {
            log.error("转json字符串失败:{}", obj);
            return null;
        }
    }
    /**
     * 反序列化对象字符串
     */
    public static <T> T parseObject(String json, Class<T> clazz) {
        try {
            ObjectMapper om = getObjectMapper();
            return om.readValue(json, clazz);
        } catch (JsonProcessingException e) {
            throw new RuntimeException("反序列化对象字符串失败");
        }
    }
    /**
     * 反序列化字符串成为对象
     */
    public static <T> T parseObject(String json, TypeReference<T> valueTypeRef) {
        try {
            ObjectMapper om = getObjectMapper();
            return om.readValue(json, valueTypeRef);
        } catch (JsonProcessingException e) {
            throw new RuntimeException("反序列化字符串成为对象失败");
        }
    }
}
ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/wx/tools/WxUtils.java
New file
@@ -0,0 +1,175 @@
package com.ruoyi.account.wx.tools;
import com.ruoyi.account.wx.pojo.AppletUserDecodeData;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.CharEncoding;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.util.encoders.Base64;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import javax.servlet.http.HttpServletRequest;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.security.AlgorithmParameters;
import java.security.Security;
import java.util.Arrays;
/**
 * @Description 获取用户信息工具类
 * @Author xiaochen
 * @Date 2021/8/12 15:45
 */
@Slf4j
public class WxUtils {
    /**
     * 微信小程序API 用户数据的解密
     *
     * @param encryptedData
     * @param sessionKey
     * @param iv
     * @return
     */
    public static AppletUserDecodeData encryptedData(String encryptedData, String sessionKey, String iv) {
        // 被加密的数据
        byte[] dataByte = Base64.decode(encryptedData);
        // 加密秘钥
        byte[] keyByte = Base64.decode(sessionKey);
        // 偏移量
        byte[] ivByte = Base64.decode(iv);
        try {
            // 如果密钥不足16位,那么就补足.  这个if 中的内容很重要
            int base = 16;
            if (keyByte.length % base != 0) {
                int groups = keyByte.length / base + (keyByte.length % base != 0 ? 1 : 0);
                byte[] temp = new byte[groups * base];
                Arrays.fill(temp, (byte) 0);
                System.arraycopy(keyByte, 0, temp, 0, keyByte.length);
                keyByte = temp;
            }
            // 初始化
            Security.addProvider(new BouncyCastleProvider());
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding", "BC");
            SecretKeySpec spec = new SecretKeySpec(keyByte, "AES");
            AlgorithmParameters parameters = AlgorithmParameters.getInstance("AES");
            parameters.init(new IvParameterSpec(ivByte));
            cipher.init(Cipher.DECRYPT_MODE, spec, parameters);
            byte[] resultByte = cipher.doFinal(dataByte);
            if (null != resultByte && resultByte.length > 0) {
                String result = new String(resultByte, CharEncoding.UTF_8);
                log.info("解密原串:{}", result);
                return WxJsonUtils.parseObject(result, AppletUserDecodeData.class);
            }
            throw new RuntimeException("解密的数据为空");
        } catch (Exception e) {
            log.error("解密失败. error = {}", e.getMessage(), e);
            throw new RuntimeException(e.getMessage());
        }
    }
    /**
     * 微信小程序API 用户数据的签名验证
     * signature = sha1( rawData + session_key )
     *
     * @param rawData    不包括敏感信息的原始数据字符串,用于计算签名。
     * @param sessionKey
     */
    public static void verifySignature(String rawData, String sessionKey, String signature) {
        String serverSignature = SHA1.getSHA1(rawData + sessionKey);
        log.info(rawData + ">>>>>>:" + sessionKey + " === " + serverSignature + "  ======" + signature);
        if (!signature.equals(serverSignature)) {
            throw new RuntimeException("数据验签不通过");
        }
    }
    /**
     * 根据流接收请求数据
     *
     * @param request
     * @return
     */
    public static String streamBodyByReceive(HttpServletRequest request) throws IOException {
        log.info("微信异步回调地址:{}", request.getRequestURL());
        StringBuffer buffer = new StringBuffer();
        InputStream inputStream = request.getInputStream();
        InputStreamReader reader = new InputStreamReader(inputStream);
        BufferedReader bufferedReader = new BufferedReader(reader);
        String body = null;
        while ((body = bufferedReader.readLine()) != null) {
            buffer.append(body);
        }
        String data = buffer.toString();
        reader.close();
        inputStream.close();
        log.info("微信异步回调数据:{}", data);
        return data;
    }
    /**
     * 日志
     *
     * @return
     */
    public static Logger getLogger() {
        Logger logger = LoggerFactory.getLogger("wxpay java sdk");
        return logger;
    }
    /**
     * debug
     *
     * @param msg
     * @param args
     */
    public static void debug(String msg, Object... args) {
        Logger log = getLogger();
        if (log.isDebugEnabled()) {
            log.debug(msg, args);
        }
    }
    /**
     * info
     *
     * @param msg
     * @param args
     */
    public static void info(String msg, Object... args) {
        Logger log = getLogger();
        if (log.isInfoEnabled()) {
            log.info(msg, args);
        }
    }
    /**
     * warn
     *
     * @param msg
     * @param args
     */
    public static void warn(String msg, Object... args) {
        Logger log = getLogger();
        if (log.isWarnEnabled()) {
            log.warn(msg, args);
        }
    }
    /**
     * error
     *
     * @param msg
     * @param args
     */
    public static void error(String msg, Object... args) {
        Logger log = getLogger();
        if (log.isErrorEnabled()) {
            log.error(msg, args);
        }
    }
}
ruoyi-service/ruoyi-account/src/main/resources/bootstrap.yml
@@ -43,7 +43,7 @@
  enabled: true
  application-id: ${spring.application.name}
  tx-service-group: seata_tx_group    #此处配置自定义的seata事务分组名称
  enable-auto-data-source-proxy: true    #开启数据库代理
  enable-auto-data-source-proxy: false    #关闭数据库代理
  service:
    vgroup-mapping:
      seata_tx_group: default
ruoyi-service/ruoyi-chargingPile/pom.xml
@@ -15,7 +15,12 @@
    </description>
    <dependencies>
        <dependency>
            <groupId>com.ruoyi</groupId>
            <artifactId>ruoyi-api-account</artifactId>
            <version>3.6.2</version>
            <scope>compile</scope>
        </dependency>
        <!-- ruoyi-modules-chargingPile-api -->
        <dependency>
            <groupId>com.ruoyi</groupId>
ruoyi-service/ruoyi-chargingPile/src/main/java/com/ruoyi/chargingPile/config/DataUpdateHandlerConfig.java
@@ -7,6 +7,7 @@
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import java.time.LocalDateTime;
import java.util.Date;
/**
@@ -33,8 +34,8 @@
     */
    @Override
    public void insertFill(MetaObject metaObject) {
        this.setFieldValByName("createTime", new Date(), metaObject);
        this.setFieldValByName("updateTime", new Date(), metaObject);
        this.setFieldValByName("createTime", LocalDateTime.now(), metaObject);
        this.setFieldValByName("updateTime", LocalDateTime.now(), metaObject);
    }
    /**
@@ -44,6 +45,6 @@
     */
    @Override
    public void updateFill(MetaObject metaObject) {
        this.setFieldValByName("updateTime", new Date(), metaObject);
        this.setFieldValByName("updateTime", LocalDateTime.now(), metaObject);
    }
}
ruoyi-service/ruoyi-chargingPile/src/main/java/com/ruoyi/chargingPile/controller/PartnerController.java
@@ -81,12 +81,12 @@
    
    
    @ResponseBody
    @DeleteMapping("/delPartner/{id}")
    @DeleteMapping("/delPartner")
    @ApiOperation(value = "删除合作商", tags = {"管理后台-合作商管理"})
    @ApiImplicitParams({
            @ApiImplicitParam(value = "合作商id", name = "id", dataTypeClass = Integer.class, required = true)
    })
    public AjaxResult delPartner(@PathVariable("id") Integer[] id){
    public AjaxResult delPartner(@RequestParam("id") List<Integer> id){
        return partnerService.delPartner(id);
    }
    
ruoyi-service/ruoyi-chargingPile/src/main/java/com/ruoyi/chargingPile/controller/SiteController.java
@@ -2,24 +2,16 @@
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.ruoyi.chargingPile.api.dto.GetSiteListDTO;
import com.ruoyi.chargingPile.api.feignClient.ParkingLotClient;
import com.ruoyi.chargingPile.api.model.Site;
import com.ruoyi.chargingPile.api.model.TChargingGun;
import com.ruoyi.chargingPile.api.model.TChargingPile;
import com.ruoyi.chargingPile.api.model.TParkingLot;
import com.ruoyi.chargingPile.api.model.*;
import com.ruoyi.chargingPile.api.query.GetSiteList;
import com.ruoyi.chargingPile.api.query.SiteDetailQuery;
import com.ruoyi.chargingPile.api.query.SiteQuery;
import com.ruoyi.chargingPile.api.vo.ChargingGunCountVO;
import com.ruoyi.chargingPile.api.vo.SiteDetailVO;
import com.ruoyi.chargingPile.api.vo.SiteVO;
import com.ruoyi.chargingPile.api.vo.TChargingPileVO;
import com.ruoyi.chargingPile.api.vo.*;
import com.ruoyi.chargingPile.domain.SiteMenu;
import com.ruoyi.chargingPile.service.IPartnerService;
import com.ruoyi.chargingPile.service.ISiteService;
import com.ruoyi.chargingPile.service.TChargingGunService;
import com.ruoyi.chargingPile.service.TChargingPileService;
import com.ruoyi.chargingPile.service.*;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.core.utils.GeodesyUtil;
import com.ruoyi.common.core.web.domain.AjaxResult;
@@ -38,6 +30,8 @@
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import java.math.BigDecimal;
import java.time.LocalTime;
import java.util.*;
/**
@@ -69,29 +63,68 @@
    @Resource
    private TChargingGunService chargingGunService;
    @Autowired
    private TParkingLotService parkingLotService;
    @Autowired
    private TAccountingStrategyService accountingStrategyService;
    @Autowired
    private TAccountingStrategyDetailService accountingStrategyDetailService;
    
    
    /**
     * 小程序扫一扫
     * @param number
     * @return
     */
    @PostMapping("/getSiteInfoByNumber")
    @ApiOperation(value = "扫一扫后通过桩编号获取电站信息", tags = {"小程序-扫一扫"})
    public R<SiteInfoVO> getSiteInfoByNumber(@RequestParam("number") String number){
        SiteInfoVO siteInfoVO = new SiteInfoVO();
        TChargingPile one = chargingPileService.lambdaQuery().eq(TChargingPile::getNumber, number).one();
        Site byId = siteService.getById(one.getSiteId());
        TAccountingStrategy byId1 = accountingStrategyService.getById(byId.getAccountingStrategyId());
        List<TAccountingStrategyDetail> list = accountingStrategyDetailService.lambdaQuery().eq(TAccountingStrategyDetail::getAccountingStrategyId, byId1.getId()).list();
        for (TAccountingStrategyDetail tAccountingStrategyDetail : list) {
            // 当前时间属于那个阶段 取哪个阶段的电价
            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.setName(byId.getName());
        siteInfoVO.setNumber(one.getNumber().toString());
        siteInfoVO.setSpaceChargeExplain(byId.getSpaceChargeExplain());
        return R.ok(siteInfoVO);
    }
    
    @ResponseBody
    @GetMapping("/getSiteList")
    @ApiOperation(value = "获取站点列表", tags = {"管理后台-站点管理"})
    public AjaxResult<PageInfo<GetSiteListDTO>> getSiteList(GetSiteList siteList){
        PageInfo<GetSiteListDTO> list = siteService.getSiteList(siteList);
        return AjaxResult.success(list);
    }
    @ResponseBody
    @GetMapping("/getSiteList1")
    @ApiOperation(value = "获取站点列表 不分页", tags = {"管理后台-活动费用统计"})
    public AjaxResult<List<Site>> getSiteList1(){
        return AjaxResult.success(siteService.list(new QueryWrapper<>()));
    }
    @ApiOperation(value = "获取站点分页列表", tags = {"小程序-站点管理"})
    @PostMapping("/getSiteListGun")
    @ApiOperation(value = "获取站点列表 不分页", tags = {"管理后台-接口信息使用"})
    public AjaxResult<List<Site>> getSiteListGun(){
        return AjaxResult.success(siteService.getSiteListGun());
    }
    @PostMapping("/getSiteListParkLot")
    @ApiOperation(value = "获取站点列表不分页", tags = {"管理后台-停车场绑定"})
    public AjaxResult<List<Site>> getSiteListParkLot(){
        return AjaxResult.success(siteService.getSiteListParkLot());
    }
    @ApiOperation(value = "获取站点分页列表", tags = {"小程序-站点管理-首页"})
    @PostMapping("/pageList")
    public AjaxResult<PageInfo<SiteVO>> pageList(@Validated @RequestBody SiteQuery query){
        return AjaxResult.success(siteService.pageList(query));
    }
    @ApiOperation(value = "获取站点详细信息", tags = {"小程序-站点管理-站点详情"})
    @PostMapping("/getDetailById")
    public AjaxResult<SiteDetailVO> getDetailById(@Validated @RequestBody SiteDetailQuery query){
@@ -100,7 +133,8 @@
        Map<String, Double> distance = GeodesyUtil.getDistance(query.getLat() + "," + query.getLon(), siteDetailVO.getLat() + "," + siteDetailVO.getLon());
        siteDetailVO.setDistance(distance.get("WGS84"));
        // 查询绑定车牌提示文案
        TParkingLot parkingLot = parkingLotClient.getLotBySiteId(query.getSiteId()).getData();
        TParkingLot parkingLot = parkingLotService.getOne(Wrappers.lambdaQuery(TParkingLot.class)
                .eq(TParkingLot::getSiteId, query.getSiteId()));
        if(Objects.nonNull(parkingLot)){
            siteDetailVO.setRemark(parkingLot.getRemark());
        }
@@ -136,12 +170,12 @@
    }
    
    @ResponseBody
    @DeleteMapping("/delSite/{id}")
    @DeleteMapping("/delSite")
    @ApiOperation(value = "删除站点", tags = {"管理后台-站点管理"})
    @ApiImplicitParams({
            @ApiImplicitParam(value = "站点id", name = "id", dataTypeClass = Integer.class, required = true)
    })
    public AjaxResult delSite(@PathVariable Integer[] id){
    public AjaxResult delSite(@RequestParam("id") Integer[] id){
        return siteService.delSite(id);
    }
    
ruoyi-service/ruoyi-chargingPile/src/main/java/com/ruoyi/chargingPile/controller/TAccountingStrategyController.java
@@ -1,18 +1,19 @@
package com.ruoyi.chargingPile.controller;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.chargingPile.api.dto.TAccountingStrategyDTO;
import com.ruoyi.chargingPile.api.model.TAccountingStrategy;
import com.ruoyi.chargingPile.api.model.TAccountingStrategyDetail;
import com.ruoyi.chargingPile.api.model.TCarport;
import com.ruoyi.chargingPile.api.model.TVehicleRamp;
import com.ruoyi.chargingPile.api.model.*;
import com.ruoyi.chargingPile.api.query.TAccountingStrategyQuery;
import com.ruoyi.chargingPile.api.vo.TAccountingStrategyDetailVO;
import com.ruoyi.chargingPile.api.vo.TAccountingStrategyVO;
import com.ruoyi.chargingPile.service.ISiteService;
import com.ruoyi.chargingPile.service.TAccountingStrategyDetailService;
import com.ruoyi.chargingPile.service.TAccountingStrategyService;
import com.ruoyi.chargingPile.service.TChargingPileService;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.core.web.domain.AjaxResult;
import com.ruoyi.common.core.web.page.PageInfo;
import com.ruoyi.common.security.service.TokenService;
@@ -25,6 +26,10 @@
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.math.RoundingMode;
import java.time.LocalTime;
import java.util.List;
/**
@@ -42,6 +47,10 @@
    private final TAccountingStrategyService accountingStrategyService;
    private final TAccountingStrategyDetailService accountingStrategyDetailService;
    @Autowired
    private TChargingPileService chargingPileService;
    @Autowired
    private ISiteService siteService;
    @Resource
    private TokenService tokenService;
@@ -173,7 +182,69 @@
        return AjaxResult.ok(accountingStrategyService.pageList(query));
    }
    /**
     * 小程序远程调用 根据会员折扣、预付金额 计算服务费
     * @return
     */
    @PostMapping(value = "/getServiceMoney")
    public R<BigDecimal> getServiceMoney(@RequestParam("param") String param){
        // 桩编号 + 是否是会员 + 内部会员折扣金额 + 普通会员最高折扣金额 + 预付金额
        String[] split = param.split("-");
        // 根据桩编号 查询电站
        TChargingPile one = chargingPileService.lambdaQuery().eq(TChargingPile::getNumber, split[0]).one();
        Site byId = siteService.getById(one.getSiteId());
        // 计费策略
        TAccountingStrategy one1 = accountingStrategyService.lambdaQuery().eq(TAccountingStrategy::getSiteId, byId.getId())
                .eq(TAccountingStrategy::getAuditStatus, 3).one();
        // 会员折扣
        BigDecimal discount = one1.getDiscount();
        List<TAccountingStrategyDetail> list = accountingStrategyDetailService.lambdaQuery().eq(TAccountingStrategyDetail::getAccountingStrategyId, one1.getId())
                .list();
        // 当前时间属于哪个计费策略
        LocalTime now = LocalTime.now();
        // 电价
        BigDecimal electronic = new BigDecimal(BigInteger.ZERO);
        // 最终服务费
        BigDecimal serviceMoney = new BigDecimal(BigInteger.ZERO);
        // 预付金额
        BigDecimal beforeMoney = new BigDecimal(split[4]);
        // 普通会员最高折扣金额
        BigDecimal discountMoney = new BigDecimal(split[3]);
        // 内部会员折扣
        BigDecimal discountInner = new BigDecimal(split[2]);
        // 最终服务费
        BigDecimal res = new BigDecimal(BigInteger.ZERO);
        for (TAccountingStrategyDetail tAccountingStrategyDetail : list) {
            if (now.isAfter(LocalTime.parse(tAccountingStrategyDetail.getStartTime())) && now.isBefore(LocalTime.parse(tAccountingStrategyDetail.getEndTime()))){
                electronic = tAccountingStrategyDetail.getElectrovalence();
                // 非会员下的服务费
                BigDecimal multiply = beforeMoney.multiply(tAccountingStrategyDetail.getServiceCharge()).divide(electronic, 2, RoundingMode.HALF_UP);
                if (byId.getBusinessCategory() == 1){
                    // 直营才享受会员折扣
                    // 根据电价和预付金额 是否有会员折扣 计算最终服务费
                    if (split[1].equals("1")){
                        // 抵扣金额
                        BigDecimal multiply1 = multiply.multiply(discount);
                        if (multiply1.compareTo(discountMoney)>0){
                            multiply1 = discountMoney;
                        }
                        // 普通最终服务费
                        res = multiply.subtract(multiply1);
                    }
                    if (split[1].equals("2")){
                        // 内部会员折扣 最终服务费
                        res = discountInner.multiply(multiply);
                    }
                }else{
                    // 非直营
                    res = multiply;
                }
                break;
            }
        }
        return R.ok(res);
    }
}
ruoyi-service/ruoyi-chargingPile/src/main/java/com/ruoyi/chargingPile/controller/TChargingGunController.java
@@ -3,9 +3,12 @@
import com.ruoyi.chargingPile.api.dto.TChargingGunDTO;
import com.ruoyi.chargingPile.api.model.TChargingGun;
import com.ruoyi.chargingPile.api.model.TChargingPile;
import com.ruoyi.chargingPile.api.query.TChargingGunQuery;
import com.ruoyi.chargingPile.api.vo.TChargingGunVO;
import com.ruoyi.chargingPile.service.TChargingGunService;
import com.ruoyi.chargingPile.service.TChargingPileService;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.core.web.domain.AjaxResult;
import com.ruoyi.common.core.web.page.PageInfo;
import com.ruoyi.common.log.annotation.Log;
@@ -16,6 +19,7 @@
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.util.List;
@@ -33,6 +37,9 @@
public class TChargingGunController {
    private final TChargingGunService chargingGunService;
    @Resource
    private TChargingPileService chargingPileService;
    @Autowired
    public TChargingGunController(TChargingGunService chargingGunService) {
@@ -85,8 +92,7 @@
    @DeleteMapping(value = "/deleteById")
    public AjaxResult<Boolean> deleteById(@RequestParam("id") Integer id) {
        TChargingGun chargingGun = chargingGunService.getById(id);
        chargingGun.setDelFlag(true);
        chargingGunService.updateById(chargingGun);
        chargingGunService.removeById(chargingGun);
        return AjaxResult.success();
    }
@@ -118,5 +124,32 @@
    public void downloadQRCode(@PathVariable Integer id, HttpServletResponse response){
        // todo 待完善
    }
    /**
     * 根据id获取充电枪完整名称
     * @param id
     * @return
     */
    @ResponseBody
    @PostMapping(value = "/getAllName/{id}")
    public R<String> getAllName(@PathVariable Integer id){
        TChargingGun chargingGun = chargingGunService.getById(id);
        TChargingPile chargingPile = chargingPileService.getById(chargingGun.getChargingPileId());
        return R.ok(chargingPile.getName() + chargingGun.getName());
    }
    /**
     * 根据id获取充电枪详情
     * @param id
     * @return
     */
    @ResponseBody
    @PostMapping(value = "/getChargingGunById/{id}")
    public R<TChargingGun> getChargingGunById(@PathVariable Integer id){
        TChargingGun chargingGun = chargingGunService.getById(id);
        return R.ok(chargingGun);
    }
}
ruoyi-service/ruoyi-chargingPile/src/main/java/com/ruoyi/chargingPile/controller/TChargingPileController.java
@@ -6,6 +6,9 @@
import com.ruoyi.chargingPile.api.query.TChargingGunQuery;
import com.ruoyi.chargingPile.api.vo.TChargingGunVO;
import com.ruoyi.chargingPile.dto.ChargeMonitoring;
import com.ruoyi.chargingPile.dto.ChargingGunCountMonitoring;
import com.ruoyi.chargingPile.dto.ChargingGunMonitoring;
import com.ruoyi.chargingPile.dto.GetChargingGunMonitoring;
import com.ruoyi.chargingPile.service.TChargingGunService;
import com.ruoyi.chargingPile.service.TChargingPileService;
import com.ruoyi.common.core.domain.R;
@@ -77,7 +80,6 @@
    }
    
    
    @ResponseBody
    @GetMapping("/pageChargingPileList")
    @ApiOperation(value = "获取充电桩列表数据", tags = {"管理后台-充电桩信息"})
@@ -115,13 +117,13 @@
    
    
    @ResponseBody
    @DeleteMapping("/delChargingPile/{id}")
    @DeleteMapping("/delChargingPile")
    @Log(title = "删除充电桩", businessType = BusinessType.DELETE,operatorType = OperatorType.MANAGE)
    @ApiOperation(value = "删除充电桩", tags = {"管理后台-充电桩信息"})
    @ApiImplicitParams({
            @ApiImplicitParam(value = "充电桩id", name = "id", dataTypeClass = Integer.class, required = true)
    })
    public AjaxResult delChargingPile(@PathVariable Integer[] id){
    public AjaxResult delChargingPile(@RequestParam("id") Integer[] id){
        return chargingPileService.delChargingPile(id);
    }
    
@@ -136,11 +138,29 @@
    }
    
    
    @ResponseBody
    @GetMapping("/chargeMonitoring/{siteId}")
    @ApiOperation(value = "修改备注", tags = {"管理后台-充电设备监控"})
    @ApiOperation(value = "获取充电设施监控数据", tags = {"管理后台-充电设备监控"})
    public AjaxResult<ChargeMonitoring> chargeMonitoring(@PathVariable Integer siteId){
        ChargeMonitoring chargeMonitoring = chargingPileService.chargeMonitoring(siteId);
        return AjaxResult.success(chargeMonitoring);
    }
    
    @ResponseBody
    @GetMapping("/getChargingGunCountMonitoring/{siteId}")
    @ApiOperation(value = "获取充电枪各种状态汇总监控数据", tags = {"管理后台-充电设备监控"})
    public AjaxResult<ChargingGunCountMonitoring> getChargingGunCountMonitoring(@PathVariable Integer siteId){
        ChargingGunCountMonitoring chargingGunCountMonitoring = chargingPileService.getChargingGunCountMonitoring(siteId);
        return AjaxResult.success(chargingGunCountMonitoring);
    }
    @ResponseBody
    @GetMapping("/getChargingGunMonitoring")
    @ApiOperation(value = "获取充电枪监控数据", tags = {"管理后台-充电设备监控"})
    public AjaxResult<PageInfo<ChargingGunMonitoring>> getChargingGunMonitoring(GetChargingGunMonitoring query){
        PageInfo<ChargingGunMonitoring> chargingGunMonitoring = chargingPileService.getChargingGunMonitoring(query);
        return AjaxResult.success(chargingGunMonitoring);
    }
}
ruoyi-service/ruoyi-chargingPile/src/main/java/com/ruoyi/chargingPile/controller/TChargingPileNotificationController.java
@@ -39,9 +39,9 @@
    
    
    @ResponseBody
    @DeleteMapping("/delChargingPileNotification/{id}")
    @DeleteMapping("/delChargingPileNotification")
    @ApiOperation(value = "删除系统通知", tags = {"管理后台-系统通知"})
    public AjaxResult delChargingPileNotification(@PathVariable Long id){
    public AjaxResult delChargingPileNotification(@RequestParam("id") Long id){
        TChargingPileNotification chargingPileNotification = chargingPileNotificationService.getById(id);
        chargingPileNotification.setDelFlag(1);
        chargingPileNotificationService.updateById(chargingPileNotification);
ruoyi-service/ruoyi-chargingPile/src/main/java/com/ruoyi/chargingPile/controller/TParkingLotController.java
@@ -130,7 +130,7 @@
    /**
     * 通过站点id查询停车场信息
     */
    @GetMapping(value = "/getLotBySiteId")
    @PostMapping(value = "/getLotBySiteId")
    public R<TParkingLot> getLotBySiteId(@RequestParam("siteId") Integer siteId){
        return R.ok(parkingLotService.getOne(Wrappers.lambdaQuery(TParkingLot.class)
                .eq(TParkingLot::getSiteId, siteId)));
ruoyi-service/ruoyi-chargingPile/src/main/java/com/ruoyi/chargingPile/dto/ChargingGunCountMonitoring.java
New file
@@ -0,0 +1,25 @@
package com.ruoyi.chargingPile.dto;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
 * @author zhibing.pu
 * @Date 2024/8/15 16:36
 */
@Data
@ApiModel
public class ChargingGunCountMonitoring {
    @ApiModelProperty("空闲中")
    private Long leisure;
    @ApiModelProperty("已插枪")
    private Long loaded;
    @ApiModelProperty("充电中")
    private Long charging;
    @ApiModelProperty("离网")
    private Long offline;
    @ApiModelProperty("故障")
    private Long breakdown;
}
ruoyi-service/ruoyi-chargingPile/src/main/java/com/ruoyi/chargingPile/dto/ChargingGunMonitoring.java
New file
@@ -0,0 +1,38 @@
package com.ruoyi.chargingPile.dto;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.math.BigDecimal;
/**
 * @author zhibing.pu
 * @Date 2024/8/15 13:58
 */
@Data
@ApiModel
public class ChargingGunMonitoring {
    @ApiModelProperty("充电枪id")
    private Integer id;
    @ApiModelProperty("充电枪状态(1=离线,2=空闲,3=已插枪,4=占用(充电中),7=故障)")
    private Integer status;
    @ApiModelProperty("名称")
    private String name;
    @ApiModelProperty("充电比率")
    private BigDecimal chargingRatio;
    @ApiModelProperty("车位")
    private String parkingNumber;
    @ApiModelProperty("车牌号")
    private String licensePlate;
    @ApiModelProperty("电量")
    private String electricQuantity;
    @ApiModelProperty("当前SOC值")
    private String soc;
    @ApiModelProperty("实时A相电流")
    private String electricCurrent;
    @ApiModelProperty("实时A相电压")
    private String voltage;
    @ApiModelProperty("故障原因")
    private String faultCause;
}
ruoyi-service/ruoyi-chargingPile/src/main/java/com/ruoyi/chargingPile/dto/GetChargingGunMonitoring.java
New file
@@ -0,0 +1,28 @@
package com.ruoyi.chargingPile.dto;
import com.ruoyi.common.core.web.page.BasePage;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.util.List;
/**
 * @author zhibing.pu
 * @Date 2024/8/15 13:48
 */
@Data
@ApiModel
public class GetChargingGunMonitoring extends BasePage {
    @ApiModelProperty("站带你id")
    private Integer siteId;
    @ApiModelProperty("充电枪状态(1=离线,2=空闲,3=已插枪,4=占用(充电中),7=故障)")
    private List<Integer> status;
    @ApiModelProperty("soc开始值")
    private Double socStart;
    @ApiModelProperty("soc结束值")
    private Double socEnd;
    @ApiModelProperty("终端名称")
    private String name;
}
ruoyi-service/ruoyi-chargingPile/src/main/java/com/ruoyi/chargingPile/mapper/SiteMapper.java
@@ -41,4 +41,16 @@
     */
    SiteDetailVO getDetailById(@Param("siteId")Integer siteId);
    /**
     * 获取站点列表不分页 管理后台-停车场绑定
     * @return
     */
    List<Site> getSiteListParkLot(@Param("ids")Set<Integer> ids);
    /**
     * 获取站点列表不分页接口信息使用
     * @param ids
     * @return
     */
    List<Site> getSiteListGun(@Param("ids")Set<Integer> ids);
}
ruoyi-service/ruoyi-chargingPile/src/main/java/com/ruoyi/chargingPile/mapper/TChargingPileMapper.java
@@ -5,6 +5,8 @@
import com.ruoyi.chargingPile.api.model.TChargingPile;
import com.ruoyi.chargingPile.api.query.PageChargingPileList;
import com.ruoyi.chargingPile.api.vo.TChargingPileVO;
import com.ruoyi.chargingPile.dto.ChargingGunMonitoring;
import com.ruoyi.chargingPile.dto.GetChargingGunMonitoring;
import com.ruoyi.common.core.web.page.PageInfo;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
@@ -45,4 +47,15 @@
     * @return
     */
    List<TChargingPileVO> getChargingGunList(@Param("siteId")Integer siteId);
    /**
     * 获取充电枪监控数据
     * @param pageInfo
     * @param siteIds
     * @param query
     * @return
     */
    List<ChargingGunMonitoring> getChargingGunMonitoring(PageInfo<ChargingGunMonitoring> pageInfo, @Param("siteIds") Set<Integer> siteIds,
                                                         @Param("query") GetChargingGunMonitoring query);
}
ruoyi-service/ruoyi-chargingPile/src/main/java/com/ruoyi/chargingPile/service/IPartnerService.java
@@ -58,7 +58,7 @@
     * 删除合作商
     * @param id
     */
    AjaxResult delPartner(Integer[] id);
    AjaxResult delPartner(List<Integer> id);
    
    /**
     * 重置密码
ruoyi-service/ruoyi-chargingPile/src/main/java/com/ruoyi/chargingPile/service/ISiteService.java
@@ -13,6 +13,8 @@
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import java.util.List;
/**
 * @author zhibing.pu
 * @Date 2024/8/7 19:54
@@ -73,4 +75,18 @@
     * @return
     */
    SiteDetailVO getDetailById(Integer siteId);
    /**
     * 获取站点列表不分页 管理后台-停车场绑定
     * @return
     */
    List<Site> getSiteListParkLot();
    /**
     * 获取站点列表不分页接口信息使用
     * @return
     */
    List<Site> getSiteListGun();
}
ruoyi-service/ruoyi-chargingPile/src/main/java/com/ruoyi/chargingPile/service/TChargingPileService.java
@@ -7,6 +7,9 @@
import com.ruoyi.chargingPile.api.query.PageChargingPileList;
import com.ruoyi.chargingPile.api.vo.TChargingPileVO;
import com.ruoyi.chargingPile.dto.ChargeMonitoring;
import com.ruoyi.chargingPile.dto.ChargingGunCountMonitoring;
import com.ruoyi.chargingPile.dto.ChargingGunMonitoring;
import com.ruoyi.chargingPile.dto.GetChargingGunMonitoring;
import com.ruoyi.common.core.web.domain.AjaxResult;
import com.ruoyi.common.core.web.page.PageInfo;
import org.springframework.web.bind.annotation.PathVariable;
@@ -87,4 +90,21 @@
     */
    ChargeMonitoring chargeMonitoring(Integer siteId);
    /**
     * 获取充电枪各种状态汇总
     * @param siteId 站点id
     * @return
     */
    ChargingGunCountMonitoring getChargingGunCountMonitoring(Integer siteId);
    /**
     * 获取充电枪监控数据
     * @param query
     * @return
     */
    PageInfo<ChargingGunMonitoring> getChargingGunMonitoring(GetChargingGunMonitoring query);
}
ruoyi-service/ruoyi-chargingPile/src/main/java/com/ruoyi/chargingPile/service/impl/PartnerServiceImpl.java
@@ -165,10 +165,10 @@
        if(StringUtils.isEmpty(partner.getPhoneOne())){
            return AjaxResult.error("合作商电话1不能为空");
        }
        if(StringUtils.isEmpty(partner.getAccount())){
        if(null == partner.getId() && StringUtils.isEmpty(partner.getAccount())){
            return AjaxResult.error("登录账号不能为空");
        }
        if(StringUtils.isEmpty(partner.getPassword())){
        if(null == partner.getId() && StringUtils.isEmpty(partner.getPassword())){
            return AjaxResult.error("登录密码不能为空");
        }
        return AjaxResult.success();
@@ -272,7 +272,7 @@
     */
    @Override
    @GlobalTransactional(rollbackFor = Exception.class)//分布式事务
    public AjaxResult delPartner(Integer[] ids) {
    public AjaxResult delPartner(List<Integer> ids) {
        //查询有无关联数据
        //站点
        long count = siteService.count(new LambdaQueryWrapper<Site>().in(Site::getPartnerId, Arrays.asList(ids)).eq(Site::getDelFlag, 0));
@@ -289,8 +289,7 @@
            if(null == partner){
                return AjaxResult.error("删除失败");
            }
            partner.setDelFlag(true);
            this.updateById(partner);
            this.removeById(partner);
            SysUser user = sysUserClient.queryUserByUserName(partner.getAccount()).getData();
            user.setDelFlag("2");
            sysUserClient.updateUser(user);
ruoyi-service/ruoyi-chargingPile/src/main/java/com/ruoyi/chargingPile/service/impl/SiteServiceImpl.java
@@ -210,8 +210,7 @@
        }
        for (Integer id : ids) {
            Site site = this.getById(id);
            site.setDelFlag(true);
            this.updateById(site);
            this.removeById(site);
        }
        return AjaxResult.success();
    }
@@ -229,6 +228,50 @@
        return this.baseMapper.getDetailById(siteId);
    }
    @Override
    public List<Site> getSiteListParkLot() {
        Set<Integer> ids = null;
        //校验合作商权限
        SysUser sysUser = sysUserClient.getSysUser(SecurityUtils.getUserId()).getData();
        Integer roleType = sysUser.getRoleType();
        Integer objectId = sysUser.getObjectId();
        //合作商
        if(roleType == 2){
            ids = partnerService.authSite(objectId, SiteMenu.SITE_LIST);
        }else{
            //非管理员需要根据角色和用户配置查询允许的站点数据
            if(!SecurityUtils.isAdmin(SecurityUtils.getUserId())){
                List<Integer> data = userSiteClient.getSiteIds(sysUser.getUserId()).getData();
                List<Integer> data1 = roleSiteClient.getSiteIds(sysUser.getRoleId()).getData();
                data.addAll(data1);
                ids = new HashSet<>(data);
            }
        }
        return this.baseMapper.getSiteListParkLot(ids);
    }
    @Override
    public List<Site> getSiteListGun() {
        Set<Integer> ids = null;
        //校验合作商权限
        SysUser sysUser = sysUserClient.getSysUser(SecurityUtils.getUserId()).getData();
        Integer roleType = sysUser.getRoleType();
        Integer objectId = sysUser.getObjectId();
        //合作商
        if(roleType == 2){
            ids = partnerService.authSite(objectId, SiteMenu.SITE_LIST);
        }else{
            //非管理员需要根据角色和用户配置查询允许的站点数据
            if(!SecurityUtils.isAdmin(SecurityUtils.getUserId())){
                List<Integer> data = userSiteClient.getSiteIds(sysUser.getUserId()).getData();
                List<Integer> data1 = roleSiteClient.getSiteIds(sysUser.getRoleId()).getData();
                data.addAll(data1);
                ids = new HashSet<>(data);
            }
        }
        return this.baseMapper.getSiteListGun(ids);
    }
    /**
     * 设置站点计费策略
ruoyi-service/ruoyi-chargingPile/src/main/java/com/ruoyi/chargingPile/service/impl/TChargingPileServiceImpl.java
@@ -3,6 +3,8 @@
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.account.api.feignClient.AppUserCarClient;
import com.ruoyi.account.api.model.TAppUserCar;
import com.ruoyi.chargingPile.api.dto.PageChargingPileListDTO;
import com.ruoyi.chargingPile.api.model.Site;
import com.ruoyi.chargingPile.api.model.TChargingGun;
@@ -13,6 +15,9 @@
import com.ruoyi.chargingPile.api.vo.TChargingPileVO;
import com.ruoyi.chargingPile.domain.SiteMenu;
import com.ruoyi.chargingPile.dto.ChargeMonitoring;
import com.ruoyi.chargingPile.dto.ChargingGunCountMonitoring;
import com.ruoyi.chargingPile.dto.ChargingGunMonitoring;
import com.ruoyi.chargingPile.dto.GetChargingGunMonitoring;
import com.ruoyi.chargingPile.mapper.TChargingPileMapper;
import com.ruoyi.chargingPile.service.IPartnerService;
import com.ruoyi.chargingPile.service.ISiteService;
@@ -23,6 +28,8 @@
import com.ruoyi.common.core.web.page.PageInfo;
import com.ruoyi.common.security.utils.SecurityUtils;
import com.ruoyi.order.api.feignClient.ChargingOrderAccountingStrategyClient;
import com.ruoyi.order.api.feignClient.ChargingOrderClient;
import com.ruoyi.order.api.model.TChargingOrder;
import com.ruoyi.other.api.feignClient.RoleSiteClient;
import com.ruoyi.other.api.feignClient.UserSiteClient;
import com.ruoyi.system.api.domain.SysUser;
@@ -68,6 +75,12 @@
    
    @Resource
    private ChargingOrderAccountingStrategyClient chargingOrderAccountingStrategyClient;
    @Resource
    private ChargingOrderClient chargingOrderClient;
    @Resource
    private AppUserCarClient appUserCarClient;
    
    
    
@@ -209,8 +222,7 @@
        }
        for (Integer id : ids) {
            TChargingPile chargingPile = this.getById(id);
            chargingPile.setDelFlag(true);
            this.updateById(chargingPile);
            this.removeById(chargingPile);
        }
        return AjaxResult.success();
    }
@@ -250,6 +262,7 @@
     */
    @Override
    public ChargeMonitoring chargeMonitoring(Integer siteId) {
        //获取当前登录账户的站点权限数据
        Set<Integer> siteIds = new HashSet<>();
        if(null == siteId){
            SysUser sysUser = sysUserClient.getSysUser(SecurityUtils.getUserId()).getData();
@@ -292,14 +305,127 @@
            calendar.set(Calendar.DAY_OF_YEAR, calendar.get(Calendar.DAY_OF_YEAR) - i);
            dates.add(sdf.format(calendar.getTime()));
        }
        List<List<Map<String, Object>>> value1 = chargingOrderAccountingStrategyClient.getTotalElectricQuantity(6).getData();
        List<List<Map<String, Object>>> value1 = chargingOrderAccountingStrategyClient.getTotalElectricQuantity(6, siteIds).getData();
        Map<String, Object> chargeTrend = new HashMap<>();
        chargeTrend.put("date", dates);
        chargeTrend.put("value", value1);
        chargeMonitoring.setChargeTrend(chargeTrend);
        
//        chargeMonitoring.setUtilizationTrend();
        //每日利用率=当日充电度数/(总桩数量*桩总功率*24小时)
        List<TChargingPile> list = this.list(new LambdaQueryWrapper<TChargingPile>().in(TChargingPile::getSiteId, siteIds).eq(TChargingPile::getDelFlag, 0));
        BigDecimal v = list.stream().map(TChargingPile::getRatedPower).reduce(BigDecimal.ZERO, BigDecimal::add).setScale(4, RoundingMode.HALF_EVEN);
        List<Double> data = chargingOrderAccountingStrategyClient.getDailyChargingDegree(6, siteIds).getData();
        List<Double> value2 = new ArrayList<>();
        for (Double datum : data) {
            Double datum1 = new BigDecimal(datum).divide(new BigDecimal(list.size()).multiply(v).multiply(new BigDecimal(24))).setScale(2, BigDecimal.ROUND_HALF_EVEN).doubleValue();
            value2.add(datum1);
        }
        Map<String, Object> utilizationTrend = new HashMap<>();
        utilizationTrend.put("date", dates);
        utilizationTrend.put("value", value2);
        chargeMonitoring.setUtilizationTrend(utilizationTrend);
        return chargeMonitoring;
    }
    /**
     * 获取充电枪各种状态汇总
     * @param siteId 站点id
     * @return
     */
    @Override
    public ChargingGunCountMonitoring getChargingGunCountMonitoring(Integer siteId) {
        //获取当前登录账户的站点权限数据
        Set<Integer> siteIds = new HashSet<>();
        if(null == siteId){
            SysUser sysUser = sysUserClient.getSysUser(SecurityUtils.getUserId()).getData();
            Integer roleType = sysUser.getRoleType();
            Integer objectId = sysUser.getObjectId();
            if(2 == roleType){
                siteIds = partnerService.authSite(objectId, SiteMenu.SITE_LIST);
            }else{
                //非管理员需要根据角色和用户配置查询允许的站点数据
                if(!SecurityUtils.isAdmin(SecurityUtils.getUserId())){
                    List<Integer> data = userSiteClient.getSiteIds(sysUser.getUserId()).getData();
                    List<Integer> data1 = roleSiteClient.getSiteIds(sysUser.getRoleId()).getData();
                    data.addAll(data1);
                    siteIds = new HashSet<>(data);
                }
            }
        }else{
            siteIds.add(siteId);
        }
        List<TChargingGun> list = chargingGunService.list(new LambdaQueryWrapper<TChargingGun>().in(TChargingGun::getSiteId, siteIds).eq(TChargingGun::getDelFlag, 0));
        //空闲
        long leisure = list.stream().filter(s -> s.getStatus().equals(2)).count();
        //已插枪
        long loaded = list.stream().filter(s -> Arrays.asList(3, 5, 6).contains(s.getStatus())).count();
        //充电中
        long charging = list.stream().filter(s -> s.getStatus().equals(4)).count();
        //离网
        long offline = list.stream().filter(s -> s.getStatus().equals(0)).count();
        //故障
        long breakdown = list.stream().filter(s -> s.getStatus().equals(7)).count();
        ChargingGunCountMonitoring chargingGunCountMonitoring = new ChargingGunCountMonitoring();
        chargingGunCountMonitoring.setLeisure(leisure);
        chargingGunCountMonitoring.setLoaded(loaded);
        chargingGunCountMonitoring.setCharging(charging);
        chargingGunCountMonitoring.setOffline(offline);
        chargingGunCountMonitoring.setBreakdown(breakdown);
        return chargingGunCountMonitoring;
    }
    /**
     * 获取充电枪监控数据
     * @param query
     * @return
     */
    @Override
    public PageInfo<ChargingGunMonitoring> getChargingGunMonitoring(GetChargingGunMonitoring query) {
        //获取当前登录账户的站点权限数据
        Set<Integer> siteIds = new HashSet<>();
        if(null == query.getSiteId()){
            SysUser sysUser = sysUserClient.getSysUser(SecurityUtils.getUserId()).getData();
            Integer roleType = sysUser.getRoleType();
            Integer objectId = sysUser.getObjectId();
            if(2 == roleType){
                siteIds = partnerService.authSite(objectId, SiteMenu.SITE_LIST);
            }else{
                //非管理员需要根据角色和用户配置查询允许的站点数据
                if(!SecurityUtils.isAdmin(SecurityUtils.getUserId())){
                    List<Integer> data = userSiteClient.getSiteIds(sysUser.getUserId()).getData();
                    List<Integer> data1 = roleSiteClient.getSiteIds(sysUser.getRoleId()).getData();
                    data.addAll(data1);
                    siteIds = new HashSet<>(data);
                }
            }
        }else{
            siteIds.add(query.getSiteId());
        }
        // todo 待完善
        if(null != query.getStatus() && 0 < query.getStatus().size()){
            if(query.getStatus().contains(3)){
                query.getStatus().add(6);
                query.getStatus().add(7);
            }
        }
        PageInfo<ChargingGunMonitoring> pageInfo = new PageInfo<>(query.getPageCurr(), query.getPageSize());
        List<ChargingGunMonitoring> chargingGunMonitoring = this.baseMapper.getChargingGunMonitoring(pageInfo, siteIds, query);
        for (ChargingGunMonitoring gunMonitoring : chargingGunMonitoring) {
            Integer status = gunMonitoring.getStatus();
            if(status == 5 || status == 6){
                gunMonitoring.setStatus(3);
            }
            if(status == 4){
                Integer id = gunMonitoring.getId();
                //根据订单数据查询车牌号
                TChargingOrder chargingOrder = chargingOrderClient.getOrderDetailByGunId(id).getData();
                Long appUserCarId = chargingOrder.getAppUserCarId();
                if(null != appUserCarId){
                    TAppUserCar tAppUserCar = appUserCarClient.getCarByIds(Arrays.asList(appUserCarId)).getData().get(0);
                    gunMonitoring.setLicensePlate(tAppUserCar.getLicensePlate());
                }
            }
        }
        return pageInfo.setRecords(chargingGunMonitoring);
    }
}
ruoyi-service/ruoyi-chargingPile/src/main/resources/bootstrap.yml
@@ -44,7 +44,7 @@
  enabled: true
  application-id: ${spring.application.name}
  tx-service-group: seata_tx_group    #此处配置自定义的seata事务分组名称
  enable-auto-data-source-proxy: true    #开启数据库代理
  enable-auto-data-source-proxy: false    #关闭数据库代理
  service:
    vgroup-mapping:
      seata_tx_group: default
ruoyi-service/ruoyi-chargingPile/src/main/resources/mapper/chargingPile/SiteMapper.xml
@@ -178,4 +178,32 @@
        SELECT <include refid="Base_Column_List"></include>
        FROM t_site WHERE id = #{siteId} AND del_flag = ${@com.ruoyi.common.core.enums.DelFlagEnum@NO.getCode()}
    </select>
    <select id="getSiteListParkLot" resultType="com.ruoyi.chargingPile.api.model.Site">
        select ts.id, ts.partner_id, ts.code, ts.`name`, ts.site_type, ts.business_category, ts.status,tpl.id AS parkingLotId
        from t_site ts
        left join t_parking_lot tpl on (ts.id = tpl.site_id)
        <where>
            <if test="null != ids and ids.size()>0">
                AND ts.id IN
                <foreach collection="ids" item="id" index="index" open="(" separator="," close=")">
                    #{id}
                </foreach>
            </if>
            AND ts.del_flag = ${@com.ruoyi.common.core.enums.DelFlagEnum@NO.getCode()}
            AND tpl.id IS NULL
        </where>
    </select>
    <select id="getSiteListGun" resultType="com.ruoyi.chargingPile.api.model.Site">
        select <include refid="Base_Column_List"></include>
        from t_site
        <where>
            <if test="null != ids and ids.size()>0">
                AND id IN
                <foreach collection="ids" item="id" index="index" open="(" separator="," close=")">
                    #{id}
                </foreach>
            </if>
            AND del_flag = ${@com.ruoyi.common.core.enums.DelFlagEnum@NO.getCode()}
        </where>
    </select>
</mapper>
ruoyi-service/ruoyi-chargingPile/src/main/resources/mapper/chargingPile/TChargingPileMapper.xml
@@ -79,4 +79,32 @@
        select id,code, `name`, `number` from t_charging_pile
        where del_flag = ${@com.ruoyi.common.core.enums.DelFlagEnum@NO.getCode()} and site_id = #{siteId}
    </select>
    <select id="getChargingGunMonitoring" resultType="com.ruoyi.chargingPile.dto.ChargingGunMonitoring">
        select
        a.id,
        a.status,
        CONCAT(b.number, a.name) as name,
        a.parking_number as parkingNumber
        from t_charging_gun a
        left join t_charging_pile b on (a.charging_pile_id = b.id)
        where a.del_flag = 0
        <if test="null != siteIds and siteIds.size() > 0">
            and a.site_id in
            <foreach collection="siteIds" item="item" index="index" open="(" separator="," close=")">
                #{item}
            </foreach>
        </if>
        <if test="null != query.status and query.status.size() > 0">
            and a.status in
            <foreach collection="query.status" item="item" index="index" open="(" separator="," close=")">
                #{item}
            </foreach>
        </if>
        <if test="null != query.name and '' != query.name">
            and a.name like CONCAT('%', #{query.name}, '%')
        </if>
    </select>
</mapper>
ruoyi-service/ruoyi-integration/pom.xml
@@ -100,6 +100,38 @@
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <!--rocketmq-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-stream-rocketmq</artifactId>
            <version>2.2.2.RELEASE</version>
            <exclusions>
                <exclusion>
                    <groupId>org.apache.rocketmq</groupId>
                    <artifactId>rocketmq-client</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>org.apache.rocketmq</groupId>
                    <artifactId>rocketmq-acl</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.apache.rocketmq</groupId>
            <artifactId>rocketmq-client</artifactId>
            <version>4.7.1</version>
        </dependency>
        <dependency>
            <groupId>org.apache.rocketmq</groupId>
            <artifactId>rocketmq-acl</artifactId>
            <version>4.7.1</version>
        </dependency>
        <!--spirngboot集成mongodb-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-mongodb</artifactId>
        </dependency>
    </dependencies>
ruoyi-service/ruoyi-integration/src/main/java/com/ruoyi/integration/mongodb/model/AcquisitionBillingMode.java
New file
@@ -0,0 +1,21 @@
package com.ruoyi.integration.mongodb.model;
import lombok.Data;
import lombok.experimental.Accessors;
import org.springframework.data.mongodb.core.mapping.Document;
/**
 * 充电桩计费模型请求
 **/
@Data
@Document(collection = "acquisition_billing_mode") //指定要对应的文档名(表名)
@Accessors(chain = true)
public class AcquisitionBillingMode {
    private String charging_pile_code;// 桩编码
}
ruoyi-service/ruoyi-integration/src/main/java/com/ruoyi/integration/mongodb/model/AcquisitionBillingModeReply.java
New file
@@ -0,0 +1,81 @@
package com.ruoyi.integration.mongodb.model;
import lombok.Data;
import lombok.experimental.Accessors;
import org.springframework.data.mongodb.core.mapping.Document;
import java.math.BigDecimal;
/**
 * 计费模型请求应答
 **/
@Data
@Document(collection = "acquisition_billing_mode_reply") //指定要对应的文档名(表名)
@Accessors(chain = true)
public class AcquisitionBillingModeReply {
    private String charging_pile_code;// 桩编码
    private String billing_model_code;// "计费模型编号
    private BigDecimal sharp_peak_electricity_rate;// 尖费电费费率(精确到五位小数)
    private BigDecimal sharp_peak_service_rate;// 尖服务费费率(精确到五位小数)
    private BigDecimal peak_electricity_rate;// 峰电费费率(精确到五位小数)
    private BigDecimal peak_service_rate;// 峰服务费费率(精确到五位小数)
    private BigDecimal flat_peak_electricity_rate;// 平电费费率(精确到五位小数)
    private BigDecimal flat_peak_service_rate;// 平服务费费率(精确到五位小数)
    private BigDecimal low_peak_electricity_rate;// 谷电费费率(精确到五位小数)
    private BigDecimal low_peak_service_rate;// 谷服务费费率(精确到五位小数)
    private BigDecimal loss_ratio;// 计损比例
    private Integer time1;// 0:00~0:30 时段费率号(0:尖费率,1:峰费率,2:平费率,3:谷费率)
    private Integer time2;// 0:30~1:00 时段费率号(0:尖费率,1:峰费率,2:平费率,3:谷费率)
    private Integer time3;// 0:30~1:00 时段费率号(0:尖费率,1:峰费率,2:平费率,3:谷费率)
    private Integer time4;
    private Integer time5;
    private Integer time6;
    private Integer time7;
    private Integer time8;
    private Integer time9;
    private Integer time10;
    private Integer time11;
    private Integer time12;
    private Integer time13;
    private Integer time14;
    private Integer time15;
    private Integer time16;
    private Integer time17;
    private Integer time18;
    private Integer time19;
    private Integer time20;
    private Integer time21;
    private Integer time22;
    private Integer time23;
    private Integer time24;
    private Integer time25;
    private Integer time26;
    private Integer time27;
    private Integer time28;
    private Integer time29;
    private Integer time30;
    private Integer time31;
    private Integer time32;
    private Integer time33;
    private Integer time34;
    private Integer time35;
    private Integer time36;
    private Integer time37;
    private Integer time38;
    private Integer time39;
    private Integer time40;
    private Integer time41;
    private Integer time42;
    private Integer time43;
    private Integer time44;
    private Integer time45;
    private Integer time46;
    private Integer time47;// 23:00~23:30 时段费率号(0:尖费率,1:峰费率,2:平费率,3:谷费率)
    private Integer time48;// 23:30~0:00 时段费率号(0:尖费率,1:峰费率,2:平费率,3:谷费率)
}
ruoyi-service/ruoyi-integration/src/main/java/com/ruoyi/integration/mongodb/model/BillingModeVerify.java
New file
@@ -0,0 +1,21 @@
package com.ruoyi.integration.mongodb.model;
import lombok.Data;
import lombok.experimental.Accessors;
import org.springframework.data.mongodb.core.mapping.Document;
/**
 * 计费模型验证请求
 **/
@Data
@Document(collection = "billing_mode_verify") //指定要对应的文档名(表名)
@Accessors(chain = true)
public class BillingModeVerify {
    private String charging_pile_code;// 桩编码
    private String billing_model_code;// "计费模型编号,首次连接到平台时置零
}
ruoyi-service/ruoyi-integration/src/main/java/com/ruoyi/integration/mongodb/model/BillingModeVerifyReply.java
New file
@@ -0,0 +1,23 @@
package com.ruoyi.integration.mongodb.model;
import lombok.Data;
import lombok.experimental.Accessors;
import org.springframework.data.mongodb.core.mapping.Document;
/**
 * 计费模型验证请求应答
 **/
@Data
@Document(collection = "billing_mode_verify_reply") //指定要对应的文档名(表名)
@Accessors(chain = true)
public class BillingModeVerifyReply {
    private String charging_pile_code; // 桩编
    private String billing_model_code; // 计费模型编
    private Integer billing_model_result; // 验证结果(0:桩计费模型与平台一致,1:桩计费模型与平台不一致)
}
ruoyi-service/ruoyi-integration/src/main/java/com/ruoyi/integration/mongodb/model/BmsAbort.java
New file
@@ -0,0 +1,27 @@
package com.ruoyi.integration.mongodb.model;
import lombok.Data;
import lombok.experimental.Accessors;
import org.springframework.data.mongodb.core.mapping.Document;
import java.math.BigDecimal;
/**
 * 充电阶段BMS中止
 **/
@Data
@Document(collection = "bms_abort") //指定要对应的文档名(表名)
@Accessors(chain = true)
public class BmsAbort {
    private String transaction_serial_number; // 交易流水号
    private String charging_pile_code; // 桩编码
    private String charging_gun_code; // 抢号
    private Integer bms_stop_charging_reason; // BMS 中止充电原因(1-2 位——所需求的 SOC 目标值,3-4 位——达到总电压的设定值,5-6 位——达到单体电压设定值,7-8 位——充电机主动中止)
    private Integer bms_aborted_charging_fault_cause; // BMS 中止充电故障原因(1-2 位——绝缘故障,3-4 位——输出连接器过温故障,5-6 位——BMS 元件、输出连接器过温,7-8 位——充电连接器故障,9-10 位——电池组温度过高故障,11-12 位——高压继电器故障,13 位-14 位——检测点 2 电压检测故障,15-16 位——其他故障)
    private Integer bms_aborted_charging_error_cause; // BMS 中止充电错误原因(1-2 位——电流过大,3-4 位——电压异常,5-8 位——预留位)
}
ruoyi-service/ruoyi-integration/src/main/java/com/ruoyi/integration/mongodb/model/BmsDemandAndChargerExportation.java
New file
@@ -0,0 +1,36 @@
package com.ruoyi.integration.mongodb.model;
import lombok.Data;
import lombok.experimental.Accessors;
import org.springframework.data.mongodb.core.mapping.Document;
import java.math.BigDecimal;
/**
 * 充电过程BMS需求、充电机输出
 **/
@Data
@Document(collection = "bms_demand_and_charger_exportation") //指定要对应的文档名(表名)
@Accessors(chain = true)
public class BmsDemandAndChargerExportation {
    private String transaction_serial_number; // 交易流水号
    private String charging_pile_code; // 桩编码
    private String charging_gun_code; // 抢号
    private BigDecimal bms_voltage_requirements; // BMS 电压需求
    private BigDecimal bms_current_requirements; // BMS 电流需求
    private Integer bms_charging_mode; // BMS 充电模式(1:恒压充电;2:恒流充电)
    private BigDecimal bms_voltage_measurement_value; // BMS 充电电压测量值
    private BigDecimal bms_current_measurement_value; // BMS 充电电流测量值
    private Integer bms_battery_voltage_and_group_number; // BMS 最高单体动力蓄电池电压及组号(1-12 位:最高单体动力蓄电池电压,数据分辨率:0.01 V/位,0 V 偏移量;数据范围:0~24 V;13-16 位:最高单体动力蓄电池电压所在组号,数据分辨率:1/位,0 偏移量;数据范围:0~15)
    private BigDecimal bms_charged_status; // BMS 当前荷电状态 SOC( %)
    private Integer bms_remaining_charging_time; // BMS 估算剩余充电时间
    private BigDecimal voltage_output_value; // 电桩电压输出值
    private BigDecimal current_output_value; // 电桩电流输出值
    private Integer cumulative_charging_time; // 累计充电时间
}
ruoyi-service/ruoyi-integration/src/main/java/com/ruoyi/integration/mongodb/model/BmsInformation.java
New file
@@ -0,0 +1,36 @@
package com.ruoyi.integration.mongodb.model;
import lombok.Data;
import lombok.experimental.Accessors;
import org.springframework.data.mongodb.core.mapping.Document;
import java.math.BigDecimal;
/**
 * 充电过程BMS信息
 **/
@Data
@Document(collection = "bms_information") //指定要对应的文档名(表名)
@Accessors(chain = true)
public class BmsInformation {
    private String transaction_serial_number; // 交易流水号
    private String charging_pile_code; // 桩编码
    private String charging_gun_code; // 抢号
    private Integer bms_highest_voltage_number; // BMS 最高单体动力蓄电池电压所在编号
    private String bms_maximum_battery_temperature; // BMS 最高动力蓄电池温度
    private Integer maximum_temperature_detection_point_number; // 最高温度检测点编号
    private String minimum_cell_temperature; // 最低动力蓄电池温度
    private Integer lowest_temperature_detection_pointt_number; // 最低动力蓄电池温度检测点编号
    private Integer bms_battery_voltage; // BMS 单体动力蓄电池电压过高 /过低(0:正常,1:过高,10:过低)
    private Integer bms_battery_charging_status; // BMS 整车动力蓄电池荷电状态SOC 过高/过低(0:正常,1:过高,10:过低)
    private Integer bms_charging_overcurrent; // BMS 动力蓄电池充电过电流(0:正常,1:过流,10:不可信状态)
    private Integer bms_high_battery_temperature; // BMS 动力蓄电池温度过高(0:正常,1:过流,10:不可信状态)
    private Integer bms_battery_insulation_status; // BMS 动力蓄电池绝缘状态(0:正常,1:过流,10:不可信状态)
    private Integer bms_output_connector_connection_status; // BMS 动力蓄电池组输出连接器连接状态(0:正常,1:过流,10:不可信状态)
    private Integer charge_prohibition; // 充电禁止(0:禁止,1:允许)
    private Integer reserve; // 预留位
}
ruoyi-service/ruoyi-integration/src/main/java/com/ruoyi/integration/mongodb/model/ChargingHandshake.java
New file
@@ -0,0 +1,39 @@
package com.ruoyi.integration.mongodb.model;
import lombok.Data;
import lombok.experimental.Accessors;
import org.springframework.data.mongodb.core.mapping.Document;
import java.math.BigDecimal;
/**
 * 充电握手
 **/
@Data
@Document(collection = "charging_handshake") //指定要对应的文档名(表名)
@Accessors(chain = true)
public class ChargingHandshake {
    private String transaction_serial_number;// 交易流水号
    private String charging_pile_code;// 桩编码
    private String charging_gun_code;// 抢号
    private String bms_protocol_version;// BMS 通信协议版本号
    private Integer bms_battery_type;// BMS 电池类型(1:铅酸电池;2:氢:电池;3:磷酸铁锂电池;4:锰酸锂电池;5:钴酸锂电池;6:三元材料电池;7:聚合物锂离子电池;8:钛酸锂电池;FFH:其他)
    private BigDecimal bms_battery_capacity;// BMS 整车动力蓄电池系统额定容量
    private BigDecimal bms_total_battery_voltage;// BMS 整车动力蓄电池系统额定总电压
    private String bms_battery_manufacturer;// BMS 电池生产厂商名称
    private String bms_battery_serial_number;// BMS 电池组序号
    private Integer bms_battery_production_year;// BMS 电池组生产日期年
    private Integer bms_battery_production_month;// BMS 电池组生产日期月
    private Integer bms_battery_production_day;// BMS 电池组生产日期日
    private Integer bms_battery_charging_times;// BMS 电池组充电次数
    private Integer bms_battery_property_rights;// BMS 电池组产权标识
    private Integer reserved;// 预留位
    private String vim;// BMS 车辆识别码
    private Integer bms_software_version;// BMS 软件版本号
}
ruoyi-service/ruoyi-integration/src/main/java/com/ruoyi/integration/mongodb/model/ChargingPileReturnsGroundLockData.java
New file
@@ -0,0 +1,23 @@
package com.ruoyi.integration.mongodb.model;
import lombok.Data;
import lombok.experimental.Accessors;
import org.springframework.data.mongodb.core.mapping.Document;
/**
 *  充电桩返回数据(上行)
 **/
@Data
@Document(collection = "charging_pile_returns_ground_lock_data") //指定要对应的文档名(表名)
@Accessors(chain = true)
public class ChargingPileReturnsGroundLockData {
    private String charging_pile_code; //桩编码
    private String charging_gun_code; // 抢号
    private Integer control_state; // 地锁控制返回标志(1:鉴权成功,0:鉴权失败)
    private String reserve; // 预留位
}
ruoyi-service/ruoyi-integration/src/main/java/com/ruoyi/integration/mongodb/model/ChargingPileStartsCharging.java
New file
@@ -0,0 +1,26 @@
package com.ruoyi.integration.mongodb.model;
import lombok.Data;
import lombok.experimental.Accessors;
import org.springframework.data.mongodb.core.mapping.Document;
/**
 * 充电桩主动申请启动充电
 **/
@Data
@Document(collection = "charging_pile_starts_charging") //指定要对应的文档名(表名)
@Accessors(chain = true)
public class ChargingPileStartsCharging {
    private String charging_pile_code; // 桩编码
    private String charging_gun_code; // 抢号
    private Integer start_method; // 启动方式(1:表示通过刷卡启动充电,2:表示通过帐号启动充电(暂不支持),3:表示vin码启动充电)
    private Integer need_password; // 是否需要密码(0:不需要,1:需要)
    private String account; // 账号或者物理卡号
    private String password; // 输入密码
    private String vin; // "VIN 码,启动方式为vin码启动充电时上送,其他方式置零( ASCII码),VIN码需要反序上送
}
ruoyi-service/ruoyi-integration/src/main/java/com/ruoyi/integration/mongodb/model/ClearOfflineCard.java
New file
@@ -0,0 +1,44 @@
package com.ruoyi.integration.mongodb.model;
import lombok.Data;
import lombok.experimental.Accessors;
import org.springframework.data.mongodb.core.mapping.Document;
/**
 * 离线卡数据清除
 **/
@Data
@Document(collection = "clear_offline_card") //指定要对应的文档名(表名)
@Accessors(chain = true)
public class ClearOfflineCard {
    private String charging_pile_code; //桩编码
    private Integer physical_card_number; // 清除离线卡的个数,最大 24 个
    private String physical_card1; // 第 1 个卡物理卡号,离线卡物理卡号
    private String physical_card2;
    private String physical_card3;
    private String physical_card4;
    private String physical_card5;
    private String physical_card6;
    private String physical_card7;
    private String physical_card8;
    private String physical_card9;
    private String physical_card10;
    private String physical_card11;
    private String physical_card12;
    private String physical_card13;
    private String physical_card14;
    private String physical_card15;
    private String physical_card16;
    private String physical_card17;
    private String physical_card18;
    private String physical_card19;
    private String physical_card20;
    private String physical_card21;
    private String physical_card22;
    private String physical_card23;
    private String physical_card24;
}
ruoyi-service/ruoyi-integration/src/main/java/com/ruoyi/integration/mongodb/model/ClearOfflineCardReply.java
New file
@@ -0,0 +1,91 @@
package com.ruoyi.integration.mongodb.model;
import lombok.Data;
import lombok.experimental.Accessors;
import org.springframework.data.mongodb.core.mapping.Document;
/**
 * 离线卡数据清除应答
 **/
@Data
@Document(collection = "clear_offline_card_reply") //指定要对应的文档名(表名)
@Accessors(chain = true)
public class ClearOfflineCardReply {
    private String charging_pile_code; //桩编码
    private String physical_card1; // 第 1 个卡物理卡号,离线卡物理卡号
    private String physical_card2;
    private String physical_card3;
    private String physical_card4;
    private String physical_card5;
    private String physical_card6;
    private String physical_card7;
    private String physical_card8;
    private String physical_card9;
    private String physical_card10;
    private String physical_card11;
    private String physical_card12;
    private String physical_card13;
    private String physical_card14;
    private String physical_card15;
    private String physical_card16;
    private String physical_card17;
    private String physical_card18;
    private String physical_card19;
    private String physical_card20;
    private String physical_card21;
    private String physical_card22;
    private String physical_card23;
    private String physical_card24;
    private Integer clear_result1; // 清除标记(0:清除失败,1:清除成功)
    private Integer clear_result2;
    private Integer clear_result3;
    private Integer clear_result4;
    private Integer clear_result5;
    private Integer clear_result6;
    private Integer clear_result7;
    private Integer clear_result8;
    private Integer clear_result9;
    private Integer clear_result10;
    private Integer clear_result11;
    private Integer clear_result12;
    private Integer clear_result13;
    private Integer clear_result14;
    private Integer clear_result15;
    private Integer clear_result16;
    private Integer clear_result17;
    private Integer clear_result18;
    private Integer clear_result19;
    private Integer clear_result20;
    private Integer clear_result21;
    private Integer clear_result22;
    private Integer clear_result23;
    private Integer clear_result24;
    private Integer failure_cause1; // 失败原因(1:卡号格式错误,2:清除成功)
    private Integer failure_cause2;
    private Integer failure_cause3;
    private Integer failure_cause4;
    private Integer failure_cause5;
    private Integer failure_cause6;
    private Integer failure_cause7;
    private Integer failure_cause8;
    private Integer failure_cause9;
    private Integer failure_cause10;
    private Integer failure_cause11;
    private Integer failure_cause12;
    private Integer failure_cause13;
    private Integer failure_cause14;
    private Integer failure_cause15;
    private Integer failure_cause16;
    private Integer failure_cause17;
    private Integer failure_cause18;
    private Integer failure_cause19;
    private Integer failure_cause20;
    private Integer failure_cause21;
    private Integer failure_cause22;
    private Integer failure_cause23;
    private Integer failure_cause24;
}
ruoyi-service/ruoyi-integration/src/main/java/com/ruoyi/integration/mongodb/model/ConfirmTransactionRecord.java
New file
@@ -0,0 +1,21 @@
package com.ruoyi.integration.mongodb.model;
import lombok.Data;
import lombok.experimental.Accessors;
import org.springframework.data.mongodb.core.mapping.Document;
/**
 * 交易记录确认
 **/
@Data
@Document(collection = "confirm_transaction_record") //指定要对应的文档名(表名)
@Accessors(chain = true)
public class ConfirmTransactionRecord {
    private String transaction_serial_number;// 桩编码
    private Integer confirm_result;// 确认结果(0:上传成功,1:非法账单)
}
ruoyi-service/ruoyi-integration/src/main/java/com/ruoyi/integration/mongodb/model/EndCharge.java
New file
@@ -0,0 +1,33 @@
package com.ruoyi.integration.mongodb.model;
import lombok.Data;
import lombok.experimental.Accessors;
import org.springframework.data.mongodb.core.mapping.Document;
import java.math.BigDecimal;
/**
 * 充电结束
 **/
@Data
@Document(collection = "end_charge") //指定要对应的文档名(表名)
@Accessors(chain = true)
public class EndCharge {
    private String transaction_serial_number; // 交易流水号
    private String charging_pile_code; // 桩编码
    private String charging_gun_code; // 抢号
    private BigDecimal bms_battery_charging_status; // BMS 中止荷电状态 SOC
    private BigDecimal bms_minimum_battery_voltage; // BMS 动力蓄电池单体最低电压
    private BigDecimal bms_maximum_battery_voltage; // BMS 动力蓄电池单体最高电压
    private Integer bms_minimum_battery_temperature; // BMS 动力蓄电池最低温度
    private Integer bms_maximum_battery_temperature; // BMS 动力蓄电池最高温度
    private BigDecimal cumulative_charging_time; // 电桩累计充电时间
    private BigDecimal output_energy; // 电桩输出能量
    private String charger_code; // 电桩充电机编号
}
ruoyi-service/ruoyi-integration/src/main/java/com/ruoyi/integration/mongodb/model/ErrorMessage.java
New file
@@ -0,0 +1,47 @@
package com.ruoyi.integration.mongodb.model;
import lombok.Data;
import lombok.experimental.Accessors;
import org.springframework.data.mongodb.core.mapping.Document;
import java.math.BigDecimal;
/**
 * 错误报文
 **/
@Data
@Document(collection = "error_message") //指定要对应的文档名(表名)
@Accessors(chain = true)
public class ErrorMessage {
    private String transaction_serial_number; // 交易流水号
    private String charging_pile_code; // 桩编码
    private String charging_gun_code; // 抢号
    private Integer charger_identification_0; // 接收 SPN2560=0x00 的充电机辨识报文超时(0=正常,1=超时,10=不可信状态)
    private Integer charger_identification_170; // 接收 SPN2560=0xAA 的充电机辨识报文超时(0=正常,1=超时,10=不可信状态)
    private String charger_identification_reserve; // 预留位
    private Integer time_synchronization_and_maximum_output_charger; // 接收充电机的时间同步和充电机最大输出能力报文超时(0=正常,1=超时,10=不可信状态)
    private Integer charge_preparation; // 接收充电机完成充电准备报文超时(0=正常,1=超时,10=不可信状态)
    private String charge_preparation_reserve; // 预留位
    private Integer charging_status; // 接收充电机充电状态报文超时(0=正常,1=超时,10=不可信状态)
    private Integer stop_charging; // 接收充电机中止充电报文超时(0=正常,1=超时,10=不可信状态)
    private String charge_reserve; // 预留位
    private Integer charging_statistics; // 接收充电机充电统计报文超时(0=正常,1=超时,10=不可信状态)
    private String bms_other; // BMS 其他
    private Integer bms_and_vehicle_identification; // 接收 BMS 和车辆的辨识报文超时(0=正常,1=超时,10=不可信状态)
    private String bms_and_vehicle_identification_reserve; // 预留位
    private Integer battery_charging_parameter; // 接收电池充电参数报文超时(0=正常,1=超时,10=不可信状态)
    private Integer bms_completes_charging_preparation; // 接收 BMS 完成充电准备报文超时(0=正常,1=超时,10=不可信状态)
    private String bms_completes_charging_preparation_reserve; // 预留位
    private Integer total_battery_charging_status; // 接收电池充电总状态报文超时(0=正常,1=超时,10=不可信状态)
    private Integer battery_charging_requirements; // 接收电池充电要求报文超时(0=正常,1=超时,10=不可信状态)
    private Integer bms_stop_charging; // 接收 BMS 中止充电报文超时(0=正常,1=超时,10=不可信状态)
    private Integer bms_reserve; // 预留位
    private Integer bms_charging_statistics; // 接收 BMS 充电统计报文超时0=正常,1=超时,10=不可信状态)
    private String charger_other; // 充电机其他
}
ruoyi-service/ruoyi-integration/src/main/java/com/ruoyi/integration/mongodb/model/GroundLockRealTimeData.java
New file
@@ -0,0 +1,26 @@
package com.ruoyi.integration.mongodb.model;
import lombok.Data;
import lombok.experimental.Accessors;
import org.springframework.data.mongodb.core.mapping.Document;
/**
 * 地锁数据上送(充电桩上送)
 **/
@Data
@Document(collection = "ground_lock_real_time_data") //指定要对应的文档名(表名)
@Accessors(chain = true)
public class GroundLockRealTimeData {
    private String charging_pile_code; //桩编码
    private String charging_gun_code; // 抢号
    private Integer parking_lock_status; // 车位锁状态(0:未到位状态,1:升锁到位状态,2:降锁到位状态)
    private Integer parking_status; // 车位状态(0:无车辆,1:停放车辆)
    private Integer ground_locking_capacity; // 地锁电量状态
    private Integer alarm_status; // 报警状态(0:正常无报警,1:待机状态摇臂破坏,2:摇臂升降异常(未到位))
    private String reserve; // 预留位
}
ruoyi-service/ruoyi-integration/src/main/java/com/ruoyi/integration/mongodb/model/MotorAbort.java
New file
@@ -0,0 +1,25 @@
package com.ruoyi.integration.mongodb.model;
import lombok.Data;
import lombok.experimental.Accessors;
import org.springframework.data.mongodb.core.mapping.Document;
/**
 * 充电阶段充电机中止
 **/
@Data
@Document(collection = "motor_abort") //指定要对应的文档名(表名)
@Accessors(chain = true)
public class MotorAbort {
    private String transaction_serial_number; // 交易流水号
    private String charging_pile_code; // 桩编码
    private String charging_gun_code; // 抢号
    private Integer charger_stops_charging_reason; // 充电机中止充电原因(1-2 位——达到充电机设定的条件中止,3-4 位——人工中止,5-6 位——异常中止)
    private Integer charging_failure_cause; // 充电机中止充电故障原因(1-2 位——充电机过温故障,3-4 位——充电连接器故障,5-6 位——充电机内部过温故障,7-8 位——所需电量不能传送,9-10 位——充电机急停故障,11-12 位——其他故障,13-16 位——预留位)
    private Integer charging_machine_stopped_charging_error_cause; // 充电机中止充电错误原因(1-2 位——电流不匹配,3-4 位——电压异常,5-8 位——预留位)
}
ruoyi-service/ruoyi-integration/src/main/java/com/ruoyi/integration/mongodb/model/Online.java
New file
@@ -0,0 +1,25 @@
package com.ruoyi.integration.mongodb.model;
import lombok.Data;
import lombok.experimental.Accessors;
import org.springframework.data.mongodb.core.mapping.Document;
/**
 * 充电桩登录认证
 **/
@Data
@Document(collection = "online") //指定要对应的文档名(表名)
@Accessors(chain = true)
public class Online {
    private String charging_pile_code;// 充电桩编号
    private Integer charging_pile_type;// 充电桩类型(0 表示直流桩,1 表示交流桩)
    private Integer charging_gun_number;// 充电枪数量
    private String protocol_version;// 通信协议版本
    private String program_version;// 程序版本
    private Integer network_link_type;// 网络链接类型(0:SIM 卡,1:LAN,2:WAN,3:其他)
    private String sim;// Sim 卡
    private Integer operator;// 运营商(0: 移动,2:电信,3:联通,4:其他)
}
ruoyi-service/ruoyi-integration/src/main/java/com/ruoyi/integration/mongodb/model/OnlineReply.java
New file
@@ -0,0 +1,19 @@
package com.ruoyi.integration.mongodb.model;
import lombok.Data;
import lombok.experimental.Accessors;
import org.springframework.data.mongodb.core.mapping.Document;
/**
 * 登录认证应答
 **/
@Data
@Document(collection = "online_reply") //指定要对应的文档名(表名)
@Accessors(chain = true)
public class OnlineReply {
    private String charging_pile_code;// 桩编码
    private Integer online_result;// 登陆结果(0:成功,1:失败)
}
ruoyi-service/ruoyi-integration/src/main/java/com/ruoyi/integration/mongodb/model/OpenOrCloseGroundLock.java
New file
@@ -0,0 +1,23 @@
package com.ruoyi.integration.mongodb.model;
import lombok.Data;
import lombok.experimental.Accessors;
import org.springframework.data.mongodb.core.mapping.Document;
/**
 *  遥控地锁升锁与降锁命令 (下行)
 **/
@Data
@Document(collection = "open_or_close_ground_lock") //指定要对应的文档名(表名)
@Accessors(chain = true)
public class OpenOrCloseGroundLock {
    private String charging_pile_code; //桩编码
    private String charging_gun_code; // 抢号
    private Integer operate; // 升/降地锁(1:升锁,2:降锁)
    private String reserve; // 预留位
}
ruoyi-service/ruoyi-integration/src/main/java/com/ruoyi/integration/mongodb/model/ParameterSetting.java
New file
@@ -0,0 +1,36 @@
package com.ruoyi.integration.mongodb.model;
import lombok.Data;
import lombok.experimental.Accessors;
import org.springframework.data.mongodb.core.mapping.Document;
import java.math.BigDecimal;
/**
 * 参数配置
 **/
@Data
@Document(collection = "parameter_setting") //指定要对应的文档名(表名)
@Accessors(chain = true)
public class ParameterSetting {
    private String transaction_serial_number; // 交易流水号
    private String charging_pile_code; // 桩编码
    private String charging_gun_code; // 抢号
    private BigDecimal bms_maximum_charging_voltage; // BMS 单体动力蓄电池最高允许充电电压
    private BigDecimal bms_maximum_charging_current; // BMS 最高允许充电电流
    private BigDecimal bms_battery_total_energy; // BMS 动力蓄电池标称总能量
    private BigDecimal bms_total_charging_voltage; // BMS 最高允许充电总电压
    private Integer bms_maximum_temperature; // BMS 最高允许温度
    private BigDecimal bms_battery_charging_status; // BMS 整车动力蓄电池荷电状态 (soc)
    private BigDecimal bms_battery_voltage; // BMS 整车动力蓄电池当前电池 电压
    private BigDecimal maximum_output_voltage; // 电桩最高输出电压
    private BigDecimal minimum_output_voltage; // 电桩最低输出电压
    private BigDecimal maximum_output_current; // 电桩最大输出电流
    private BigDecimal minimum_output_current; // 电桩最小输出电流
}
ruoyi-service/ruoyi-integration/src/main/java/com/ruoyi/integration/mongodb/model/Ping.java
New file
@@ -0,0 +1,22 @@
package com.ruoyi.integration.mongodb.model;
import lombok.Data;
import lombok.experimental.Accessors;
import org.springframework.data.mongodb.core.mapping.Document;
/**
 * 充电桩心跳包
 **/
@Data
@Document(collection = "ping") //指定要对应的文档名(表名)
@Accessors(chain = true)
public class Ping {
    private String charging_pile_code;// 桩编码
    private String charging_gun_code;// 抢号
    private Integer charging_gun_status;// 抢状态(0:正常,1:故障)
}
ruoyi-service/ruoyi-integration/src/main/java/com/ruoyi/integration/mongodb/model/PlatformConfirmationCharging.java
New file
@@ -0,0 +1,29 @@
package com.ruoyi.integration.mongodb.model;
import lombok.Data;
import lombok.experimental.Accessors;
import org.springframework.data.mongodb.core.mapping.Document;
import java.math.BigDecimal;
/**
 * 运营平台确认启动充电
 **/
@Data
@Document(collection = "platform_confirmation_charging") //指定要对应的文档名(表名)
@Accessors(chain = true)
public class PlatformConfirmationCharging {
    private String transaction_serial_number; // 交易流水号
    private String charging_pile_code; // 桩编码
    private String charging_gun_code; // 抢号
    private String card_number; // 逻辑卡号
    private BigDecimal account_balance; // 账户余额
    private Integer authentication; // 鉴权成功标志(0:失败,1:成功)
    private Integer failure_cause; // 失败原因(1:账户不存在,2: 账户冻结,3:账户余额不足,4:该卡存在未结账记录,5: 桩停用,6:该账户不能在此桩上充电,7:密码错误,8:电站电容不足,9:系统中 vin 码不存在,10:该桩存在未结账记录,11:该桩不支持刷卡)
}
ruoyi-service/ruoyi-integration/src/main/java/com/ruoyi/integration/mongodb/model/PlatformRemoteUpdate.java
New file
@@ -0,0 +1,29 @@
package com.ruoyi.integration.mongodb.model;
import lombok.Data;
import lombok.experimental.Accessors;
import org.springframework.data.mongodb.core.mapping.Document;
/**
 *  远程更新
 **/
@Data
@Document(collection = "platform_remote_update") //指定要对应的文档名(表名)
@Accessors(chain = true)
public class PlatformRemoteUpdate {
    private String charging_pile_code; //桩编码
    private Integer charging_pile_type; // 桩型号(1:直流,2:交流)
    private Integer charging_pile_power; // 桩功率
    private String upgrade_server_address; // 升级服务器地址
    private Integer upgrade_server_port; // 升级服务器端口
    private String username; // 用户名
    private String password; // 密码
    private String file_path; // 文件路径
    private Integer operate; // 执行控制(1:立即执行,2:空闲执行)
    private Integer download_timeout; // 下载超时时间
}
ruoyi-service/ruoyi-integration/src/main/java/com/ruoyi/integration/mongodb/model/PlatformRemoteUpdateReply.java
New file
@@ -0,0 +1,21 @@
package com.ruoyi.integration.mongodb.model;
import lombok.Data;
import lombok.experimental.Accessors;
import org.springframework.data.mongodb.core.mapping.Document;
/**
 *  远程更新应答
 **/
@Data
@Document(collection = "platform_remote_update_reply") //指定要对应的文档名(表名)
@Accessors(chain = true)
public class PlatformRemoteUpdateReply {
    private String charging_pile_code; //桩编码
    private Integer escalation_state; // 升级状态(0:成功,1:编码错误,2:程序与桩型号不符,3:下载更新文件超时)
}
ruoyi-service/ruoyi-integration/src/main/java/com/ruoyi/integration/mongodb/model/PlatformRestart.java
New file
@@ -0,0 +1,21 @@
package com.ruoyi.integration.mongodb.model;
import lombok.Data;
import lombok.experimental.Accessors;
import org.springframework.data.mongodb.core.mapping.Document;
/**
 *  远程重启
 **/
@Data
@Document(collection = "platform_restart") //指定要对应的文档名(表名)
@Accessors(chain = true)
public class PlatformRestart {
    private String charging_pile_code; //桩编码
    private Integer operate; // 执行控制 (1:立即执行,2:空闲执行)
}
ruoyi-service/ruoyi-integration/src/main/java/com/ruoyi/integration/mongodb/model/PlatformRestartReply.java
New file
@@ -0,0 +1,21 @@
package com.ruoyi.integration.mongodb.model;
import lombok.Data;
import lombok.experimental.Accessors;
import org.springframework.data.mongodb.core.mapping.Document;
/**
 *  远程重启应答
 **/
@Data
@Document(collection = "platform_restart_reply") //指定要对应的文档名(表名)
@Accessors(chain = true)
public class PlatformRestartReply {
    private String charging_pile_code; //桩编码
    private Integer set_result; // 设置结果(0:失败,1:成功)
}
ruoyi-service/ruoyi-integration/src/main/java/com/ruoyi/integration/mongodb/model/PlatformStartCharging.java
New file
@@ -0,0 +1,28 @@
package com.ruoyi.integration.mongodb.model;
import lombok.Data;
import lombok.experimental.Accessors;
import org.springframework.data.mongodb.core.mapping.Document;
import java.math.BigDecimal;
/**
 * 运营平台远程控制启机
 **/
@Data
@Document(collection = "platform_start_charging") //指定要对应的文档名(表名)
@Accessors(chain = true)
public class PlatformStartCharging {
    private String transaction_serial_number; // 交易流水号
    private String charging_pile_code; // 桩编码
    private String charging_gun_code; // 抢号
    private String card_number; // 逻辑卡号
    private String physical_card_number; // 物理卡号
    private BigDecimal account_balance; // 账户余额
}
ruoyi-service/ruoyi-integration/src/main/java/com/ruoyi/integration/mongodb/model/PlatformStartChargingReply.java
New file
@@ -0,0 +1,27 @@
package com.ruoyi.integration.mongodb.model;
import lombok.Data;
import lombok.experimental.Accessors;
import org.springframework.data.mongodb.core.mapping.Document;
import java.math.BigDecimal;
/**
 * 远程启机命令回复
 **/
@Data
@Document(collection = "platform_start_charging_reply") //指定要对应的文档名(表名)
@Accessors(chain = true)
public class PlatformStartChargingReply {
    private String transaction_serial_number; // 交易流水号
    private String charging_pile_code; // 桩编码
    private String charging_gun_code; // 抢号
    private Integer startup_result; // 启动结果(0:失败,1:成功)
    private Integer failure_cause; // "失败原因(0:无,1:设备编号不匹配,2:枪已在充电,3:设备故障,4:设备离线,5:未插枪桩在收到启充命令后,检测到未插枪则发送 0x33 报文回复充电失败。若在 60 秒(以收到 0x34 时间开始计算)内检测到枪重新连接,则补送 0x33 成功报文;超时或者离线等其他异常,桩不启充、不补发 0x33 报文)"
}
ruoyi-service/ruoyi-integration/src/main/java/com/ruoyi/integration/mongodb/model/PlatformStopCharging.java
New file
@@ -0,0 +1,22 @@
package com.ruoyi.integration.mongodb.model;
import lombok.Data;
import lombok.experimental.Accessors;
import org.springframework.data.mongodb.core.mapping.Document;
/**
 * 运营平台远程停机
 **/
@Data
@Document(collection = "platform_stop_charging") //指定要对应的文档名(表名)
@Accessors(chain = true)
public class PlatformStopCharging {
    private String charging_pile_code; // 桩编码
    private String charging_gun_code; // 抢号
}
ruoyi-service/ruoyi-integration/src/main/java/com/ruoyi/integration/mongodb/model/PlatformStopChargingReply.java
New file
@@ -0,0 +1,26 @@
package com.ruoyi.integration.mongodb.model;
import lombok.Data;
import lombok.experimental.Accessors;
import org.springframework.data.mongodb.core.mapping.Document;
import java.math.BigDecimal;
/**
 * 远程停机命令回复
 **/
@Data
@Document(collection = "platform_stop_charging_reply") //指定要对应的文档名(表名)
@Accessors(chain = true)
public class PlatformStopChargingReply {
    private String charging_pile_code; // 桩编码
    private String charging_gun_code; // 抢号
    private Integer stop_result; // 停止结果(0:失败,1:成功)
    private Integer failure_cause; // 失败原因(0:无,1:设备编号不匹配,2:枪未处于充电状态,3:其他)
}
ruoyi-service/ruoyi-integration/src/main/java/com/ruoyi/integration/mongodb/model/Pong.java
New file
@@ -0,0 +1,22 @@
package com.ruoyi.integration.mongodb.model;
import lombok.Data;
import lombok.experimental.Accessors;
import org.springframework.data.mongodb.core.mapping.Document;
/**
 * 心跳包应答
 **/
@Data
@Document(collection = "pong") //指定要对应的文档名(表名)
@Accessors(chain = true)
public class Pong {
    private String charging_pile_code;// 桩编码
    private String charging_gun_code;// 抢号
    private Integer charging_gun_status;// 心跳应答(0)
}
ruoyi-service/ruoyi-integration/src/main/java/com/ruoyi/integration/mongodb/model/QueryOfflineCard.java
New file
@@ -0,0 +1,44 @@
package com.ruoyi.integration.mongodb.model;
import lombok.Data;
import lombok.experimental.Accessors;
import org.springframework.data.mongodb.core.mapping.Document;
/**
 * 离线卡数据查询
 **/
@Data
@Document(collection = "query_offline_card") //指定要对应的文档名(表名)
@Accessors(chain = true)
public class QueryOfflineCard {
    private String charging_pile_code; //桩编码
    private Integer physical_card_number; //桩编码
    private String physical_card1; // 第 1 个卡物理卡号,离线卡物理卡号
    private String physical_card2;
    private String physical_card3;
    private String physical_card4;
    private String physical_card5;
    private String physical_card6;
    private String physical_card7;
    private String physical_card8;
    private String physical_card9;
    private String physical_card10;
    private String physical_card11;
    private String physical_card12;
    private String physical_card13;
    private String physical_card14;
    private String physical_card15;
    private String physical_card16;
    private String physical_card17;
    private String physical_card18;
    private String physical_card19;
    private String physical_card20;
    private String physical_card21;
    private String physical_card22;
    private String physical_card23;
    private String physical_card24;
}
ruoyi-service/ruoyi-integration/src/main/java/com/ruoyi/integration/mongodb/model/QueryOfflineCardReply.java
New file
@@ -0,0 +1,67 @@
package com.ruoyi.integration.mongodb.model;
import lombok.Data;
import lombok.experimental.Accessors;
import org.springframework.data.mongodb.core.mapping.Document;
/**
 * 离线卡数据查询应答
 **/
@Data
@Document(collection = "query_offline_card_reply") //指定要对应的文档名(表名)
@Accessors(chain = true)
public class QueryOfflineCardReply {
    private String charging_pile_code; //桩编码
    private String physical_card1; // 第 1 个卡物理卡号,离线卡物理卡号
    private String physical_card2;
    private String physical_card3;
    private String physical_card4;
    private String physical_card5;
    private String physical_card6;
    private String physical_card7;
    private String physical_card8;
    private String physical_card9;
    private String physical_card10;
    private String physical_card11;
    private String physical_card12;
    private String physical_card13;
    private String physical_card14;
    private String physical_card15;
    private String physical_card16;
    private String physical_card17;
    private String physical_card18;
    private String physical_card19;
    private String physical_card20;
    private String physical_card21;
    private String physical_card22;
    private String physical_card23;
    private String physical_card24;
    private Integer query_result1; // 查询结果(0:不存在,1:存在)
    private Integer query_result2;
    private Integer query_result3;
    private Integer query_result4;
    private Integer query_result5;
    private Integer query_result6;
    private Integer query_result7;
    private Integer query_result8;
    private Integer query_result9;
    private Integer query_result10;
    private Integer query_result11;
    private Integer query_result12;
    private Integer query_result13;
    private Integer query_result14;
    private Integer query_result15;
    private Integer query_result16;
    private Integer query_result17;
    private Integer query_result18;
    private Integer query_result19;
    private Integer query_result20;
    private Integer query_result21;
    private Integer query_result22;
    private Integer query_result23;
    private Integer query_result24;
}
ruoyi-service/ruoyi-integration/src/main/java/com/ruoyi/integration/mongodb/model/ReadRealTimeMonitoringData.java
New file
@@ -0,0 +1,21 @@
package com.ruoyi.integration.mongodb.model;
import lombok.Data;
import lombok.experimental.Accessors;
import org.springframework.data.mongodb.core.mapping.Document;
/**
 * 读取实时监测数据
 **/
@Data
@Document(collection = "read_real_time_monitoring_data") //指定要对应的文档名(表名)
@Accessors(chain = true)
public class ReadRealTimeMonitoringData {
    private String charging_pile_code;// 桩编码
    private String charging_gun_code;//  抢号
}
ruoyi-service/ruoyi-integration/src/main/java/com/ruoyi/integration/mongodb/model/SetupBillingModel.java
New file
@@ -0,0 +1,80 @@
package com.ruoyi.integration.mongodb.model;
import lombok.Data;
import lombok.experimental.Accessors;
import org.springframework.data.mongodb.core.mapping.Document;
import java.math.BigDecimal;
/**
 * 计费模型设置
 **/
@Data
@Document(collection = "setup_billing_model") //指定要对应的文档名(表名)
@Accessors(chain = true)
public class SetupBillingModel {
    private String charging_pile_code; //桩编码
    private String billing_model_code; // 计费模型编号
    private BigDecimal sharp_peak_electricity_rate; // 尖费电费费率(精确到五位小数)
    private BigDecimal sharp_peak_service_rate; // 尖服务费费率(精确到五位小数)
    private BigDecimal peak_electricity_rate; // 峰电费费率(精确到五位小数)
    private BigDecimal peak_service_rate; // 峰服务费费率(精确到五位小数)
    private BigDecimal flat_peak_electricity_rate; // 平电费费率(精确到五位小数)
    private BigDecimal flat_peak_service_rate; // 平服务费费率(精确到五位小数)
    private BigDecimal low_peak_electricity_rate; // 谷电费费率(精确到五位小数)
    private BigDecimal low_peak_service_rate; // 谷服务费费率(精确到五位小数)
    private BigDecimal loss_ratio; // 计损比例
    private Integer time1; // 0:00~0:30 时段费率号(0:尖费率,1:峰费率,2:平费率,3:谷费率)
    private Integer time2; // 0:30~1:00 时段费率号(0:尖费率,1:峰费率,2:平费率,3:谷费率)
    private Integer time3;
    private Integer time4;
    private Integer time5;
    private Integer time6;
    private Integer time7;
    private Integer time8;
    private Integer time9;
    private Integer time10;
    private Integer time11;
    private Integer time12;
    private Integer time13;
    private Integer time14;
    private Integer time15;
    private Integer time16;
    private Integer time17;
    private Integer time18;
    private Integer time19;
    private Integer time20;
    private Integer time21;
    private Integer time22;
    private Integer time23;
    private Integer time24;
    private Integer time25;
    private Integer time26;
    private Integer time27;
    private Integer time28;
    private Integer time29;
    private Integer time30;
    private Integer time31;
    private Integer time32;
    private Integer time33;
    private Integer time34;
    private Integer time35;
    private Integer time36;
    private Integer time37;
    private Integer time38;
    private Integer time39;
    private Integer time40;
    private Integer time41;
    private Integer time42;
    private Integer time43;
    private Integer time44;
    private Integer time45;
    private Integer time46;
    private Integer time47;
    private Integer time48;
}
ruoyi-service/ruoyi-integration/src/main/java/com/ruoyi/integration/mongodb/model/SetupBillingModelReply.java
New file
@@ -0,0 +1,21 @@
package com.ruoyi.integration.mongodb.model;
import lombok.Data;
import lombok.experimental.Accessors;
import org.springframework.data.mongodb.core.mapping.Document;
/**
 * 计费模型应答
 **/
@Data
@Document(collection = "setup_billing_model_reply") //指定要对应的文档名(表名)
@Accessors(chain = true)
public class SetupBillingModelReply {
    private String charging_pile_code; //桩编码
    private String set_result; //设置结果(0:失败,1:成功)
}
ruoyi-service/ruoyi-integration/src/main/java/com/ruoyi/integration/mongodb/model/SynchronizeOfflineCard.java
New file
@@ -0,0 +1,36 @@
package com.ruoyi.integration.mongodb.model;
import lombok.Data;
import lombok.experimental.Accessors;
import org.springframework.data.mongodb.core.mapping.Document;
/**
 * 离线卡数据同步
 **/
@Data
@Document(collection = "synchronize_offline_card") //指定要对应的文档名(表名)
@Accessors(chain = true)
public class SynchronizeOfflineCard {
    private String charging_pile_code; //桩编码
    private Integer card_number; // 下发卡个数
    private String card1; // 第 1 个卡逻辑卡号,离线卡逻辑卡号
    private String card2;
    private String card3;
    private String card4;
    private String card5;
    private String card6;
    private String card7;
    private String card8;
    private String card9;
    private String card10;
    private String card11;
    private String card12;
    private String card13;
    private String card14;
    private String card15;
}
ruoyi-service/ruoyi-integration/src/main/java/com/ruoyi/integration/mongodb/model/SynchronizeOfflineCardReply.java
New file
@@ -0,0 +1,24 @@
package com.ruoyi.integration.mongodb.model;
import lombok.Data;
import lombok.experimental.Accessors;
import org.springframework.data.mongodb.core.mapping.Document;
import java.math.BigDecimal;
/**
 * 卡数据同步应答
 **/
@Data
@Document(collection = "synchronize_offline_card_reply") //指定要对应的文档名(表名)
@Accessors(chain = true)
public class SynchronizeOfflineCardReply {
    private String charging_pile_code; //桩编码
    private Integer save_result; // 保存结果(0:失败,1:成功)
    private Integer failure_cause; // 失败原因(1:卡号格式错误,2:储存空间不足)
}
ruoyi-service/ruoyi-integration/src/main/java/com/ruoyi/integration/mongodb/model/TimingSetting.java
New file
@@ -0,0 +1,20 @@
package com.ruoyi.integration.mongodb.model;
import lombok.Data;
import lombok.experimental.Accessors;
import org.springframework.data.mongodb.core.mapping.Document;
/**
 * 对时设置
 **/
@Data
@Document(collection = "timing_setting") //指定要对应的文档名(表名)
@Accessors(chain = true)
public class TimingSetting {
    private String charging_pile_code; //桩编码
    private String current_time; //当前时间
}
ruoyi-service/ruoyi-integration/src/main/java/com/ruoyi/integration/mongodb/model/TimingSettingReply.java
New file
@@ -0,0 +1,20 @@
package com.ruoyi.integration.mongodb.model;
import lombok.Data;
import lombok.experimental.Accessors;
import org.springframework.data.mongodb.core.mapping.Document;
/**
 * 对时设置应答
 **/
@Data
@Document(collection = "timing_setting_reply") //指定要对应的文档名(表名)
@Accessors(chain = true)
public class TimingSettingReply {
    private String charging_pile_code; //桩编码
    private String current_time; //当前时间
}
ruoyi-service/ruoyi-integration/src/main/java/com/ruoyi/integration/mongodb/model/TransactionRecord.java
New file
@@ -0,0 +1,53 @@
package com.ruoyi.integration.mongodb.model;
import lombok.Data;
import lombok.experimental.Accessors;
import org.springframework.data.mongodb.core.mapping.Document;
import java.math.BigDecimal;
/**
 * 交易记录
 **/
@Data
@Document(collection = "transaction_record") //指定要对应的文档名(表名)
@Accessors(chain = true)
public class TransactionRecord {
    private String transaction_serial_number;// 交易流水号
    private String charging_pile_code;// 桩编码
    private String charging_gun_code;// 抢号
    private String start_time;// 开始时间
    private String end_time;// 结束时间
    private BigDecimal sharp_peak_unit_price;// 尖单价
    private BigDecimal sharp_peak_charge;// 尖电量
    private BigDecimal loss_sharp_peak_charge;// 计损尖电量
    private BigDecimal sharp_peak_amount;// 尖金额
    private BigDecimal peak_unit_price;// 峰单价
    private BigDecimal peak_charge;// 峰电量
    private BigDecimal loss_peak_charge;// 计损峰电量
    private BigDecimal peak_amount;// 峰金额
    private BigDecimal flat_peak_unit_price;// 平单价
    private BigDecimal flat_peak_charge;// 平电量
    private BigDecimal loss_flat_peak_charge;// 计损平电量
    private BigDecimal flat_peak_amount;// 平金额
    private BigDecimal low_peak_unit_price;// 谷单价
    private BigDecimal low_peak_charge;// 谷电量
    private BigDecimal loss_low_peak_charge;// 计损谷电量
    private BigDecimal low_peak_amount;// 谷金额
    private BigDecimal total_initial_value;// 电表总起值
    private BigDecimal total_stop_value;// 电表总止值
    private BigDecimal total_electricity;// 总电量
    private BigDecimal loss_total_electricity;// 计损总电量
    private BigDecimal payment_amount;// 消费金额
    private String vin;// 电动汽车唯一标识
    private Integer start_method;// 交易标识(1:app 启动,2:卡启动,4:离线卡启动,5: vin 码启动充电)
    private String trade_date;// 交易日期、时间
    private Integer stop_reason;// 停止原因
    private String physical_card_number;// 物理卡号
}
ruoyi-service/ruoyi-integration/src/main/java/com/ruoyi/integration/mongodb/model/UpdateBalance.java
New file
@@ -0,0 +1,24 @@
package com.ruoyi.integration.mongodb.model;
import lombok.Data;
import lombok.experimental.Accessors;
import org.springframework.data.mongodb.core.mapping.Document;
import java.math.BigDecimal;
/**
 * 远程账户余额更新
 **/
@Data
@Document(collection = "update_balance") //指定要对应的文档名(表名)
@Accessors(chain = true)
public class UpdateBalance {
    private String charging_pile_code; //桩编码
    private String physical_card_number; //物理卡号
    private BigDecimal account_balance; //修改结果(:0:修改成功,1:设备编号错误,2:卡号错误)
}
ruoyi-service/ruoyi-integration/src/main/java/com/ruoyi/integration/mongodb/model/UpdateBalanceReply.java
New file
@@ -0,0 +1,26 @@
package com.ruoyi.integration.mongodb.model;
import lombok.Data;
import lombok.experimental.Accessors;
import org.springframework.data.mongodb.core.mapping.Document;
import java.math.BigDecimal;
/**
 * 余额更新应答
 **/
@Data
@Document(collection = "update_balance_reply") //指定要对应的文档名(表名)
@Accessors(chain = true)
public class UpdateBalanceReply {
    private String charging_pile_code;// 桩编码
    private String charging_gun_code;// 抢号
    private String physical_card_number;// 物理卡号
    private BigDecimal account_balance;// 修改后账户金额
}
ruoyi-service/ruoyi-integration/src/main/java/com/ruoyi/integration/mongodb/model/UploadRealTimeMonitoringData.java
New file
@@ -0,0 +1,40 @@
package com.ruoyi.integration.mongodb.model;
import lombok.Data;
import lombok.experimental.Accessors;
import org.springframework.data.mongodb.core.mapping.Document;
import java.math.BigDecimal;
/**
 * 上传实时监测数据
 **/
@Data
@Document(collection = "upload_real_time_monitoring_data") //指定要对应的文档名(表名)
@Accessors(chain = true)
public class UploadRealTimeMonitoringData {
    private String transaction_serial_number; // 交易流水号
    private String charging_pile_code; // 桩编码
    private String charging_gun_code; // 抢号
    private Integer charging_gun_status; // 状态(0:离线,1:故障,2:空闲,3:充电)
    private Integer homing_status; // 枪是否归位(0:否,1:是,2:未知)
    private Integer insertion_status; // 是否插枪(0:否,1:是)
    private BigDecimal output_voltage; // 输出电压,精确到小数点后一位;待机置零
    private BigDecimal output_current; // 输出电流,精确到小数点后一位;待机置零
    private Integer gun_line_temperature; // 枪线温度,整形,偏移量-50;待机置零
    private String gun_line_code; // 枪线编码,没有置零
    private Integer soc; // SOC待机置零;交流桩置零
    private Integer battery_temperature; // 电池组最高温度,整形,偏移量-50 ºC;待机置零;交流桩置零
    private Integer cumulative_charging_time; // 累计充电时间,单位:min;待机置零
    private Integer time_remaining; // 剩余时间,单位:min;待机置零、交流桩置零
    private BigDecimal charging_degree; // 充电度数,精确到小数点后四位;待机置零
    private BigDecimal loss_of_charging_degree; // 计损充电度数,精确到小数点后四位;待机置零,未设置计损比例时等于充电度数
    private BigDecimal paid_amount; // 已充金额,精确到小数点后四位;待机置零(电费+服务费)*计损充电度数
    private Integer hardware_fault; // 硬件故障(1:急停按钮动作故障;2:无可用整流模块;3:出风口温度过高;4:交流防雷故障;5:交直流模块 DC20 通信中断;6:绝缘检测模块 FC08 通信中断;7:电度表通信中断;8:读卡器通信中断;9:RC10 通信中断;10:风扇调速板故障;11:直流熔断器故障;12:高压接触器故障;13:门打开)
}
ruoyi-service/ruoyi-integration/src/main/java/com/ruoyi/integration/mongodb/model/WorkingParameterSetting.java
New file
@@ -0,0 +1,21 @@
package com.ruoyi.integration.mongodb.model;
import lombok.Data;
import lombok.experimental.Accessors;
import org.springframework.data.mongodb.core.mapping.Document;
/**
 * 充电桩工作参数设置
 **/
@Data
@Document(collection = "working_parameter_setting") //指定要对应的文档名(表名)
@Accessors(chain = true)
public class WorkingParameterSetting {
    private String charging_pile_code; //桩编码
    private Integer allow_work;// 是否允许工作(0:表示允许正常工作,1:表示停止使用,锁定充电桩)
    private Integer maximum_output_power; // 充电桩最大允许输出功率
}
ruoyi-service/ruoyi-integration/src/main/java/com/ruoyi/integration/mongodb/model/WorkingParameterSettingReply.java
New file
@@ -0,0 +1,20 @@
package com.ruoyi.integration.mongodb.model;
import lombok.Data;
import lombok.experimental.Accessors;
import org.springframework.data.mongodb.core.mapping.Document;
/**
 * 充电桩工作参数设置应答
 **/
@Data
@Document(collection = "working_parameter_setting_reply") //指定要对应的文档名(表名)
@Accessors(chain = true)
public class WorkingParameterSettingReply {
    private String charging_pile_code; //桩编码
    private Integer set_result; //设置结果(0:失败,1:成功)
}
ruoyi-service/ruoyi-integration/src/main/java/com/ruoyi/integration/mongodb/service/OnlineReplyService.java
New file
@@ -0,0 +1,8 @@
package com.ruoyi.integration.mongodb.service;
/**
 * 登录认证应答接口
 */
public interface OnlineReplyService {
}
ruoyi-service/ruoyi-integration/src/main/java/com/ruoyi/integration/mongodb/service/OnlineService.java
New file
@@ -0,0 +1,8 @@
package com.ruoyi.integration.mongodb.service;
/**
 * 充电桩登录认证接口
 */
public interface OnlineService {
}
ruoyi-service/ruoyi-integration/src/main/java/com/ruoyi/integration/mongodb/service/impl/OnlineReplyServiceImpl.java
New file
@@ -0,0 +1,14 @@
package com.ruoyi.integration.mongodb.service.impl;
import com.ruoyi.integration.mongodb.service.OnlineReplyService;
import org.springframework.stereotype.Service;
/**
 * 登录认证应答实现类
 **/
@Service
public class OnlineReplyServiceImpl implements OnlineReplyService {
}
ruoyi-service/ruoyi-integration/src/main/java/com/ruoyi/integration/mongodb/service/impl/OnlineServiceImpl.java
New file
@@ -0,0 +1,14 @@
package com.ruoyi.integration.mongodb.service.impl;
import com.ruoyi.integration.mongodb.service.OnlineService;
import org.springframework.stereotype.Service;
/**
 * 充电桩登录认证实现类
 **/
@Service
public class OnlineServiceImpl implements OnlineService {
}
ruoyi-service/ruoyi-integration/src/main/java/com/ruoyi/integration/rocket/base/BaseMessage.java
New file
@@ -0,0 +1,30 @@
package com.ruoyi.integration.rocket.base;
import lombok.Data;
import java.time.LocalDateTime;
/**
 * 消息实体,所有消息都需要继承此类
 */
@Data
public abstract class BaseMessage {
    /**
     * 业务键,用于RocketMQ控制台查看消费情况
     */
    protected String key;
    /**
     * 发送消息来源,用于排查问题
     */
    protected String source = "";
    /**
     * 发送时间
     */
    protected LocalDateTime sendTime = LocalDateTime.now();
    /**
     * 重试次数,用于判断重试次数,超过重试次数发送异常警告
     */
    protected Integer retryTimes = 0;
}
ruoyi-service/ruoyi-integration/src/main/java/com/ruoyi/integration/rocket/configuration/EnvironmentIsolationConfig.java
New file
@@ -0,0 +1,32 @@
package com.ruoyi.integration.rocket.configuration;
import org.apache.rocketmq.spring.support.DefaultRocketMQListenerContainer;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.util.StringUtils;
public class EnvironmentIsolationConfig implements BeanPostProcessor {
    private RocketEnhanceProperties rocketEnhanceProperties;
    public EnvironmentIsolationConfig(RocketEnhanceProperties rocketEnhanceProperties) {
        this.rocketEnhanceProperties = rocketEnhanceProperties;
    }
    /**
     * 在装载Bean之前实现参数修改
     */
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        if (bean instanceof DefaultRocketMQListenerContainer) {
            DefaultRocketMQListenerContainer container = (DefaultRocketMQListenerContainer) bean;
            if (rocketEnhanceProperties.isEnabledIsolation() && StringUtils.hasText(rocketEnhanceProperties.getEnvironment())) {
                container.setTopic(String.join("_", container.getTopic(), rocketEnhanceProperties.getEnvironment()));
            }
            return container;
        }
        return bean;
    }
}
ruoyi-service/ruoyi-integration/src/main/java/com/ruoyi/integration/rocket/configuration/RocketEnhanceProperties.java
New file
@@ -0,0 +1,13 @@
package com.ruoyi.integration.rocket.configuration;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
@ConfigurationProperties(prefix = "rocketmq.enhance")
@Data
public class RocketEnhanceProperties {
    private boolean enabledIsolation;
    private String environment;
}
ruoyi-service/ruoyi-integration/src/main/java/com/ruoyi/integration/rocket/configuration/RocketMQEnhanceAutoConfiguration.java
New file
@@ -0,0 +1,61 @@
package com.ruoyi.integration.rocket.configuration;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.ruoyi.integration.rocket.util.RocketMQEnhanceTemplate;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.messaging.converter.CompositeMessageConverter;
import org.springframework.messaging.converter.MappingJackson2MessageConverter;
import org.springframework.messaging.converter.MessageConverter;
import java.util.List;
@Configuration
@EnableConfigurationProperties(RocketEnhanceProperties.class)
public class RocketMQEnhanceAutoConfiguration {
    /**
     * 注入增强的RocketMQEnhanceTemplate
     */
    @Bean
    public RocketMQEnhanceTemplate rocketMQEnhanceTemplate(RocketMQTemplate rocketMQTemplate){
        return new RocketMQEnhanceTemplate(rocketMQTemplate);
    }
    /**
     * 解决RocketMQ Jackson不支持Java时间类型配置
     * 源码参考:{@link org.apache.rocketmq.spring.autoconfigure.MessageConverterConfiguration}
     */
    @Bean
    @Primary
    public RocketMQMessageConverter enhanceRocketMQMessageConverter(){
        RocketMQMessageConverter converter = new RocketMQMessageConverter();
        CompositeMessageConverter compositeMessageConverter = (CompositeMessageConverter) converter.getMessageConverter();
        List<MessageConverter> messageConverterList = compositeMessageConverter.getConverters();
        for (MessageConverter messageConverter : messageConverterList) {
            if(messageConverter instanceof MappingJackson2MessageConverter){
                MappingJackson2MessageConverter jackson2MessageConverter = (MappingJackson2MessageConverter) messageConverter;
                ObjectMapper objectMapper = jackson2MessageConverter.getObjectMapper();
                objectMapper.registerModules(new JavaTimeModule());
            }
        }
        return converter;
    }
    /**
     * 环境隔离配置
     */
    @Bean
    @ConditionalOnProperty(name="rocketmq.enhance.enabledIsolation", havingValue="true")
    public EnvironmentIsolationConfig environmentSetup(RocketEnhanceProperties rocketEnhanceProperties){
        return new EnvironmentIsolationConfig(rocketEnhanceProperties);
    }
}
ruoyi-service/ruoyi-integration/src/main/java/com/ruoyi/integration/rocket/configuration/RocketMQMessageConverter.java
New file
@@ -0,0 +1,46 @@
package com.ruoyi.integration.rocket.configuration;
import org.springframework.messaging.converter.*;
import org.springframework.util.ClassUtils;
import java.util.ArrayList;
import java.util.List;
public class RocketMQMessageConverter {
    private static final boolean JACKSON_PRESENT;
    private static final boolean FASTJSON_PRESENT;
    static {
        ClassLoader classLoader = RocketMQMessageConverter.class.getClassLoader();
        JACKSON_PRESENT = ClassUtils.isPresent("com.fasterxml.jackson,databind.ObjectMapper", classLoader)
                && ClassUtils.isPresent("com.fasterxml.jackson.core.JsonGenerator", classLoader);
        FASTJSON_PRESENT = ClassUtils.isPresent("com.alibaba.fastjson.JSON", classLoader)
                && ClassUtils.isPresent("com.alibaba.fastjson.support.config.FastJsonConfig", classLoader);
    }
    private final CompositeMessageConverter messageConverter;
    public RocketMQMessageConverter(){
        List<MessageConverter> messageConverters = new ArrayList<>();
        ByteArrayMessageConverter byteArrayMessageConverter = new ByteArrayMessageConverter();
        byteArrayMessageConverter.setContentTypeResolver(null);
        messageConverters.add(byteArrayMessageConverter);
        messageConverters.add(new StringMessageConverter());
        if (JACKSON_PRESENT) {
            messageConverters.add(new MappingJackson2MessageConverter());
        }
        if(FASTJSON_PRESENT) {
            try {
                messageConverters.add(
                        (MessageConverter) ClassUtils.forName(
                                "com.alibaba.fastjson.support.spring.messaging.MappingFastJsonMessageConverter", ClassUtils.getDefaultClassLoader()).newInstance());
            } catch (ClassNotFoundException | IllegalAccessException | InstantiationException ignored) {
            }
        }
        messageConverter = new CompositeMessageConverter(messageConverters);
    }
    public MessageConverter getMessageConverter(){ return messageConverter; }
}
ruoyi-service/ruoyi-integration/src/main/java/com/ruoyi/integration/rocket/constant/EnhanceMessageConstant.java
New file
@@ -0,0 +1,14 @@
package com.ruoyi.integration.rocket.constant;
public class EnhanceMessageConstant {
    /**
     * 延时等级
     */
    public static final int FIVE_SECOND=5000;
    /**
     * 消息前缀
     */
    public static final String RETRY_PREFIX="retry_";
}
ruoyi-service/ruoyi-integration/src/main/java/com/ruoyi/integration/rocket/listener/EnhanceMemberMessageListener.java
New file
@@ -0,0 +1,66 @@
package com.ruoyi.integration.rocket.listener;
import com.ruoyi.integration.rocket.test.MemberMessage;
import com.ruoyi.integration.rocket.util.EnhanceMessageHandler;
import lombok.extern.slf4j.Slf4j;
import org.apache.rocketmq.spring.annotation.RocketMQMessageListener;
import org.apache.rocketmq.spring.core.RocketMQListener;
import org.springframework.stereotype.Component;
@Slf4j
@Component
@RocketMQMessageListener(
        consumerGroup = "enhance_consumer_group",
        topic = "rocket_enhance",
        selectorExpression = "*",
        consumeThreadMax = 5 //默认是64个线程并发消息,配置 consumeThreadMax 参数指定并发消费线程数,避免太大导致资源不够
)
public class EnhanceMemberMessageListener extends EnhanceMessageHandler<MemberMessage> implements RocketMQListener<MemberMessage> {
    @Override
    protected void handleMessage(MemberMessage message) throws Exception {
        // 此时这里才是最终的业务处理,代码只需要处理资源类关闭异常,其他的可以交给父类重试
        System.out.println("业务消息处理:"+message.getUserName());
    }
    @Override
    protected void handleMaxRetriesExceeded(MemberMessage message) {
        // 当超过指定重试次数消息时此处方法会被调用
        // 生产中可以进行回退或其他业务操作
        log.error("消息消费失败,请执行后续处理");
    }
    /**
     * 是否执行重试机制
     */
    @Override
    protected boolean isRetry() {
        return true;
    }
    @Override
    protected boolean throwException() {
        // 是否抛出异常,false搭配retry自行处理异常
        return false;
    }
    /**
     * 若需要处理消息过滤,在父级中进行统一处理,或者在此处实现之后,自行处理
     * @param message 待处理消息
     * @return true: 本次消息被过滤,false:不过滤
     */
    @Override
    protected boolean filter(MemberMessage message) {
        // 此处可做消息过滤
        return false;
    }
    /**
     * 监听消费消息,不需要执行业务处理,委派给父类做基础操作,父类做完基础操作后会调用子类的实际处理类型
     */
    @Override
    public void onMessage(MemberMessage memberMessage) {
        super.dispatchMessage(memberMessage);
    }
}
ruoyi-service/ruoyi-integration/src/main/java/com/ruoyi/integration/rocket/produce/EnhanceProduceController.java
New file
@@ -0,0 +1,45 @@
package com.ruoyi.integration.rocket.produce;
import com.ruoyi.integration.rocket.test.MemberMessage;
import com.ruoyi.integration.rocket.util.RocketMQEnhanceTemplate;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.apache.rocketmq.client.producer.SendResult;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.time.LocalDate;
import java.util.UUID;
@RestController
@RequestMapping("enhance")
@Slf4j
public class EnhanceProduceController {
    //注入增强后的模板,可以自动实现环境隔离,日志记录
    @Setter(onMethod_ = @Autowired)
    private RocketMQEnhanceTemplate rocketMQEnhanceTemplate;
    private static final String topic = "rocket_enhance";
    private static final String tag = "member";
    /**
     * 发送实体消息
     */
    @GetMapping("/member")
    public SendResult member() {
        String key = UUID.randomUUID().toString();
        MemberMessage message = new MemberMessage();
        // 设置业务key
        message.setKey(key);
        // 设置消息来源,便于查询
        message.setSource("MEMBER");
        // 业务消息内容
        message.setUserName("Java日知录");
        message.setBirthday(LocalDate.now());
        return rocketMQEnhanceTemplate.send(topic, tag, message);
    }
}
ruoyi-service/ruoyi-integration/src/main/java/com/ruoyi/integration/rocket/test/MemberMessage.java
New file
@@ -0,0 +1,14 @@
package com.ruoyi.integration.rocket.test;
import com.ruoyi.integration.rocket.base.BaseMessage;
import lombok.Data;
import java.time.LocalDate;
@Data
public class MemberMessage extends BaseMessage {
    private String userName;
    private LocalDate birthday;
}
ruoyi-service/ruoyi-integration/src/main/java/com/ruoyi/integration/rocket/util/EnhanceMessageHandler.java
New file
@@ -0,0 +1,151 @@
package com.ruoyi.integration.rocket.util;
import com.alibaba.fastjson.JSONObject;
import com.ruoyi.integration.rocket.base.BaseMessage;
import com.ruoyi.integration.rocket.constant.EnhanceMessageConstant;
import lombok.extern.slf4j.Slf4j;
import org.apache.rocketmq.client.producer.SendResult;
import org.apache.rocketmq.client.producer.SendStatus;
import org.apache.rocketmq.spring.annotation.RocketMQMessageListener;
import javax.annotation.Resource;
@Slf4j
public abstract class EnhanceMessageHandler<T extends BaseMessage> {
    /**
     * 默认重试次数
     */
    private static final int MAX_RETRY_TIMES = 3;
    /**
     * 延时等级
     */
    private static final int DELAY_LEVEL = EnhanceMessageConstant.FIVE_SECOND;
    @Resource
    private RocketMQEnhanceTemplate rocketMQEnhanceTemplate;
    /**
     * 消息处理
     *
     * @param message 待处理消息
     * @throws Exception 消费异常
     */
    protected abstract void handleMessage(T message) throws Exception;
    /**
     * 超过重试次数消息,需要启用isRetry
     *
     * @param message 待处理消息
     */
    protected abstract void handleMaxRetriesExceeded(T message);
    /**
     * 是否需要根据业务规则过滤消息,去重逻辑可以在此处处理
     * @param message 待处理消息
     * @return true: 本次消息被过滤,false:不过滤
     */
    protected boolean filter(T message) {
        return false;
    }
    /**
     * 是否异常时重复发送
     *
     * @return true: 消息重试,false:不重试
     */
    protected abstract boolean isRetry();
    /**
     * 消费异常时是否抛出异常
     * 返回true,则由rocketmq机制自动重试
     * false:消费异常(如果没有开启重试则消息会被自动ack)
     */
    protected abstract boolean throwException();
    /**
     * 最大重试次数
     *
     * @return 最大重试次数,默认5次
     */
    protected int getMaxRetryTimes() {
        return MAX_RETRY_TIMES;
    }
    /**
     * isRetry开启时,重新入队延迟时间
     * @return -1:立即入队重试
     */
    protected int getDelayLevel() {
        return DELAY_LEVEL;
    }
    /**
     * 使用模板模式构建消息消费框架,可自由扩展或删减
     */
    public void dispatchMessage(T message) {
        // 基础日志记录被父类处理了
        log.info("消费者收到消息[{}]", JSONObject.toJSON(message));
        if (filter(message)) {
            log.info("消息id{}不满足消费条件,已过滤。",message.getKey());
            return;
        }
        // 超过最大重试次数时调用子类方法处理
        if (message.getRetryTimes() > getMaxRetryTimes()) {
            handleMaxRetriesExceeded(message);
            return;
        }
        try {
            long now = System.currentTimeMillis();
            handleMessage(message);
            long costTime = System.currentTimeMillis() - now;
            log.info("消息{}消费成功,耗时[{}ms]", message.getKey(),costTime);
        } catch (Exception e) {
            log.error("消息{}消费异常", message.getKey(),e);
            // 是捕获异常还是抛出,由子类决定
            if (throwException()) {
                //抛出异常,由DefaultMessageListenerConcurrently类处理
                throw new RuntimeException(e);
            }
            //此时如果不开启重试机制,则默认ACK了
            if (isRetry()) {
                handleRetry(message);
            }
        }
    }
    protected void handleRetry(T message) {
        // 获取子类RocketMQMessageListener注解拿到topic和tag
        RocketMQMessageListener annotation = this.getClass().getAnnotation(RocketMQMessageListener.class);
        if (annotation == null) {
            return;
        }
        //重新构建消息体
        String messageSource = message.getSource();
        if(!messageSource.startsWith(EnhanceMessageConstant.RETRY_PREFIX)){
            message.setSource(EnhanceMessageConstant.RETRY_PREFIX + messageSource);
        }
        message.setRetryTimes(message.getRetryTimes() + 1);
        SendResult sendResult;
        try {
            // 如果消息发送不成功,则再次重新发送,如果发送异常则抛出由MQ再次处理(异常时不走延迟消息)
            sendResult = rocketMQEnhanceTemplate.send(annotation.topic(), annotation.selectorExpression(), message, getDelayLevel());
        } catch (Exception ex) {
            // 此处捕获之后,相当于此条消息被消息完成然后重新发送新的消息
            //由生产者直接发送
            throw new RuntimeException(ex);
        }
        // 发送失败的处理就是不进行ACK,由RocketMQ重试
        if (sendResult.getSendStatus() != SendStatus.SEND_OK) {
            throw new RuntimeException("重试消息发送失败");
        }
    }
}
ruoyi-service/ruoyi-integration/src/main/java/com/ruoyi/integration/rocket/util/RocketMQEnhanceTemplate.java
New file
@@ -0,0 +1,82 @@
package com.ruoyi.integration.rocket.util;
import com.alibaba.fastjson.JSONObject;
import com.ruoyi.integration.rocket.base.BaseMessage;
import com.ruoyi.integration.rocket.configuration.RocketEnhanceProperties;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.rocketmq.client.producer.SendResult;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.apache.rocketmq.spring.support.RocketMQHeaders;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.messaging.Message;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.util.StringUtils;
import javax.annotation.Resource;
@Slf4j
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class RocketMQEnhanceTemplate {
    private final RocketMQTemplate template;
    @Resource
    private RocketEnhanceProperties rocketEnhanceProperties;
    public RocketMQTemplate getTemplate() {
        return template;
    }
    /**
     * 根据系统上下文自动构建隔离后的topic
     * 构建目的地
     */
    public String buildDestination(String topic, String tag) {
        topic = reBuildTopic(topic);
        return topic + ":" + tag;
    }
    /**
     * 根据环境重新隔离topic
     * @param topic 原始topic
     */
    private String reBuildTopic(String topic) {
        if(rocketEnhanceProperties.isEnabledIsolation() && StringUtils.hasText(rocketEnhanceProperties.getEnvironment())){
            return topic +"_" + rocketEnhanceProperties.getEnvironment();
        }
        return topic;
    }
    /**
     * 发送同步消息
     */
    public <T extends BaseMessage> SendResult send(String topic, String tag, T message) {
        // 注意分隔符
        return send(buildDestination(topic,tag), message);
    }
    public <T extends BaseMessage> SendResult send(String destination, T message) {
        // 设置业务键,此处根据公共的参数进行处理
        // 更多的其它基础业务处理...
        Message<T> sendMessage = MessageBuilder.withPayload(message).setHeader(RocketMQHeaders.KEYS, message.getKey()).build();
        SendResult sendResult = template.syncSend(destination, sendMessage);
        // 此处为了方便查看给日志转了json,根据选择选择日志记录方式,例如ELK采集
        log.info("[{}]同步消息[{}]发送结果[{}]", destination, JSONObject.toJSON(message), JSONObject.toJSON(sendResult));
        return sendResult;
    }
    /**
     * 发送延迟消息
     */
    public <T extends BaseMessage> SendResult send(String topic, String tag, T message, int delayLevel) {
        return send(buildDestination(topic,tag), message, delayLevel);
    }
    public <T extends BaseMessage> SendResult send(String destination, T message, int delayLevel) {
        Message<T> sendMessage = MessageBuilder.withPayload(message).setHeader(RocketMQHeaders.KEYS, message.getKey()).build();
        SendResult sendResult = template.syncSend(destination, sendMessage, 3000, delayLevel);
        log.info("[{}]延迟等级[{}]消息[{}]发送结果[{}]", destination, delayLevel, JSONObject.toJSON(message), JSONObject.toJSON(sendResult));
        return sendResult;
    }
}
ruoyi-service/ruoyi-integration/src/main/resources/bootstrap.yml
@@ -44,7 +44,7 @@
  enabled: true
  application-id: ${spring.application.name}
  tx-service-group: seata_tx_group    #此处配置自定义的seata事务分组名称
  enable-auto-data-source-proxy: true    #开启数据库代理
  enable-auto-data-source-proxy: false    #关闭数据库代理
  service:
    vgroup-mapping:
      seata_tx_group: default
ruoyi-service/ruoyi-order/pom.xml
@@ -61,6 +61,11 @@
            <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-seata</artifactId>
        </dependency>
        <!-- SpringBoot Actuator -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/controller/TChargingOrderAccountingStrategyController.java
@@ -9,6 +9,7 @@
import javax.annotation.Resource;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
 * <p>
@@ -33,9 +34,22 @@
     * @return
     */
    @ResponseBody
    @GetMapping("/getTotalElectricQuantity/{days}")
    public R<List<List<Map<String, Object>>>> getTotalElectricQuantity(@PathVariable Integer days){
        List<List<Map<String, Object>>> list = chargingOrderAccountingStrategyService.getTotalElectricQuantity(days);
    @PostMapping("/getTotalElectricQuantity/{days}")
    public R<List<List<Map<String, Object>>>> getTotalElectricQuantity(@PathVariable Integer days, @RequestParam("siteIds") Set<Integer> siteIds){
        List<List<Map<String, Object>>> list = chargingOrderAccountingStrategyService.getTotalElectricQuantity(days, siteIds);
        return R.ok(list);
    }
    /**
     * 获取给定天数每天的充电度数
     * @param days 天数
     * @return
     */
    @ResponseBody
    @PostMapping("/getDailyChargingDegree/{days}")
    public R<List<Double>> getDailyChargingDegree(@PathVariable Integer days, @RequestParam("siteIds") Set<Integer> siteIds){
        List<Double> list = chargingOrderAccountingStrategyService.getDailyChargingDegree(days, siteIds);
        return R.ok(list);
    }
}
ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/controller/TChargingOrderController.java
@@ -3,13 +3,24 @@
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.security.service.TokenService;
import com.ruoyi.common.core.web.domain.AjaxResult;
import com.ruoyi.common.core.web.page.BasePage;
import com.ruoyi.common.core.web.page.PageInfo;
import com.ruoyi.order.api.model.TChargingOrder;
import com.ruoyi.order.api.query.TChargingCountQuery;
import com.ruoyi.order.dto.GetMyChargingOrderList;
import com.ruoyi.order.dto.MyChargingOrderInfo;
import com.ruoyi.order.service.TChargingOrderService;
import io.swagger.annotations.Api;
import org.springframework.beans.factory.annotation.Autowired;
import io.swagger.annotations.ApiOperation;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import java.util.List;
import java.util.Map;
import java.util.List;
/**
 * <p>
@@ -26,6 +37,26 @@
    @Resource
    private TChargingOrderService chargingOrderService;
    @Autowired
    private TokenService tokenService;
    /**
     * 查询用户最近一次充电记录使用的车辆
     * @param
     * @return
     */
    @GetMapping(value = "/getCar")
    public R<Long> getCar() {
        List<TChargingOrder> list = chargingOrderService.list(new LambdaQueryWrapper<TChargingOrder>()
                .eq(TChargingOrder::getAppUserId, tokenService.getLoginUserApplet().getUserId())
                .isNotNull(TChargingOrder::getAppUserCarId));
        if (!list.isEmpty()){
            // 最近使用的车辆id
            Long size = list.get(0).getAppUserCarId();
            return R.ok(size);
        }else{
            return R.ok(-1L);
        }
    }
    /**
     * 查询会员在本月有多少次享受了充电折扣
@@ -53,5 +84,36 @@
        return R.ok(chargingOrderService.getById(orderId));
    }
    /**
     * 根据充电枪id获取正在进行中的订单
     * @param chargingGunId 充电枪id
     * @return
     */
    @PostMapping(value = "/getOrderDetailByGunId")
    public R<TChargingOrder> getOrderDetailByGunId(@RequestParam("chargingGunId") Integer chargingGunId){
        TChargingOrder one = chargingOrderService.getOne(new LambdaQueryWrapper<TChargingOrder>().eq(TChargingOrder::getChargingGunId, chargingGunId)
                .eq(TChargingOrder::getDelFlag, 0).eq(TChargingOrder::getStatus, 3));
        return R.ok(one);
    }
    @ResponseBody
    @GetMapping(value = "/getMyChargingOrderList")
    @ApiOperation(value = "获取充电记录列表", tags = {"小程序-充电记录"})
    public AjaxResult<Map<String, Object>> getMyChargingOrderList(GetMyChargingOrderList query){
        Map<String, Object> orderList = chargingOrderService.getMyChargingOrderList(query);
        return AjaxResult.success(orderList);
    }
    @ResponseBody
    @GetMapping(value = "/getMyChargingOrderInfo")
    @ApiOperation(value = "获取充电记订单明细", tags = {"小程序-充电记录"})
    public AjaxResult<MyChargingOrderInfo> getMyChargingOrderInfo(String id){
        MyChargingOrderInfo myChargingOrderInfo = chargingOrderService.getMyChargingOrderInfo(id);
        return AjaxResult.success(myChargingOrderInfo);
    }
}
ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/controller/TExchangeOrderController.java
@@ -6,26 +6,25 @@
import com.ruoyi.chargingPile.api.feignClient.ChargingPileClient;
import com.ruoyi.chargingPile.api.model.TChargingPile;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.core.web.page.PageInfo;
import com.ruoyi.order.api.model.TChargingOrder;
import com.ruoyi.common.core.web.domain.AjaxResult;
import com.ruoyi.order.api.model.TExchangeOrder;
import com.ruoyi.order.api.model.TShoppingOrder;
import com.ruoyi.order.api.query.TActivityStatisticsQuery;
import com.ruoyi.order.api.vo.TActivityStatisticslVO;
import com.ruoyi.order.api.vo.TActivityVO;
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.order.service.impl.TChargingOrderServiceImpl;
import io.swagger.annotations.Api;
import io.swagger.models.auth.In;
import org.springframework.beans.factory.annotation.Autowired;
import io.swagger.annotations.ApiOperation;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
@@ -41,17 +40,14 @@
@RestController
@RequestMapping("/t-exchange-order")
public class TExchangeOrderController {
    @Autowired
    @Resource
    private TShoppingOrderService tShoppingOrderService;
    @Autowired
    @Resource
    private TExchangeOrderService exchangeOrderService;
    @Autowired
    private TChargingOrderService chargingOrderService;
    @Autowired
    private TVipOrderService vipOrderService;
    @Autowired
    @Resource
    private AppUserClient appUserClient;
    @Autowired
    @Resource
    private ChargingPileClient chargingPileClient;
    /**
     * 管理后台 活动费用统计
@@ -148,11 +144,27 @@
        return R.ok(strings);
    }
    /**
     * 管理后台 根据t_app_coupon订单ids查询积分兑换优惠券的订单编号
     * @param goodsIds  订单号-商品类型
     * @return
     */
    @GetMapping("/getMyExchangeOrder")
    @ApiOperation(value = "获取兑换记录列表", tags = {"小程序-兑换记录"})
    public AjaxResult<List<MyExchangeOrderList>> getMyExchangeOrder(GetMyExchangeOrder query){
        List<MyExchangeOrderList> list = exchangeOrderService.getMyExchangeOrder(query);
        return AjaxResult.success(list);
    }
    @GetMapping("/getGoodsExchangeOrder/{id}")
    @ApiOperation(value = "获取兑换订单详情", tags = {"小程序-兑换记录"})
    public AjaxResult<ExchangeOrderGoodsInfo> getGoodsExchangeOrder(@PathVariable String id){
        ExchangeOrderGoodsInfo goodsExchangeOrder = exchangeOrderService.getGoodsExchangeOrder(id);
        return AjaxResult.success(goodsExchangeOrder);
    }
}
ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/controller/TOrderAppealController.java
@@ -4,6 +4,8 @@
import com.ruoyi.common.core.web.domain.AjaxResult;
import com.ruoyi.common.core.web.page.PageInfo;
import com.ruoyi.common.security.service.TokenService;
import com.ruoyi.common.security.utils.SecurityUtils;
import com.ruoyi.order.api.model.TOrderAppeal;
import com.ruoyi.order.api.query.TOrderAppealQuery;
import com.ruoyi.order.api.vo.TOrderAppealVO;
import com.ruoyi.order.service.TOrderAppealService;
@@ -56,5 +58,15 @@
    @ResponseBody
    @GetMapping(value = "/addOrderAppeal")
    @ApiOperation(value = "申诉订单", tags = {"小程序-充电记录(个人中心)"})
    public AjaxResult addOrderAppeal(@RequestBody TOrderAppeal orderAppeal){
        Long appUserId = tokenService.getLoginUser().getUserid();
        orderAppeal.setAppUserId(appUserId);
        orderAppeal.setStatus(1);
        orderAppealService.save(orderAppeal);
        return AjaxResult.success();
    }
}
ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/controller/TOrderEvaluateController.java
@@ -9,11 +9,10 @@
import com.ruoyi.order.api.vo.TOrderEvaluateVO;
import com.ruoyi.order.dto.GetOrderEvaluatePageList;
import com.ruoyi.order.dto.GetOrderEvaluatePageListDTO;
import com.ruoyi.order.dto.OrderEvaluateVo;
import com.ruoyi.order.service.TOrderEvaluateService;
import com.ruoyi.other.api.vo.TEvaluationTagVO;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import io.swagger.annotations.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
@@ -68,8 +67,7 @@
    @ApiOperation(value = "删除充电评价", tags = {"管理后台-充电评价"})
    public AjaxResult delOrderEvaluate(@PathVariable Long id){
        TOrderEvaluate orderEvaluate = orderEvaluateService.getById(id);
        orderEvaluate.setDelFlag(true);
        orderEvaluateService.updateById(orderEvaluate);
        orderEvaluateService.removeById(orderEvaluate);
        return AjaxResult.success();
    }
    
@@ -112,5 +110,27 @@
        return AjaxResult.ok(orderEvaluateService.getSiteDetailEveList(siteId));
    }
    @PostMapping(value = "/addOrderEvaluate")
    @ApiOperation(value = "添加评价记录", tags = {"小程序-充电记录"})
    public AjaxResult addOrderEvaluate(@RequestBody OrderEvaluateVo query){
        return orderEvaluateService.addOrderEvaluate(query);
    }
    @GetMapping(value = "/getOrderEvaluate")
    @ApiOperation(value = "获取订单评价详情", tags = {"小程序-充电记录"})
    @ApiImplicitParams({
            @ApiImplicitParam(value = "订单id", name = "orderId", required = true),
            @ApiImplicitParam(value = "订单类型(1=充电订单,2=购物订单,3=兑换订单,4=会员订单)", name = "orderType", required = true),
    })
    public AjaxResult<OrderEvaluateVo> getOrderEvaluate(@PathVariable Integer orderId, Integer orderType){
        OrderEvaluateVo orderEvaluate = orderEvaluateService.getOrderEvaluate(orderId, orderType);
        return AjaxResult.success(orderEvaluate);
    }
}
ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/dto/ExchangeOrderGoodsInfo.java
New file
@@ -0,0 +1,54 @@
package com.ruoyi.order.dto;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
 * @author zhibing.pu
 * @Date 2024/8/16 14:07
 */
@Data
@ApiModel
public class ExchangeOrderGoodsInfo {
    @ApiModelProperty("订单id")
    private String id;
    @ApiModelProperty("状态(1=待发货,2=待收货,3=已完成,4=已取消)")
    private Integer status;
    @ApiModelProperty("收货人姓名")
    private String consignee;
    @ApiModelProperty("电话")
    private String phone;
    @ApiModelProperty("地址")
    private String address;
    @ApiModelProperty("快递公司")
    private String expressCompany;
    @ApiModelProperty("快递单号")
    private String expressNumber;
    @ApiModelProperty("商品名称")
    private String name;
    @ApiModelProperty("商品图片")
    private String imgUrl;
    @ApiModelProperty("兑换数量")
    private Integer purchaseQuantity;
    @ApiModelProperty("兑换积分单价")
    private Integer unitPoints;
    @ApiModelProperty("订单编号")
    private String code;
    @ApiModelProperty("下单时间")
    private String createTime;
    @ApiModelProperty("兑换总积分")
    private Integer points;
    @ApiModelProperty("备注")
    private String remark;
    @ApiModelProperty("发货时间")
    private String deliveryTime;
    @ApiModelProperty("完成时间")
    private String finishTime;
    @ApiModelProperty("优惠券类型(1=充电优惠券,2=购物优惠券)")
    private Integer couponType;
    @ApiModelProperty("优惠券有效天数")
    private Integer days;
    @ApiModelProperty("有效期截止时间")
    private String endTime;
}
ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/dto/GetMyChargingOrderList.java
New file
@@ -0,0 +1,17 @@
package com.ruoyi.order.dto;
import com.ruoyi.common.core.web.page.BasePage;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
 * @author zhibing.pu
 * @Date 2024/8/15 19:11
 */
@Data
@ApiModel
public class GetMyChargingOrderList extends BasePage {
    @ApiModelProperty("0=全部,1=待评价")
    private Integer type;
}
ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/dto/GetMyExchangeOrder.java
New file
@@ -0,0 +1,17 @@
package com.ruoyi.order.dto;
import com.ruoyi.common.core.web.page.BasePage;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
 * @author zhibing.pu
 * @Date 2024/8/16 13:34
 */
@Data
@ApiModel
public class GetMyExchangeOrder extends BasePage {
    @ApiModelProperty("状态(0=全部,1=待发货,2=待收货,3=已完成)")
    private Integer status;
}
ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/dto/MyChargingOrderInfo.java
New file
@@ -0,0 +1,59 @@
package com.ruoyi.order.dto;
import com.ruoyi.order.api.model.TChargingOrderAccountingStrategy;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.math.BigDecimal;
import java.util.List;
import java.util.Map;
/**
 * @author zhibing.pu
 * @Date 2024/8/16 9:08
 */
@Data
@ApiModel
public class MyChargingOrderInfo {
    @ApiModelProperty("订单id")
    private String id;
    @ApiModelProperty("订单编号")
    private String code;
    @ApiModelProperty("订单时间")
    private String createTime;
    @ApiModelProperty("订单状态(0=未知,1=等待中,2=启动中,3=充电中,4=停止中,5=已结束)")
    private Integer status;
    @ApiModelProperty("订单标题")
    private String title;
    @ApiModelProperty("充电模式(1=超级快充,2=快充,3=慢充)")
    private Integer chargeMode;
    @ApiModelProperty("充电枪名称")
    private String name;
    @ApiModelProperty("车牌号")
    private String licensePlate;
    @ApiModelProperty("充电开始时间")
    private Long startTime;
    @ApiModelProperty("充电结束时间")
    private Long endTime;
    @ApiModelProperty("充电费")
    private BigDecimal orderAmount;
    @ApiModelProperty("实际消费")
    private BigDecimal paymentAmount;;
    @ApiModelProperty("充值金额")
    private BigDecimal rechargeAmount;
    @ApiModelProperty("优惠券抵扣")
    private BigDecimal couponDiscountAmount;
    @ApiModelProperty("会员抵扣")
    private BigDecimal vipDiscountAmount;
    @ApiModelProperty("退回金额")
    private BigDecimal refundAmount;
    @ApiModelProperty("阶段费用明细")
    private List<TChargingOrderAccountingStrategy> stageCost;
    @ApiModelProperty("电流电压曲线")
    private Map<String, Object> currentAndVoltage;
    @ApiModelProperty("功率曲线")
    private Map<String, Object> power;
    @ApiModelProperty("是否可申诉(0=否,1=是)")
    private Integer actionable;
}
ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/dto/MyChargingOrderList.java
New file
@@ -0,0 +1,32 @@
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/15 18:59
 */
@Data
@ApiModel
public class MyChargingOrderList {
    @ApiModelProperty("订单id")
    private String id;
    @ApiModelProperty("订单状态(0=未知,1=等待中,2=启动中,3=充电中,4=停止中,5=已结束)")
    private Integer status;
    @ApiModelProperty("标题")
    private String title;
    @ApiModelProperty("充电度数")
    private BigDecimal chargingDegree;
    @ApiModelProperty("充电桩枪名称")
    private String name;
    @ApiModelProperty("结束方式(0=异常终止,1=主动终止,2=满电终止,3=费用不足终止)")
    private Integer endMode;
    @ApiModelProperty("支付金额")
    private BigDecimal payMoney;
    @ApiModelProperty("订单时间")
    private String createTime;
}
ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/dto/MyExchangeOrderList.java
New file
@@ -0,0 +1,30 @@
package com.ruoyi.order.dto;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
 * @author zhibing.pu
 * @Date 2024/8/16 13:36
 */
@Data
@ApiModel
public class MyExchangeOrderList {
    @ApiModelProperty("订单id")
    private String id;
    @ApiModelProperty("兑换积分单价")
    private Integer unitPoints;
    @ApiModelProperty("兑换总积分")
    private Integer points;
    @ApiModelProperty("兑换数量")
    private Integer purchaseQuantity;
    @ApiModelProperty("订单类型(1=商品,2=优惠券)")
    private Integer orderType;
    @ApiModelProperty("状态(1=待发货,2=待收货,3=已完成,4=已取消)")
    private Integer status;
    @ApiModelProperty("名称")
    private String name;
    @ApiModelProperty("图片")
    private String imgUrl;
}
ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/dto/OrderEvaluateVo.java
New file
@@ -0,0 +1,23 @@
package com.ruoyi.order.dto;
import com.ruoyi.order.api.model.TOrderEvaluate;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.util.List;
/**
 * @author zhibing.pu
 * @Date 2024/8/16 10:25
 */
@Data
@ApiModel
public class OrderEvaluateVo extends TOrderEvaluate {
    @ApiModelProperty("评价标签id")
    private List<Integer> tagIds;
    @ApiModelProperty("评价标签名称")
    private List<String> tagName;
}
ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/mapper/TChargingOrderMapper.java
@@ -4,6 +4,9 @@
import com.ruoyi.order.api.model.TChargingOrder;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
import java.util.Map;
/**
 * <p>
 *  Mapper 接口
@@ -15,4 +18,14 @@
@Mapper
public interface TChargingOrderMapper extends BaseMapper<TChargingOrder> {
    /**
     * 获取小程序充电记录列表数据
     * @param appUserId
     * @param type
     * @param pageCurr
     * @param pageSize
     * @return
     */
    List<TChargingOrder> getMyChargingOrderList(Long appUserId, Integer type, Integer pageCurr, Integer pageSize);
}
ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/service/TChargingOrderAccountingStrategyService.java
@@ -2,9 +2,11 @@
import com.baomidou.mybatisplus.extension.service.IService;
import com.ruoyi.order.api.model.TChargingOrderAccountingStrategy;
import org.springframework.web.bind.annotation.PathVariable;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
 * <p>
@@ -21,6 +23,13 @@
     * @param days  天数
     * @return
     */
    List<List<Map<String, Object>>> getTotalElectricQuantity(Integer days);
    List<List<Map<String, Object>>> getTotalElectricQuantity(Integer days, Set<Integer> siteIds);
    
    /**
     * 获取给定天数每天的充电度数
     * @param days 天数
     * @return
     */
    List<Double> getDailyChargingDegree(Integer days, Set<Integer> siteIds);
}
ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/service/TChargingOrderService.java
@@ -1,7 +1,12 @@
package com.ruoyi.order.service;
import com.baomidou.mybatisplus.extension.service.IService;
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.MyChargingOrderInfo;
import java.util.Map;
/**
 * <p>
@@ -13,4 +18,19 @@
 */
public interface TChargingOrderService extends IService<TChargingOrder> {
    /**
     * 获取小程序充电记录列表数据
     * @param query
     * @return
     */
    Map<String, Object> getMyChargingOrderList(GetMyChargingOrderList query);
    /**
     * 获取小程序充电记录明细
     * @param id
     * @return
     */
    MyChargingOrderInfo getMyChargingOrderInfo(String id);
}
ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/service/TExchangeOrderService.java
@@ -2,6 +2,11 @@
import com.baomidou.mybatisplus.extension.service.IService;
import com.ruoyi.order.api.model.TExchangeOrder;
import com.ruoyi.order.dto.ExchangeOrderGoodsInfo;
import com.ruoyi.order.dto.GetMyExchangeOrder;
import com.ruoyi.order.dto.MyExchangeOrderList;
import java.util.List;
/**
 * <p>
@@ -13,4 +18,19 @@
 */
public interface TExchangeOrderService extends IService<TExchangeOrder> {
    /**
     * 获取小程序兑换记录
     * @param query
     * @return
     */
    List<MyExchangeOrderList> getMyExchangeOrder(GetMyExchangeOrder query);
    /**
     * 获取兑换订单详情
     * @param id
     * @return
     */
    ExchangeOrderGoodsInfo getGoodsExchangeOrder(String id);
}
ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/service/TOrderEvaluateService.java
@@ -1,6 +1,7 @@
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.PageInfo;
import com.ruoyi.order.api.model.TOrderEvaluate;
import com.ruoyi.order.api.query.TOrderEvaluateQuery;
@@ -8,7 +9,9 @@
import com.ruoyi.order.api.vo.TOrderEvaluateVO;
import com.ruoyi.order.dto.GetOrderEvaluatePageList;
import com.ruoyi.order.dto.GetOrderEvaluatePageListDTO;
import com.ruoyi.order.dto.OrderEvaluateVo;
import com.ruoyi.other.api.vo.TEvaluationTagVO;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import java.util.List;
@@ -50,4 +53,20 @@
     * @return
     */
    PageInfo<GetOrderEvaluatePageListDTO> getPageList(GetOrderEvaluatePageList pageList);
    /**
     * 评价订单
     * @param query
     * @return
     */
    AjaxResult addOrderEvaluate(OrderEvaluateVo query);
    /**
     * 根据订单id获取
     * @param orderId
     * @return
     */
    OrderEvaluateVo getOrderEvaluate(Integer orderId, Integer orderType);
}
ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/service/impl/TChargingOrderAccountingStrategyServiceImpl.java
@@ -14,8 +14,10 @@
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.SimpleDateFormat;
import java.time.Duration;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.LongStream;
/**
 * <p>
@@ -39,14 +41,15 @@
     * @return
     */
    @Override
    public List<List<Map<String, Object>>> getTotalElectricQuantity(Integer days) {
    public List<List<Map<String, Object>>> getTotalElectricQuantity(Integer days, Set<Integer> siteIds) {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        Calendar calendar = Calendar.getInstance();
        calendar.set(Calendar.DAY_OF_YEAR, calendar.get(Calendar.DAY_OF_YEAR) - days);
        String startTime = sdf.format(calendar.getTime()) + " 00:00:00";
        List<TChargingOrder> list = chargingOrderService.list(new LambdaQueryWrapper<TChargingOrder>().eq(TChargingOrder::getStatus, 5)
        List<TChargingOrder> list = chargingOrderService.list(new LambdaQueryWrapper<TChargingOrder>().in(TChargingOrder::getSiteId, siteIds).eq(TChargingOrder::getStatus, 5)
                .eq(TChargingOrder::getDelFlag, 0).last(" and end_time between '" + startTime + "' and DATE_FORMAT('%Y-%m-%d %H:%i:%s', now())"));
        List<TChargingOrderAccountingStrategy> list1 = this.list(new LambdaQueryWrapper<TChargingOrderAccountingStrategy>().in(TChargingOrderAccountingStrategy::getChargingOrderId));
        List<Long> orderId = list.stream().map(TChargingOrder::getId).collect(Collectors.toList());
        List<TChargingOrderAccountingStrategy> list1 = this.list(new LambdaQueryWrapper<TChargingOrderAccountingStrategy>().in(TChargingOrderAccountingStrategy::getChargingOrderId, orderId));
        
        List<List<Map<String, Object>>> list2 = new ArrayList<>();
        Calendar nowDateTime = Calendar.getInstance();
@@ -88,4 +91,32 @@
        }
        return list2;
    }
    /**
     * 获取给定天数每天的充电度数
     * @param days 天数
     * @return
     */
    @Override
    public List<Double> getDailyChargingDegree(Integer days, Set<Integer> siteIds) {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        Calendar calendar = Calendar.getInstance();
        calendar.set(Calendar.DAY_OF_YEAR, calendar.get(Calendar.DAY_OF_YEAR) - days);
        String startTime = sdf.format(calendar.getTime()) + " 00:00:00";
        List<TChargingOrder> list = chargingOrderService.list(new LambdaQueryWrapper<TChargingOrder>().in(TChargingOrder::getSiteId, siteIds).eq(TChargingOrder::getStatus, 5)
                .eq(TChargingOrder::getDelFlag, 0).last(" and end_time between '" + startTime + "' and DATE_FORMAT('%Y-%m-%d %H:%i:%s', now())"));
        List<Double> list2 = new ArrayList<>();
        Calendar nowDateTime = Calendar.getInstance();
        //遍历获取每天的数值
        for (int i = days; i >= 0; i--) {
            nowDateTime.set(Calendar.DAY_OF_YEAR, nowDateTime.get(Calendar.DAY_OF_YEAR) - i);
            //充电量
            BigDecimal reduce = list.stream().filter(s -> sdf.format(s.getEndTime()).equals(sdf.format(nowDateTime.getTime())))
                    .map(TChargingOrder::getChargingCapacity).reduce(BigDecimal.ZERO, BigDecimal::add);
            list2.add(reduce.doubleValue());
        }
        return list2;
    }
}
ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/service/impl/TChargingOrderServiceImpl.java
@@ -1,10 +1,33 @@
package com.ruoyi.order.service.impl;
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.model.TAppUserCar;
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.common.core.web.page.BasePage;
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.MyChargingOrderInfo;
import com.ruoyi.order.dto.MyChargingOrderList;
import com.ruoyi.order.mapper.TChargingOrderMapper;
import com.ruoyi.order.service.TChargingOrderAccountingStrategyService;
import com.ruoyi.order.service.TChargingOrderService;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.util.*;
/**
 * <p>
@@ -17,4 +40,101 @@
@Service
public class TChargingOrderServiceImpl extends ServiceImpl<TChargingOrderMapper, TChargingOrder> implements TChargingOrderService {
    @Resource
    private ChargingGunClient chargingGunClient;
    @Resource
    private SiteClient siteClient;
    @Resource
    private AppUserCarClient appUserCarClient;
    @Resource
    private TChargingOrderAccountingStrategyService chargingOrderAccountingStrategyService;
    @Resource
    private TokenService tokenService;
    /**
     * 获取小程序充电记录列表数据
     * @param query
     * @return
     */
    @Override
    public Map<String, Object> getMyChargingOrderList(GetMyChargingOrderList query) {
        Long appUserId = tokenService.getLoginUser().getUserid();
        Map<String, Object> map = new HashMap<>();
        int size = this.baseMapper.getMyChargingOrderList(appUserId, 1, null, null).size();
        map.put("number", size);
        List<TChargingOrder> list = this.baseMapper.getMyChargingOrderList(appUserId, query.getType(), query.getPageCurr(), query.getPageSize());
        List<MyChargingOrderList> orderLists = new ArrayList<>();
        for (TChargingOrder tChargingOrder : list) {
            MyChargingOrderList myChargingOrderList = new MyChargingOrderList();
            myChargingOrderList.setId(tChargingOrder.getId().toString());
            myChargingOrderList.setStatus(tChargingOrder.getStatus());
            Site site = siteClient.getSiteByIds(Arrays.asList(tChargingOrder.getSiteId())).getData().get(0);
            myChargingOrderList.setTitle(site.getName());
            myChargingOrderList.setChargingDegree(tChargingOrder.getChargingCapacity());
            String name = chargingGunClient.getAllName(tChargingOrder.getChargingGunId()).getData();
            myChargingOrderList.setName(name);
            myChargingOrderList.setEndMode(tChargingOrder.getEndMode());
            BigDecimal payMoney = tChargingOrder.getStatus() < 4 ? tChargingOrder.getRechargeAmount() : tChargingOrder.getPaymentAmount();
            myChargingOrderList.setPayMoney(payMoney);
            myChargingOrderList.setCreateTime(tChargingOrder.getCreateTime().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
            orderLists.add(myChargingOrderList);
        }
        map.put("list", orderLists);
        return map;
    }
    /**
     * 获取充电订单明细
     * @param id 订单id
     * @return
     */
    @Override
    public MyChargingOrderInfo getMyChargingOrderInfo(String id) {
        TChargingOrder chargingOrder = this.getById(id);
        MyChargingOrderInfo myChargingOrderInfo = new MyChargingOrderInfo();
        myChargingOrderInfo.setId(id);
        myChargingOrderInfo.setCode(chargingOrder.getCode());
        myChargingOrderInfo.setCreateTime(chargingOrder.getCreateTime().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
        myChargingOrderInfo.setStatus(chargingOrder.getStatus());
        Site site = siteClient.getSiteByIds(Arrays.asList(chargingOrder.getSiteId())).getData().get(0);
        myChargingOrderInfo.setTitle(site.getName());
        TChargingGun chargingGun = chargingGunClient.getChargingGunById(chargingOrder.getChargingGunId()).getData();
        myChargingOrderInfo.setChargeMode(chargingGun.getChargeMode());
        String name = chargingGunClient.getAllName(chargingOrder.getChargingGunId()).getData();
        myChargingOrderInfo.setName(name);
        if(null != chargingOrder.getAppUserCarId()){
            TAppUserCar tAppUserCar = appUserCarClient.getCarByIds(Arrays.asList(chargingOrder.getAppUserCarId())).getData().get(0);
            myChargingOrderInfo.setLicensePlate(tAppUserCar.getLicensePlate());
        }else{
            myChargingOrderInfo.setLicensePlate("无");
        }
        myChargingOrderInfo.setStartTime(chargingOrder.getStartTime().toInstant(ZoneOffset.of("+8")).toEpochMilli());
        myChargingOrderInfo.setEndTime(chargingOrder.getEndTime().toInstant(ZoneOffset.of("+8")).toEpochMilli());
        myChargingOrderInfo.setOrderAmount(chargingOrder.getOrderAmount());
        myChargingOrderInfo.setPaymentAmount(chargingOrder.getPaymentAmount());
        myChargingOrderInfo.setRechargeAmount(chargingOrder.getRechargeAmount());
        myChargingOrderInfo.setCouponDiscountAmount(chargingOrder.getCouponDiscountAmount());
        myChargingOrderInfo.setVipDiscountAmount(chargingOrder.getVipDiscountAmount());
        myChargingOrderInfo.setRefundAmount(chargingOrder.getRefundAmount());
        List<TChargingOrderAccountingStrategy> stageCost = chargingOrderAccountingStrategyService.list(
                new LambdaQueryWrapper<TChargingOrderAccountingStrategy>().eq(TChargingOrderAccountingStrategy::getChargingOrderId, id)
                        .orderByAsc(TChargingOrderAccountingStrategy::getStartTime)
        );
        myChargingOrderInfo.setStageCost(stageCost);
        // todo 待完善
        //再MongoDB中获取数据
        myChargingOrderInfo.setCurrentAndVoltage(null);
        //再MongoDB中获取数据
        myChargingOrderInfo.setPower(null);
        myChargingOrderInfo.setActionable(myChargingOrderInfo.getEndTime() + 604800000L > System.currentTimeMillis() ? 0 : 1);
        return myChargingOrderInfo;
    }
}
ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/service/impl/TExchangeOrderServiceImpl.java
@@ -1,10 +1,27 @@
package com.ruoyi.order.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.account.api.feignClient.AppUserAddressClient;
import com.ruoyi.account.api.model.TAppUserAddress;
import com.ruoyi.common.security.service.TokenService;
import com.ruoyi.order.api.model.TExchangeOrder;
import com.ruoyi.order.dto.ExchangeOrderGoodsInfo;
import com.ruoyi.order.dto.GetMyExchangeOrder;
import com.ruoyi.order.dto.MyExchangeOrderList;
import com.ruoyi.order.mapper.TExchangeOrderMapper;
import com.ruoyi.order.service.TExchangeOrderService;
import com.ruoyi.other.api.domain.TCoupon;
import com.ruoyi.other.api.domain.TGoods;
import com.ruoyi.other.api.feignClient.CouponClient;
import com.ruoyi.other.api.feignClient.GoodsClient;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
/**
 * <p>
@@ -17,4 +34,107 @@
@Service
public class TExchangeOrderServiceImpl extends ServiceImpl<TExchangeOrderMapper, TExchangeOrder> implements TExchangeOrderService {
    @Resource
    private TokenService tokenService;
    @Resource
    private GoodsClient goodsClient;
    @Resource
    private CouponClient couponClient;
    @Resource
    private AppUserAddressClient appUserAddressClient;
    /**
     * 获取小程序兑换记录
     * @param query
     * @return
     */
    @Override
    public List<MyExchangeOrderList> getMyExchangeOrder(GetMyExchangeOrder query) {
        Long userid = tokenService.getLoginUser().getUserid();
        LambdaQueryWrapper<TExchangeOrder> wrapper = new LambdaQueryWrapper<TExchangeOrder>().eq(TExchangeOrder::getDelFlag, 0).eq(TExchangeOrder::getAppUserId, userid);
        if(query.getStatus() == 0){
            wrapper.ne(TExchangeOrder::getStatus, 4);
        }else{
            wrapper.eq(TExchangeOrder::getStatus, query.getStatus());
        }
        List<TExchangeOrder> list = this.list(wrapper.orderByDesc(TExchangeOrder::getCreateTime).last(" limit " + query.getPageCurr() + "," + query.getPageSize()));
        List<MyExchangeOrderList> pageList = new ArrayList<>();
        for (TExchangeOrder tExchangeOrder : list) {
            MyExchangeOrderList exchangeOrderList = new MyExchangeOrderList();
            exchangeOrderList.setId(tExchangeOrder.getId().toString());
            exchangeOrderList.setUnitPoints(tExchangeOrder.getPoints() / tExchangeOrder.getPurchaseQuantity());
            exchangeOrderList.setPoints(tExchangeOrder.getPoints());
            exchangeOrderList.setPurchaseQuantity(tExchangeOrder.getPurchaseQuantity());
            exchangeOrderList.setOrderType(tExchangeOrder.getOrderType());
            exchangeOrderList.setStatus(tExchangeOrder.getStatus());
            String name = "";
            String imgUrl = "";
            if(tExchangeOrder.getOrderType() == 1){
                TGoods goods = goodsClient.getGoodsById(tExchangeOrder.getGoodsId()).getData();
                name = goods.getName();
                imgUrl = goods.getCoverPicture();
            }else{
                TCoupon coupon = couponClient.getCouponById(tExchangeOrder.getGoodsId()).getData();
                name = coupon.getName();
                imgUrl = coupon.getCoverPicture();
            }
            exchangeOrderList.setName(name);
            exchangeOrderList.setImgUrl(imgUrl);
            pageList.add(exchangeOrderList);
        }
        return pageList;
    }
    /**
     * 获取兑换订单详情
     * @param id
     * @return
     */
    @Override
    public ExchangeOrderGoodsInfo getGoodsExchangeOrder(String id) {
        TExchangeOrder exchangeOrder = this.getById(id);
        ExchangeOrderGoodsInfo info = new ExchangeOrderGoodsInfo();
        info.setId(id);
        info.setStatus(exchangeOrder.getStatus());
        TAppUserAddress userAddress = appUserAddressClient.getAppUserAddressById(exchangeOrder.getAppUserAddressId()).getData();
        info.setConsignee(userAddress.getName());
        info.setPhone(userAddress.getPhone());
        info.setAddress(userAddress.getAddress());
        info.setExpressCompany(exchangeOrder.getExpressCompany());
        info.setExpressNumber(exchangeOrder.getExpressNumber());
        String name = "";
        String imgUrl = "";
        if(exchangeOrder.getOrderType() == 1){
            TGoods goods = goodsClient.getGoodsById(exchangeOrder.getGoodsId()).getData();
            name = goods.getName();
            imgUrl = goods.getCoverPicture();
        }else{
            TCoupon coupon = couponClient.getCouponById(exchangeOrder.getGoodsId()).getData();
            info.setCouponType(coupon.getType());
            info.setDays(coupon.getDays());
            info.setEndTime(coupon.getEndTime().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
            name = coupon.getName();
            imgUrl = coupon.getCoverPicture();
        }
        info.setName(name);
        info.setImgUrl(imgUrl);
        info.setPurchaseQuantity(exchangeOrder.getPurchaseQuantity());
        info.setUnitPoints(exchangeOrder.getPoints() / exchangeOrder.getPurchaseQuantity());
        info.setCode(exchangeOrder.getCode());
        info.setCreateTime(exchangeOrder.getCreateTime().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
        info.setPoints(exchangeOrder.getPoints());
        info.setRemark(exchangeOrder.getRemark());
        info.setDeliveryTime(exchangeOrder.getConsignerTime().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
        info.setFinishTime(exchangeOrder.getReceivingTime().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
        return info;
    }
}
ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/service/impl/TOrderEvaluateServiceImpl.java
@@ -1,19 +1,26 @@
package com.ruoyi.order.service.impl;
import cn.hutool.db.DaoTemplate;
import com.alibaba.fastjson2.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.account.api.feignClient.AppUserCarClient;
import com.ruoyi.account.api.feignClient.AppUserClient;
import com.ruoyi.account.api.feignClient.AppUserIntegralChangeClient;
import com.ruoyi.account.api.model.TAppUser;
import com.ruoyi.account.api.model.TAppUserCar;
import com.ruoyi.account.api.feignClient.AppUserClient;
import com.ruoyi.account.api.model.TAppUser;
import com.ruoyi.account.api.model.TAppUserIntegralChange;
import com.ruoyi.chargingPile.api.feignClient.SiteClient;
import com.ruoyi.chargingPile.api.model.Site;
import com.ruoyi.chargingPile.api.vo.TAccountingStrategyVO;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.core.utils.StringUtils;
import com.ruoyi.common.core.web.domain.AjaxResult;
import com.ruoyi.common.core.web.page.PageInfo;
import com.ruoyi.common.security.service.TokenService;
import com.ruoyi.order.api.model.TChargingOrder;
import com.ruoyi.order.api.model.TOrderEvaluate;
import com.ruoyi.order.api.model.TOrderEvaluateTag;
@@ -22,19 +29,26 @@
import com.ruoyi.order.api.vo.TOrderEvaluateVO;
import com.ruoyi.order.dto.GetOrderEvaluatePageList;
import com.ruoyi.order.dto.GetOrderEvaluatePageListDTO;
import com.ruoyi.order.dto.OrderEvaluateVo;
import com.ruoyi.order.mapper.TChargingOrderMapper;
import com.ruoyi.order.mapper.TOrderEvaluateMapper;
import com.ruoyi.order.mapper.TOrderEvaluateTagMapper;
import com.ruoyi.order.service.TChargingOrderService;
import com.ruoyi.order.service.TOrderEvaluateService;
import com.ruoyi.other.api.domain.TEvaluationTag;
import com.ruoyi.other.api.domain.TIntegralRule;
import com.ruoyi.other.api.feignClient.IntegralRuleClient;
import com.ruoyi.other.api.feignClient.TEvaluationTagClient;
import com.ruoyi.other.api.vo.TEvaluationTagVO;
import io.seata.spring.annotation.GlobalTransactional;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import java.math.BigDecimal;
import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
@@ -66,6 +80,19 @@
    
    @Resource
    private SiteClient siteClient;
    @Resource
    private TokenService tokenService;
    @Resource
    private TChargingOrderService chargingOrderService;
    @Resource
    private IntegralRuleClient integralRuleClient;
    @Resource
    private AppUserIntegralChangeClient appUserIntegralChangeClient;
    
    
    
@@ -202,4 +229,84 @@
        }
        return pageInfo.setRecords(list);
    }
    /**
     * 评价订单
     * @param query
     * @return
     */
    @Override
    @GlobalTransactional(rollbackFor = Exception.class)//分布式事务
    public AjaxResult addOrderEvaluate(OrderEvaluateVo query) {
        TOrderEvaluate orderEvaluate = this.getOne(new LambdaQueryWrapper<TOrderEvaluate>().eq(TOrderEvaluate::getOrderId, query.getOrderId())
                .eq(TOrderEvaluate::getOrderType, query.getOrderType()).eq(TOrderEvaluate::getDelFlag, 0));
        if(null != orderEvaluate){
            return AjaxResult.error("不能重复评价");
        }
        orderEvaluate = new TOrderEvaluate();
        BeanUtils.copyProperties(query, orderEvaluate);
        Long userid = tokenService.getLoginUser().getUserid();
        orderEvaluate.setAppUserId(userid);
        if(query.getOrderType() == 1){
            TChargingOrder chargingOrder = chargingOrderService.getById(query.getOrderId());
            orderEvaluate.setAppUserCarId(chargingOrder.getAppUserCarId());
        }
        this.save(orderEvaluate);
        List<Integer> tagIds = query.getTagIds();
        for (Integer tagId : tagIds) {
            TOrderEvaluateTag orderEvaluateTag = new TOrderEvaluateTag();
            orderEvaluateTag.setOrderEvaluateId(orderEvaluate.getId());
            orderEvaluateTag.setEvaluationTagId(tagId);
            orderEvaluateTagMapper.insert(orderEvaluateTag);
        }
        //判断发放积分  文字+图片+5星好评
        if(StringUtils.isNotEmpty(query.getContent()) && StringUtils.isNotEmpty(query.getImgUrl()) && 5 == query.getMark()){
            TIntegralRule data = integralRuleClient.getSet().getData();
            String credit = data.getCredit();
            //积分
            Integer num1 = JSON.parseObject(credit).getInteger("num1");
            if(null != num1 && 0 < num1){
                TAppUser appUser = appUserClient.getUserById(userid).getData();
                Integer points = appUser.getPoints();
                appUser.setPoints(appUser.getPoints() + num1);
                appUserClient.updateAppUser(appUser);
                //积分变动明细
                TAppUserIntegralChange appUserIntegralChange = new TAppUserIntegralChange();
                appUserIntegralChange.setAppUserId(appUser.getId());
                appUserIntegralChange.setChangeType(3);
                appUserIntegralChange.setHistoricalIntegral(points);
                appUserIntegralChange.setCurrentIntegral(points + num1);
                appUserIntegralChange.setCreateTime(LocalDateTime.now());
                appUserIntegralChangeClient.addAppUserIntegralChange(appUserIntegralChange);
            }
        }
        return AjaxResult.success();
    }
    /**
     * 获取订单评价
     * @param orderId 订单id
     * @param orderType 订单类型
     * @return
     */
    @Override
    public OrderEvaluateVo getOrderEvaluate(Integer orderId, Integer orderType) {
        TOrderEvaluate orderEvaluate = this.getOne(new LambdaQueryWrapper<TOrderEvaluate>().eq(TOrderEvaluate::getOrderId, orderId).eq(TOrderEvaluate::getOrderType, orderType));
        if(null != orderEvaluate){
            OrderEvaluateVo orderEvaluateVo = new OrderEvaluateVo();
            BeanUtils.copyProperties(orderEvaluate, orderEvaluateVo);
            List<TOrderEvaluateTag> tOrderEvaluateTags = orderEvaluateTagMapper.selectList(new LambdaQueryWrapper<TOrderEvaluateTag>().eq(TOrderEvaluateTag::getOrderEvaluateId, orderEvaluate.getId()));
            List<Integer> tagId = tOrderEvaluateTags.stream().map(TOrderEvaluateTag::getEvaluationTagId).collect(Collectors.toList());
            List<TEvaluationTag> data = evaluationTagClient.getListByIds(tagId).getData();
            List<String> collect = data.stream().map(TEvaluationTag::getName).collect(Collectors.toList());
            orderEvaluateVo.setTagName(collect);
            return orderEvaluateVo;
        }
        return null;
    }
}
ruoyi-service/ruoyi-order/src/main/resources/bootstrap.yml
@@ -44,7 +44,7 @@
  enabled: true
  application-id: ${spring.application.name}
  tx-service-group: seata_tx_group    #此处配置自定义的seata事务分组名称
  enable-auto-data-source-proxy: true    #开启数据库代理
  enable-auto-data-source-proxy: false    #关闭数据库代理
  service:
    vgroup-mapping:
      seata_tx_group: default
ruoyi-service/ruoyi-order/src/main/resources/mapper/order/TChargingOrderMapper.xml
@@ -14,9 +14,12 @@
        <result column="parking_lot_id" property="parkingLotId" />
        <result column="charging_pile_id" property="chargingPileId" />
        <result column="charging_gun_id" property="chargingGunId" />
        <result column="charging_capacity" property="chargingCapacity"/>
        <result column="charging_power" property="chargingPower"/>
        <result column="start_time" property="startTime" />
        <result column="end_time" property="endTime" />
        <result column="status" property="status" />
        <result column="end_mode" property="endMode"/>
        <result column="recharge_amount" property="rechargeAmount" />
        <result column="recharge_payment_type" property="rechargePaymentType" />
        <result column="recharge_payment_status" property="rechargePaymentStatus" />
@@ -41,4 +44,15 @@
        id, code, order_type, order_classification, app_user_id, app_user_car_id,site_id, parking_lot_id, charging_pile_id, charging_gun_id, start_time, end_time, status, recharge_amount, recharge_payment_type, recharge_payment_status, recharge_serial_number, order_amount, app_coupon_id, coupon_discount_amount, vip_discount, vip_discount_amount, payment_amount, refund_code, refund_amount, refund_status, refund_serial_number, refund_time, create_time, del_flag
    </sql>
    <select id="getMyChargingOrderList" resultMap="BaseResultMap">
        select * from t_charging_order where del_flag = 0 and app_user_id = #{appUserId}
        <if test="1 == type">
            and id not in (select order_id from t_order_evaluate where order_type = 1 and app_user_id = #{appUserId} and del_flag = 0)
        </if>
        order by create_time desc
        <if test="null != pageCurr and null != pageSize">
            limit #{pageCurr}, #{pageSize}
        </if>
    </select>
</mapper>
ruoyi-service/ruoyi-other/pom.xml
@@ -51,6 +51,11 @@
            <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-seata</artifactId>
        </dependency>
        <!-- SpringBoot Actuator -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/config/DataSourceProxyConfig.java
New file
@@ -0,0 +1,21 @@
//package com.ruoyi.other.config;
//
//import io.seata.rm.datasource.DataSourceProxy;
//import org.springframework.context.annotation.Bean;
//import org.springframework.context.annotation.Configuration;
//
//import javax.sql.DataSource;
//
///**
// * @author zhibing.pu
// * @date 2024/8/17 11:10
// */
//@Configuration
//public class DataSourceProxyConfig {
//
//
//    @Bean
//    public DataSource dataSource(DataSource druidDataSource){
//        return new DataSourceProxy(druidDataSource);
//    }
//}
ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/controller/OssController.java
New file
@@ -0,0 +1,80 @@
package com.ruoyi.other.controller;
import com.ruoyi.common.core.utils.ObsUploadUtil;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
@RestController
@RequestMapping("/base/oss")
@Api(value = "文件上传", tags = "文件上传", description = "文件上传")
public class OssController {
    private static final String endpoint = "obs.cn-north-4.myhuaweicloud.com";
    private static final String accessKeyId = "DRJFJRVQ9KVLQUSJUROI";
    private static final String accessKeySecret = "3zpPkULh0a8CTTYjKL4f7szQZECkvVol4SwNab0f";
    private static final String bucketName = "jkjianshen";
    @PostMapping("/upload")
    @ApiOperation(value = "文件上传")
    public String upload(@RequestParam("file") MultipartFile file) throws IOException {
        String s = ObsUploadUtil.obsUpload(file);
        return s;
    }
//    @PostMapping("/uploadVideo")
//    @ApiOperation(value = "视频上传",tags = "文件上传")
//    public String uploadVideo(@RequestParam("file") MultipartFile file) throws Exception {
//        // 获取文件名
//        String fileName = file.getOriginalFilename();
//        // 构建目标文件路径
//        String filePath = "/usr/local/temp" + fileName;
////        String filePath = "C:/temp/" + fileName;
//        // 创建目标文件对象
//        File targetFile = new File(filePath);
//        // 将 MultipartFile 内容写入目标文件
//        try (FileOutputStream fos = new FileOutputStream(targetFile)) {
//            fos.write(file.getBytes());
//        }
//        String s = UploadVideoUtil.endUpload(targetFile.toString(), UUIDUtil.getRandomCode(5)+fileName);
//        // 创建要删除的文件对象
//        File fileToDelete = new File(targetFile.toString());
//        // 检查文件是否存在,并删除文件
//        if (fileToDelete.exists()) {
//            fileToDelete.delete();
//        }
//        return s;
//    }
//    @PostMapping("/upload")
//    @ApiOperation(value = "文件上传",tags = "文件上传")
//    public ResponseEntity<String> upload(@RequestParam("file") MultipartFile file) throws IOException {
//        // 创建 OSSClient 实例
//        OSSClient ossClient = new OSSClient(endpoint, accessKeyId, accessKeySecret);
//
//
//        // 创建 PutObjectRequest 对象
//        PutObjectRequest putObjectRequest = new PutObjectRequest(bucketName, file.getOriginalFilename(), file.getInputStream());
//
//        ObjectMetadata metadata = new ObjectMetadata();
//        // 取消文件缓存,文件每次都会从OSS服务器获取
//        metadata.setHeader("Cache-Control", "no-cache");
//        metadata.setHeader("Expires", "0");
//
//
//        // 上传文件
//        PutObjectResult putObjectResult = ossClient.putObject(putObjectRequest);
//
//        // 关闭 OSSClient 实例
//        ossClient.shutdown();
//
//        String fileUrl = "https://" + bucketName + "." + endpoint + "/" + file.getOriginalFilename();
//        return ResponseEntity.ok(fileUrl);
//    }
}
ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/controller/TCouponController.java
@@ -87,6 +87,8 @@
                }else{
                    record.setMeetTheConditions("满"+byId.getMeetTheConditions()+"元可用");
                }
                record.setCoverPicture(byId.getCoverPicture());
                record.setDetailsPicture(byId.getDetailsPicture());
            }
        }
        return AjaxResult.ok(data);
@@ -132,5 +134,17 @@
        }
        return R.ok(list);
    }
    /**
     * 根据id获取优惠券信息
     * @param id
     * @return
     */
    @PostMapping(value = "/getCouponById/{id}")
    public R<TCoupon> getCouponById(@PathVariable Integer id){
        TCoupon coupon = tCouponService.getById(id);
        return R.ok(coupon);
    }
}
ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/controller/TEvaluationTagController.java
@@ -1,6 +1,7 @@
package com.ruoyi.other.controller;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.core.web.domain.AjaxResult;
import com.ruoyi.common.core.web.page.BasePage;
@@ -79,13 +80,12 @@
    
    
    @ResponseBody
    @DeleteMapping("/delEvaluationTag/{id}")
    @DeleteMapping("/delEvaluationTag")
    @ApiOperation(value = "删除评价标签", tags = {"管理后台-评价标签设置"})
    public AjaxResult<TInvoiceType> delEvaluationTag(@PathVariable Integer[] id){
    public AjaxResult<TInvoiceType> delEvaluationTag(@RequestParam("id") Integer[] id){
        List<TEvaluationTag> tEvaluationTags = evaluationTagService.listByIds(Arrays.asList(id));
        for (TEvaluationTag evaluationTag : tEvaluationTags) {
            evaluationTag.setDelFlag(true);
            evaluationTagService.updateById(evaluationTag);
            evaluationTagService.removeById(evaluationTag);
        }
        return AjaxResult.success();
    }
@@ -96,10 +96,20 @@
     * @param tagIds
     * @return
     */
    @GetMapping("/getListByIds")
    public R<List<TEvaluationTag>> getListByIds(List<Integer> tagIds){
    @PostMapping("/getListByIds")
    public R<List<TEvaluationTag>> getListByIds(@RequestBody List<Integer> tagIds){
        List<TEvaluationTag> tEvaluationTags = evaluationTagService.listByIds(tagIds);
        return R.ok(tEvaluationTags);
    }
    @ResponseBody
    @GetMapping("/getRandomEvaluationTag")
    @ApiOperation(value = "获取随机的4个评价标签", tags = {"小程序-充电记录"})
    public AjaxResult<List<TEvaluationTag>> getRandomEvaluationTag(){
        List<TEvaluationTag> list = evaluationTagService.list(new LambdaQueryWrapper<TEvaluationTag>().last(" order by RAND() limit 0, 4"));
        return AjaxResult.success(list);
    }
}
ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/controller/TGoodsController.java
@@ -1,6 +1,7 @@
package com.ruoyi.other.controller;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.core.web.domain.AjaxResult;
import com.ruoyi.common.core.web.page.PageInfo;
import com.ruoyi.other.api.domain.TActivity;
@@ -57,5 +58,16 @@
    public AjaxResult<PageInfo<TGoods>> pageList(@RequestBody GoodsDTO dto) {
        return AjaxResult.ok(goodsService.pageList(dto));
    }
    /**
     * 根据id获取商品信息
     * @param id
     * @return
     */
    @PostMapping("/getGoodsById/{id}")
    public R<TGoods> getGoodsById(@PathVariable Integer id){
        TGoods goods = goodsService.getById(id);
        return R.ok(goods);
    }
}
ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/controller/TIntegralRuleController.java
@@ -1,14 +1,12 @@
package com.ruoyi.other.controller;
import java.time.LocalDateTime;
import java.util.Date;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.core.web.domain.AjaxResult;
import com.ruoyi.other.api.domain.THtml;
import com.ruoyi.other.api.domain.TIntegralRule;
import com.ruoyi.other.api.dto.SaveIntegralDTO;
import com.ruoyi.other.service.THtmlService;
import com.ruoyi.other.service.TIntegralRuleService;
import io.swagger.annotations.ApiImplicitParam;
@@ -32,20 +30,26 @@
    private THtmlService htmlService;
    @Autowired
    private TIntegralRuleService integralRuleService;
    @GetMapping("/getSet")
    @ApiOperation(value = "获取积分设置")
    @ApiOperation(tags = {"管理后台-积分管理"},value = "获取积分设置")
    public R<TIntegralRule> getSet() {
        TIntegralRule res = integralRuleService.getOne(new QueryWrapper<>());
        return R.ok(res);
    }
    @PostMapping("/saveSet")
    @ApiOperation(value = "保存积分设置")
    @ApiOperation(tags = {"管理后台-积分管理"},value = "保存积分设置")
    public R saveSet(@RequestBody TIntegralRule dto) {
        integralRuleService.updateById(dto);
        return R.ok();
    }
    @GetMapping("/getInfo")
    @ApiOperation(value = "type 1=积分规则说明," +
    @ApiOperation(tags = {"管理后台-积分管理"},value = "type 1=积分规则说明," +
            "2=会员折扣说明," +
            "3=优惠券说明," +
            "4=双倍积分说明," +
@@ -61,7 +65,7 @@
        return R.ok(g.getContent());
    }
    @GetMapping("/saveInfo")
    @ApiOperation(value = "保存积分说明")
    @ApiOperation(tags = {"管理后台-积分管理"},value = "保存积分说明")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "info", value = "内容", required = true),
            @ApiImplicitParam(name = "type", value = "type 1=积分规则说明,\" +\n" +
@@ -83,10 +87,6 @@
            THtml tHtml = new THtml();
            tHtml.setType(type);
            tHtml.setContent(info);
            tHtml.setCreateTime(LocalDateTime.now());
            tHtml.setDelFlag(false);
            htmlService.save(tHtml);
        }else{
            g.setContent(info);
@@ -94,5 +94,13 @@
        }
        return R.ok();
    }
    @GetMapping("/getIntegralRule")
    @ApiOperation(tags = {"小程序-充电记录"},value = "获取积分规则设置")
    public AjaxResult<String> getIntegralRule(){
        TIntegralRule one = integralRuleService.getOne(null);
        return AjaxResult.success(one);
    }
}
ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/controller/TInvoiceTypeController.java
@@ -65,13 +65,12 @@
    
    
    @ResponseBody
    @DeleteMapping("/delInvoiceType/{id}")
    @DeleteMapping("/delInvoiceType")
    @ApiOperation(value = "删除发票类型", tags = {"管理后台-发票类型管理"})
    public AjaxResult<TInvoiceType> delInvoiceType(@PathVariable Integer[] id){
    public AjaxResult<TInvoiceType> delInvoiceType(@RequestParam("id") Integer[] id){
        List<TInvoiceType> tInvoiceTypes = invoiceTypeService.listByIds(Arrays.asList(id));
        for (TInvoiceType invoiceType : tInvoiceTypes) {
            invoiceType.setDelFlag(true);
            invoiceTypeService.updateById(invoiceType);
            invoiceTypeService.removeById(invoiceType);
        }
        return AjaxResult.success();
    }
ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/controller/TRoleSiteController.java
@@ -32,8 +32,8 @@
     * @return
     */
    @ResponseBody
    @GetMapping("/getSiteIds")
    public R<List<Integer>> getSiteIds(Long roleId){
    @PostMapping("/getSiteIds/{roleId}")
    public R<List<Integer>> getSiteIds(@PathVariable Long roleId){
        List<TRoleSite> list = roleSiteService.list(new LambdaQueryWrapper<TRoleSite>().eq(TRoleSite::getRoleId, roleId));
        List<Integer> collect = list.stream().map(TRoleSite::getSiteId).collect(Collectors.toList());
        return R.ok(collect);
@@ -45,7 +45,7 @@
     * @return
     */
    @ResponseBody
    @PostMapping("/getSiteIds")
    @PostMapping("/addRoleSite")
    public R addRoleSite(@RequestBody List<TRoleSite> roleSites){
        roleSiteService.saveBatch(roleSites);
        return R.ok();
ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/controller/TUserSiteController.java
@@ -33,8 +33,8 @@
     * @return
     */
    @ResponseBody
    @GetMapping("/getSiteIds")
    public R<List<Integer>> getSiteIds(Long userId){
    @PostMapping("/getSiteIds/{userId}")
    public R<List<Integer>> getSiteIds(@PathVariable Long userId){
        List<TUserSite> list = userSiteService.list(new LambdaQueryWrapper<TUserSite>().eq(TUserSite::getUserId, userId));
        List<Integer> collect = list.stream().map(TUserSite::getSiteId).collect(Collectors.toList());
        return R.ok(collect);
ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/controller/TVipController.java
@@ -45,25 +45,25 @@
    @Resource
    private TCouponService couponService;
    @PostMapping("/saveVip")
    @ApiOperation(value = "会员添加")
    @ApiOperation(tags = {"管理后台-会员管理"},value = "会员添加")
    public AjaxResult saveVip(@RequestBody TVip dto) {
        vipService.save(dto);
        return AjaxResult.success();
    }
    @GetMapping("/delete")
    @ApiOperation(value = "会员删除")
    @ApiOperation(tags = {"管理后台-会员管理"},value = "会员删除")
    public AjaxResult delete(Integer id) {
        vipService.removeById(id);
        return AjaxResult.success();
    }
    @PostMapping("/updateVip")
    @ApiOperation(value = "会员修改")
    @ApiOperation(tags = {"管理后台-会员管理"},value = "会员修改")
    public AjaxResult updateVip(@RequestBody TVip dto) {
            vipService.updateById(dto);
        return AjaxResult.success();
    }
    @GetMapping("/getInfo")
    @ApiOperation(value = "会员查看详情")
    @ApiOperation(tags = {"管理后台-会员管理"},value = "会员查看详情")
    public AjaxResult<TVip> getInfo(Integer id) {
        return AjaxResult.ok(vipService.getById(id));
    }
@@ -84,7 +84,7 @@
    public R<TVip> getInfo1(@RequestParam("id")Integer id) {
        return R.ok(vipService.getById(id));
    }
    @ApiOperation(value = "会员列表分页查询")
    @ApiOperation(tags = {"管理后台-会员管理"},value = "会员列表分页查询")
    @PostMapping(value = "/pageList")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "pageCurr", value = "分页参数,当前页码", required = true),
ruoyi-service/ruoyi-other/src/main/resources/bootstrap.yml
@@ -37,14 +37,12 @@
        # 共享配置
        shared-configs:
          - application-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension}
---
seata:
  enabled: true
  application-id: ${spring.application.name}
  tx-service-group: seata_tx_group    #此处配置自定义的seata事务分组名称
  enable-auto-data-source-proxy: true    #开启数据库代理
  enable-auto-data-source-proxy: true    #关闭数据库代理
  service:
    vgroup-mapping:
      seata_tx_group: default
ruoyi-service/ruoyi-other/src/main/resources/mapper/other/TCouponMapper.xml
@@ -7,6 +7,8 @@
        <id column="id" property="id" />
        <result column="type" property="type" />
        <result column="name" property="name" />
        <result column="cover_picture" property="coverPicture"/>
        <result column="details_picture" property="detailsPicture"/>
        <result column="preferential_mode" property="preferentialMode" />
        <result column="discount_amount" property="discountAmount" />
        <result column="meet_the_conditions" property="meetTheConditions" />
ruoyi-service/ruoyi-other/src/main/resources/mapper/other/TVipMapper.xml
@@ -32,12 +32,15 @@
        <result column="double_integration" property="doubleIntegration" />
        <result column="mall_exclusive_price" property="mallExclusivePrice" />
        <result column="create_time" property="createTime" />
        <result column="discount" property="discount" />
        <result column="type" property="type" />
        <result column="del_flag" property="delFlag" />
    </resultMap>
    <!-- 通用查询结果列 -->
    <sql id="Base_Column_List">
        id, name, reveal, monthly_card, monthly_card_reveal, monthly_card_discount, monthly_card_discount_start, monthly_card_discount_end, monthly_card_discount_reveal, season_card, season_card_reveal, season_card_discount, season_card_discount_start, season_card_discount_end, season_card_discount_reveal, annual_card, annual_card_reveal, annual_card_discount, annual_card_discount_start, annual_card_discount_end, annual_card_discount_reveal, discount_times, maximum_deduction, deduction_type, coupon, double_integration, mall_exclusive_price, create_time, del_flag
          ,discount, type
    </sql>
    <select id="pageList" resultType="com.ruoyi.other.api.domain.TVip">
        select * from t_vip
ruoyi-service/ruoyi-payment/pom.xml
@@ -100,7 +100,18 @@
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <!-- wx sdk -->
        <!-- https://mvnrepository.com/artifact/com.github.wechatpay-apiv3/wechatpay-apache-httpclient -->
        <dependency>
            <groupId>com.github.wechatpay-apiv3</groupId>
            <artifactId>wechatpay-apache-httpclient</artifactId>
            <version>0.4.3</version>
        </dependency>
        <dependency>
            <groupId>joda-time</groupId>
            <artifactId>joda-time</artifactId>
            <version>2.10.10</version>
        </dependency>
    </dependencies>
    <build>
ruoyi-service/ruoyi-payment/src/main/java/com/ruoyi/payment/wx/config/WxConfig.java
New file
@@ -0,0 +1,33 @@
package com.ruoyi.payment.wx.config;
import com.ruoyi.payment.wx.model.WeixinProperties;
import com.ruoyi.payment.wx.utils.WxV3Pay;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
 * 项目中需继承此类
 *
 * @author lihen
 */
@ConditionalOnProperty(name = "wx.conf.enabled")
@Configuration
public class WxConfig {
    private final WeixinProperties weixinProperties;
    @Autowired
    public WxConfig(WeixinProperties weixinProperties) {
        this.weixinProperties = weixinProperties;
    }
    @Bean
    @ConditionalOnMissingBean(name = "wxV3Pay")
    public WxV3Pay wxSpV3Pay() {
        return new WxV3Pay(weixinProperties);
    }
}
ruoyi-service/ruoyi-payment/src/main/java/com/ruoyi/payment/wx/controller/WxPayController.java
New file
@@ -0,0 +1,142 @@
package com.ruoyi.payment.wx.controller;
import com.fasterxml.jackson.core.type.TypeReference;
import com.ruoyi.common.core.web.domain.AjaxResult;
import com.ruoyi.payment.wx.enums.RefundEnum;
import com.ruoyi.payment.wx.model.WxPaymentRefundModel;
import com.ruoyi.payment.wx.utils.WxV3Pay;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.Map;
import java.util.Objects;
/**
 * 微信相关接口
 */
@Slf4j
@RestController
@CrossOrigin
@RequestMapping("/wx/")
@Api(tags = {"微信支付相关接口"})
public class WxPayController {
    @Autowired
    private WxV3Pay wxV3Pay;
    /**
     * 按实际修改
     */
    @PostMapping("order")
    @ApiOperation("订单支付")
    public AjaxResult<Map<String, Object>> orderPay(@RequestParam Long orderId) {
        // 查询订单
        // 0元订单不走支付
        // 价格
        Integer totalPrice = 0;
        // 生成订单号
        String orderNo = "";
        // 查询用户信息 用户openid
        String openId = "";
        // 订单做修改
        // 调用支付方法
        Map<String, Object> result = wxV3Pay.jsApi(orderNo, totalPrice, openId,"");
        log.info("支付参数:{}", result);
        return AjaxResult.ok(result);
    }
    /**
     * 微信v3支付-订单退款
     *
     * @return
     */
    @ApiOperation("订单退款")
    @PostMapping(value = "refund-order")
    public AjaxResult<String> refundOrder() {
         Map<String, Object> result = wxV3Pay.refund(new WxPaymentRefundModel());
         log.info("退款结果:{}", result);
        // 微信支付退款单号
        String refund_id = result.get("refund_id").toString();
        // 商户退款单号
        String out_refund_no = result.get("out_refund_no").toString();
        // 微信支付订单号
        String transaction_id = result.get("transaction_id").toString();
        // 商户订单号 tradeNo
        String out_trade_no = result.get("out_trade_no").toString();
        // 退款成功时间
        String success_time = Objects.nonNull(result.get("success_time")) ? result.get("success_time").toString() : null;
        // 退款状态 RefundEnum
        String status = result.get("status").toString();
        // TODO 退款业务处理
        return AjaxResult.success();
    }
    /**
     * 支付回调
     */
    @PostMapping("pay/notify")
    public void payNotify(HttpServletRequest request) throws IOException {
        try {
            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 业务处理
        } catch (Exception e) {
            log.error("支付回调异常:{}", e, e);
            wxV3Pay.ack(false, e.getMessage());
        }
    }
    /**
     * 退款回调
     */
    @PostMapping("refund/notify")
    public void refundNotify(HttpServletRequest request) throws IOException {
        try {
            Map<String, Object> params = wxV3Pay.verifyNotify(request, new TypeReference<Map<String, Object>>() {
            });
            // 商户订单号
            String tradeNo = params.get("out_trade_no").toString();
            // 商户退款单号
            String out_refund_no = params.get("out_refund_no").toString();
            // 微信支付订单号
            String transaction_id = params.get("transaction_id").toString();
            // 微信支付退款单号
            String refund_id = params.get("refund_id").toString();
            // 退款状态
            String tradeState = params.get("refund_status").toString();
            // 退款成功时间
            // 时间不对的话,可以调用  WxTimeUtils.toRfc3339Date(success_time)转换一下
            String success_time = params.get("success_time").toString();
            if (tradeState.equals(RefundEnum.SUCCESS.name())) {
                // TODO 退款成功处理
                wxV3Pay.ack();
            } else {
                wxV3Pay.ack(false, "不是成功的退款状态");
            }
        } catch (Exception e) {
            wxV3Pay.ack(false, e.getMessage());
        }
    }
}
ruoyi-service/ruoyi-payment/src/main/java/com/ruoyi/payment/wx/enums/RefundEnum.java
New file
@@ -0,0 +1,53 @@
package com.ruoyi.payment.wx.enums;
import lombok.Getter;
import java.util.stream.Stream;
/**
 * @author xiaochen
 * @ClassName ProfitSharingEnum
 * @Description
 * @date 2021-11-21 11:15
 */
public enum RefundEnum {
    /**
     * 退款成功
     */
    SUCCESS("SUCCESS", "退款成功"),
    /**
     * 退款关闭
     */
    CLOSED("CLOSED", "退款关闭"),
    /**
     * 退款处理中
     */
    PROCESSING("PROCESSING", "退款处理中"),
    /**
     * 退款异常
     */
    ABNORMAL("ABNORMAL", "退款异常"),
    ;
    @Getter
    private String code;
    @Getter
    private String desc;
    RefundEnum(String code, String desc) {
        this.code = code;
        this.desc = desc;
    }
    /**
     * 通过交易类型执行具体的交易方法
     *
     * @param code
     * @return
     */
    public static RefundEnum fromValue(String code) {
        return Stream.of(RefundEnum.values()).filter(fileType ->
                fileType.getCode().toLowerCase().equals(code.toLowerCase())
        ).findFirst().orElse(null);
    }
}
ruoyi-service/ruoyi-payment/src/main/java/com/ruoyi/payment/wx/enums/TradeStateEnum.java
New file
@@ -0,0 +1,41 @@
package com.ruoyi.payment.wx.enums;
import lombok.Getter;
import java.util.stream.Stream;
/**
 * @author xiaochen
 * @ClassName AliTradeStateEnum
 * @Description
 * @date 2022-01-07 11:56
 */
public enum TradeStateEnum {
    SUCCESS("支付成功"),
    RETURN("已分账回退"),
    FAIL("已失败"),
    PROCESSING("处理中"),
    FINISHED("分账完成"),
    REFUND("转入退款"),
    PAYERROR("支付失败(其他原因,如银行返回失败)"),
    USERPAYING("用户支付中"),
    CLOSED("已关闭"),
    NOTPAY("未支付"),
    UNKNOWN("未知"),
    DONE("服务订单完成"),
    // ...
    ;
    @Getter
    private String desc;
    TradeStateEnum(String desc) {
        this.desc = desc;
    }
    public static TradeStateEnum tradeState(String code) {
        return Stream.of(TradeStateEnum.values()).filter(fileType ->
                fileType.name().equals(code)
        ).findFirst().orElse(UNKNOWN);
    }
}
ruoyi-service/ruoyi-payment/src/main/java/com/ruoyi/payment/wx/exception/WxException.java
New file
@@ -0,0 +1,55 @@
package com.ruoyi.payment.wx.exception;
/**
 * @author lihen
 */
public class WxException extends RuntimeException {
    private final static int OK = 0;
    private final static int ValidateSignatureError = -40001;
    private final static int ParseXmlError = -40002;
    public final static int ComputeSignatureError = -40003;
    private final static int IllegalAesKey = -40004;
    private final static int ValidateAppidError = -40005;
    private final static int EncryptAESError = -40006;
    private final static int DecryptAESError = -40007;
    private final static int IllegalBuffer = -40008;
    private int code;
    private static String getMessage(int code) {
        switch (code) {
            case ValidateSignatureError:
                return "签名验证错误";
            case ParseXmlError:
                return "xml解析失败";
            case ComputeSignatureError:
                return "sha加密生成签名失败";
            case IllegalAesKey:
                return "SymmetricKey非法";
            case ValidateAppidError:
                return "appid校验失败";
            case EncryptAESError:
                return "aes加密失败";
            case DecryptAESError:
                return "aes解密失败";
            case IllegalBuffer:
                return "解密后得到的buffer非法";
            default:
                return null;
        }
    }
    public int getCode() {
        return code;
    }
    public WxException(int code) {
        super(getMessage(code));
        this.code = code;
    }
    public WxException(String message) {
        super(message);
    }
}
ruoyi-service/ruoyi-payment/src/main/java/com/ruoyi/payment/wx/model/V3.java
New file
@@ -0,0 +1,71 @@
package com.ruoyi.payment.wx.model;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.util.IOUtils;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
/**
 * @author xiaochen
 * @ClassName V3
 * @Description
 */
@Slf4j
@Data
public class V3 {
    /**
     * 获取 API 密钥
     *
     * @return API密钥
     */
    private String apiKey;
    /**
     * 秘钥路径,apiclient_key.pem
     */
    private String privateKeyPath;
    /**
     * 商户证书序列号
     */
    private String  mchSerialNo;
    /**
     * 支付回调地址
     *
     * @return
     */
    private String notifyPayUrl;
    /**
     * 退款回调地址
     *
     * @return
     */
    private String notifyRefundUrl;
    /**
     * 退款回调地址
     */
    private String notifyTravelRefundUrl;
    public InputStream getPrivateKeyStream() {
        // 需要证书释放
        byte[] certData;
        InputStream certStream = Thread.currentThread().getContextClassLoader().getResourceAsStream(this.privateKeyPath);
        try {
            certData = IOUtils.toByteArray(certStream);
        } catch (IOException e) {
            throw new RuntimeException("私钥文件未找到");
        }finally {
            try {
                certStream.close();
            } catch (IOException e) {
                log.error("私钥流关闭异常");
            }
        }
        return new ByteArrayInputStream(certData);
    }
}
ruoyi-service/ruoyi-payment/src/main/java/com/ruoyi/payment/wx/model/WeixinProperties.java
New file
@@ -0,0 +1,101 @@
package com.ruoyi.payment.wx.model;
import lombok.ToString;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.NestedConfigurationProperty;
import org.springframework.stereotype.Component;
/**
 * @author xiaochen
 * @ClassName WeixinProperties
 * @Description
 */
@ToString
@Component
@ConfigurationProperties(prefix = "wx.conf")
public class WeixinProperties {
    /**
     * 默认开启
     */
    private boolean enabled = true;
    /**
     * 获取 App ID
     *
     * @return App ID
     */
    private String appId;
    /**
     * 获取 Mch ID
     *
     * @return Mch ID
     */
    private String mchId;
    /**
     * 获取 secret ID
     *
     * @return secret ID
     */
    private String secretId;
    public String getSecretId() {
        return secretId;
    }
    public void setSecretId(String secretId) {
        this.secretId = secretId;
    }
    /**
     * v3
     */
    @NestedConfigurationProperty
    private V3 v3;
    public boolean isEnabled() {
        return enabled;
    }
    public void setEnabled(boolean enabled) {
        this.enabled = enabled;
    }
    public V3 getV3() {
        return v3;
    }
    public void setV3(V3 v3) {
        this.v3 = v3;
    }
    /**
     * HTTP(S) 连接超时时间,单位毫秒
     *
     */
    public int getHttpConnectTimeoutMs() {
        return 6 * 1000;
    }
    /**
     * HTTP(S) 读数据超时时间,单位毫秒
     */
    public int getHttpReadTimeoutMs() {
        return 8 * 1000;
    }
    public String getAppId() {
        return appId;
    }
    public void setAppId(String appId) {
        this.appId = appId;
    }
    public String getMchId() {
        return mchId;
    }
    public void setMchId(String mchId) {
        this.mchId = mchId;
    }
}
ruoyi-service/ruoyi-payment/src/main/java/com/ruoyi/payment/wx/model/WxPaymentInfoModel.java
New file
@@ -0,0 +1,201 @@
package com.ruoyi.payment.wx.model;
import lombok.*;
import java.util.List;
/**
 * @author xiaochen
 * @ClassName WxPaymentInfoModel
 * @Description
 */
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Getter
@Setter
@ToString
public class WxPaymentInfoModel {
    /**
     * 合单商户appid
     */
    private String combine_appid;
    /**
     * 合单商户号
     */
    private String combine_mchid;
    /**
     * 合单商户订单号
     */
    private String combine_out_trade_no;
    /**
     * 合单--子单信息
     */
    private List<SubOrders> sub_orders;
    /**
     * 合单--支付者
     */
    private CombinePayerInfo combine_payer_info;
    private String appid;
    private String sp_appid;
    private String mchid;
    private String sp_mchid;
    private String sub_appid;
    private String sub_mchid;
    private String description;
    private String out_trade_no;
    private String time_expire;
    private String attach;
    private String notify_url;
    private String goods_tag;
    private SettleInfo settle_info;
    private Amount amount;
    private Payer payer;
    private Detail detail;
    private SceneInfo scene_info;
    @Builder
    @AllArgsConstructor
    @NoArgsConstructor
    @Getter
    @Setter
    @ToString
    public static class SettleInfo {
        private Boolean profit_sharing;
        private Integer subsidy_amount;
    }
    @Builder
    @AllArgsConstructor
    @NoArgsConstructor
    @Getter
    @Setter
    @ToString
    public static class Amount {
        private Integer total;
        /**
         * 合单支付时需要
         */
        private Integer total_amount;
        @Builder.Default
        private String currency = "CNY";
    }
    @Builder
    @AllArgsConstructor
    @NoArgsConstructor
    @Getter
    @Setter
    @ToString
    public static class Payer {
        private String openid;
        private String sp_openid;
        private String sub_openid;
    }
    @Builder
    @AllArgsConstructor
    @NoArgsConstructor
    @Getter
    @Setter
    @ToString
    public static class Detail {
        private int cost_price;
        private String invoice_id;
        private List<GoodsDetail> goods_detail;
        @Builder
        @AllArgsConstructor
        @NoArgsConstructor
        @Getter
        @Setter
        @ToString
        public static class GoodsDetail {
            private String merchant_goods_id;
            private String wechatpay_goods_id;
            private String goods_name;
            private int quantity;
            private int unit_price;
        }
    }
    @Builder
    @AllArgsConstructor
    @NoArgsConstructor
    @Getter
    @Setter
    @ToString
    public static class SceneInfo {
        private String payer_client_ip;
        private String device_id;
        private StoreInfo store_info;
        private H5Info h5_info;
        @Builder
        @AllArgsConstructor
        @NoArgsConstructor
        @Getter
        @Setter
        @ToString
        public static class StoreInfo {
            private String id;
            private String name;
            private String area_code;
            private String address;
        }
        @Builder
        @AllArgsConstructor
        @NoArgsConstructor
        @Getter
        @Setter
        @ToString
        public static class H5Info {
            private String type;
            private String app_name;
            private String app_url;
            private String bundle_id;
            private String package_name;
        }
    }
    @Builder
    @AllArgsConstructor
    @NoArgsConstructor
    @Getter
    @Setter
    @ToString
    public static class SubOrders {
        private String out_trade_no;
        private Amount amount;
        private String mchid;
        private String sub_mchid;
        private String attach;
        private String description;
        private String goods_tag;
        private SettleInfo settle_info;
    }
    @Builder
    @AllArgsConstructor
    @NoArgsConstructor
    @Getter
    @Setter
    @ToString
    public static class CombinePayerInfo {
        private String openid;
    }
}
ruoyi-service/ruoyi-payment/src/main/java/com/ruoyi/payment/wx/model/WxPaymentRefundModel.java
New file
@@ -0,0 +1,84 @@
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 WxPaymentRefundModel {
    /**
     * 子商户,二级商户号
     */
    private String sub_mchid;
    /**
     * 电商平台APPID
     */
    private String sp_appid;
    private String transaction_id;
    private String out_trade_no;
    /**
     * 商户退款单号
     */
    private String out_refund_no;
    /**
     * 退款原因
     */
    private String reason;
    private String notify_url;
    /**
     * 资金账户,否
     */
    private String funds_account;
    /**
     * 退款金额信息
     */
    private RefundAmount amount;
    /**
     * 退款商品
     */
    private List<RefundGoodsDetail> goods_detail;
    @Builder
    @AllArgsConstructor
    @NoArgsConstructor
    @Getter
    @Setter
    @ToString
    public static class RefundAmount {
        /**
         * 原订单金额
         */
        private int total;
        @Builder.Default
        private String currency = "CNY";
        /**
         * 退款金额
         */
        private int refund;
    }
    @Builder
    @AllArgsConstructor
    @NoArgsConstructor
    @Getter
    @Setter
    @ToString
    public static class RefundGoodsDetail {
        private String merchant_goods_id;
        private String wechatpay_goods_id;
        private String goods_name;
        private int unit_price;
        private int refund_amount;
        private int refund_quantity;
    }
}
ruoyi-service/ruoyi-payment/src/main/java/com/ruoyi/payment/wx/pojo/AppletUserDecodeData.java
New file
@@ -0,0 +1,52 @@
package com.ruoyi.payment.wx.pojo;
import lombok.Data;
/**
 * @author xiaochen
 * @ClassName AppletUserDecodeData
 * @Description
 * 用户主体信息部分
 * {
 *     "openId": "OPENID",
 *     "nickName": "NICKNAME",
 *     "gender": GENDER,
 *     "city": "CITY",
 *     "province": "PROVINCE",
 *     "country": "COUNTRY",
 *     "avatarUrl": "AVATARURL",
 *     "unionId": "UNIONID",
 *     "watermark":
 *     {
 *         "appid":"APPID",
 *         "timestamp":TIMESTAMP
 *     }
 * }
 * 电话部分
 * {
 *     "phoneNumber": "13580006666",
 *     "purePhoneNumber": "13580006666",
 *     "countryCode": "86",
 *     "watermark":
 *     {
 *         "appid":"APPID",
 *         "timestamp": TIMESTAMP
 *     }
 * }
 *
 */
@Data
public class AppletUserDecodeData {
    private String openId;
    private String unionId;
    private String nickName;
    private int gender;
    private String city;
    private String province;
    private String country;
    private String avatarUrl;
    private Watermark watermark;
    private String phoneNumber;
    private String purePhoneNumber;
    private String countryCode;
}
ruoyi-service/ruoyi-payment/src/main/java/com/ruoyi/payment/wx/pojo/Watermark.java
New file
@@ -0,0 +1,9 @@
package com.ruoyi.payment.wx.pojo;
import lombok.Data;
@Data
public class Watermark {
    private String appid;
    private String timestamp;
}
ruoyi-service/ruoyi-payment/src/main/java/com/ruoyi/payment/wx/resp/NotifyV3PayDecodeRespBody.java
New file
@@ -0,0 +1,222 @@
package com.ruoyi.payment.wx.resp;
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-service/ruoyi-payment/src/main/java/com/ruoyi/payment/wx/utils/SHA1.java
New file
@@ -0,0 +1,39 @@
package com.ruoyi.payment.wx.utils;
import com.ruoyi.payment.wx.exception.WxException;
import java.security.MessageDigest;
public class SHA1 {
    /**
     * 用SHA1算法生成安全签名
     *
     * @param str
     * @return
     * @throws WxException
     */
    public static String getSHA1(String str) throws WxException {
        try {
            // SHA1签名生成
            MessageDigest md = MessageDigest.getInstance("SHA-1");
            md.update(str.getBytes());
            byte[] digest = md.digest();
            StringBuffer hexstr = new StringBuffer();
            String shaHex;
            for (int i = 0; i < digest.length; i++) {
                shaHex = Integer.toHexString(digest[i] & 0xFF);
                if (shaHex.length() < 2) {
                    hexstr.append(0);
                }
                hexstr.append(shaHex);
            }
            return hexstr.toString();
        } catch (Exception e) {
            throw new WxException(WxException.ComputeSignatureError);
        }
    }
}
ruoyi-service/ruoyi-payment/src/main/java/com/ruoyi/payment/wx/utils/WxAbstractPay.java
New file
@@ -0,0 +1,333 @@
package com.ruoyi.payment.wx.utils;
import com.fasterxml.jackson.core.type.TypeReference;
import com.ruoyi.common.core.utils.WebUtils;
import com.ruoyi.payment.wx.model.WxPaymentInfoModel;
import com.ruoyi.payment.wx.model.WxPaymentRefundModel;
import com.ruoyi.payment.wx.resp.NotifyV3PayDecodeRespBody;
import com.wechat.pay.contrib.apache.httpclient.auth.PrivateKeySigner;
import com.wechat.pay.contrib.apache.httpclient.auth.Verifier;
import com.wechat.pay.contrib.apache.httpclient.notification.Notification;
import com.wechat.pay.contrib.apache.httpclient.notification.NotificationHandler;
import com.wechat.pay.contrib.apache.httpclient.notification.NotificationRequest;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.*;
import org.apache.http.conn.ConnectTimeoutException;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.util.EntityUtils;
import org.springframework.util.StringUtils;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.net.SocketTimeoutException;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import static com.wechat.pay.contrib.apache.httpclient.constant.WechatPayHttpHeaders.*;
/**
 * @author xiaochen
 * @ClassName WxWifiV3Pay
 * @Description
 * @date 2021-11-13 21:10
 */
@Slf4j
public abstract class WxAbstractPay {
    /**
     * 请求成功相应码
     */
    private static final int STATUS_CODE = 200;
    /**
     * 请求成功相应码
     */
    private static final int OTHER_STATUS_CODE = 204;
    /**
     * 请求根地址
     */
    private static final String HOST = "https://api.mch.weixin.qq.com";
    private static RuntimeException parameterError(String message, Object... args) {
        message = String.format(message, args);
        return new IllegalArgumentException("parameter error: " + message);
    }
    /**
     * 封装基础数据
     *
     * @param requestBody
     * @param notifyUrl
     * @return
     */
    protected String buildBaseParam(WxPaymentInfoModel requestBody, String notifyUrl) {
        // 封装基础数据
        requestBody.setNotify_url(notifyUrl);
        String reqBody = WxJsonUtils.toJsonString(requestBody);
        return reqBody;
    }
    /**
     * 微信调起支付参数
     * 返回参数如有不理解 请访问微信官方文档
     * https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter4_1_4.shtml
     *
     * @param prepayId 微信下单返回的prepay_id
     * @param appId    应用ID(appid)
     * @return 当前调起支付所需的参数
     * @throws Exception
     */
    protected Map<String, Object> wxTuneUp(PrivateKeySigner privateKeySigner, String appId, String prepayId) {
        String timeStamp = String.valueOf(System.currentTimeMillis() / 1000);
        String nonceStr = WxUtils.generateNonceStr();
        String packageStr = "prepay_id=" + prepayId;
        //加载签名
        String signStr = Stream.of(appId, timeStamp, nonceStr, packageStr).collect(Collectors.joining("\n", "", "\n"));
        String packageSign = privateKeySigner.sign(signStr.getBytes(StandardCharsets.UTF_8)).getSign();
        Map<String, Object> map = new HashMap<>(6);
        map.put("appId", appId);
        map.put("timeStamp", timeStamp);
        map.put("nonceStr", nonceStr);
        map.put("package", packageStr);
        map.put("signType", "RSA");
        map.put("paySign", packageSign);
        return map;
    }
    /**
     * 构建方法请求
     *
     * @param uri
     * @param socketTimeout
     * @param connectTimeout
     * @return
     */
    protected HttpGet requestGet(String uri, int socketTimeout, int connectTimeout) {
        //请求URL
        HttpGet httpGet = new HttpGet(HOST + uri);
        RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(socketTimeout)
                .setConnectTimeout(connectTimeout).build();
        httpGet.setConfig(requestConfig);
        httpGet.setHeader("Content-type", "application/json");
        httpGet.setHeader("Accept", "application/json");
        return httpGet;
    }
    /**
     * 构建方法请求
     *
     * @param uri
     * @param socketTimeout
     * @param connectTimeout
     * @param reqdata
     * @return
     */
    protected HttpPost requestPost(String uri, int socketTimeout, int connectTimeout, String reqdata) {
        //请求URL
        HttpPost httpPost = new HttpPost(HOST + uri);
        RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(socketTimeout)
                .setConnectTimeout(connectTimeout).build();
        httpPost.setConfig(requestConfig);
        StringEntity entity = new StringEntity(reqdata, StandardCharsets.UTF_8);
        entity.setContentType("application/json");
        httpPost.setEntity(entity);
        httpPost.setHeader("Accept", "application/json");
        return httpPost;
    }
    public abstract <T> T verifyNotify(HttpServletRequest request, TypeReference<T> valueTypeRef) throws Exception;
    /**
     * 接收回调
     *
     * @param request
     * @return
     * @throws Exception
     */
    public <T> T verifyNotify(HttpServletRequest request, Verifier verifier, String apiKey, TypeReference<T> valueTypeRef) throws Exception {
        String body = WxUtils.streamBodyByReceive(request);
        String requestId = request.getHeader(REQUEST_ID);
        String[] headers = {WECHAT_PAY_SERIAL, WECHAT_PAY_SIGNATURE, WECHAT_PAY_NONCE, WECHAT_PAY_TIMESTAMP};
        String value;
        // 验签必须参数检验
        for (String headerName : headers) {
            value = request.getHeader(headerName);
            if (value == null || "".equals(value)) {
                throw parameterError("empty [%s], request-id=[%s]", headerName, requestId);
            }
        }
        String serial = request.getHeader(WECHAT_PAY_SERIAL);
        String signature = request.getHeader(WECHAT_PAY_SIGNATURE);
        String timestamp = request.getHeader(WECHAT_PAY_TIMESTAMP);
        String nonce = request.getHeader(WECHAT_PAY_NONCE);
        // 构建request,传入必要参数
        NotificationRequest notificationRequest = new NotificationRequest.Builder().withSerialNumber(serial)
                .withNonce(nonce)
                .withTimestamp(timestamp)
                .withSignature(signature)
                .withBody(body)
                .build();
        NotificationHandler handler = new NotificationHandler(verifier, apiKey.getBytes(StandardCharsets.UTF_8));
        // 验签和解析请求体
        Notification notification = handler.parse(notificationRequest);
        assert notification != null;
        T respBody = WxJsonUtils.parseObject(notification.getDecryptData(), valueTypeRef);
        return respBody;
    }
    /**
     * 订单查询
     *
     * @param httpClient
     * @param socketTimeout
     * @param connectTimeout
     * @param url
     * @return com.abl.biz.center.payment.wx.v3.NotifyV3PayDecodeRespBody
     * @author xiaochen
     * @date 2021-12-20 17:12
     */
    protected NotifyV3PayDecodeRespBody query(CloseableHttpClient httpClient, int socketTimeout, int connectTimeout, String url) {
        //请求URL
        HttpGet httpGet = requestGet(
                url
                , socketTimeout
                , connectTimeout);
        String repBody = result(httpClient, httpGet);
        NotifyV3PayDecodeRespBody body = WxJsonUtils.parseObject(repBody, NotifyV3PayDecodeRespBody.class);
        return body;
    }
    /**
     * 子级实现
     *
     * @param out_trade_no
     * @param mchid
     * @return
     */
    public abstract NotifyV3PayDecodeRespBody query(String out_trade_no, String mchid);
    /**
     * 订单退款
     *
     * @param refundModel
     * @return
     */
    public abstract Map<String, Object> refund(WxPaymentRefundModel refundModel);
    /**
     * 订单退款
     *
     * @param httpClient
     * @param uri
     * @param httpReadTimeoutMs
     * @param httpConnectTimeoutMs
     * @param refundModel
     * @return
     */
    public Map<String, Object> refund(CloseableHttpClient httpClient,
                                      String uri,
                                      int httpReadTimeoutMs,
                                      int httpConnectTimeoutMs,
                                      WxPaymentRefundModel refundModel) {
        String reqBody = WxJsonUtils.toJsonString(refundModel);
        //请求URL
        HttpEntityEnclosingRequestBase httpPost = requestPost(
                uri
                , httpReadTimeoutMs
                , httpConnectTimeoutMs, reqBody);
        String repBody = result(httpClient, httpPost);
        Map<String, Object> body = WxJsonUtils.parseObject(repBody, Map.class);
        return body;
    }
    /**
     * 请求结果
     *
     * @param request
     * @return
     */
    protected String result(CloseableHttpClient httpClient, HttpRequestBase request) {
        CloseableHttpResponse response = null;
        try {
            response = httpClient.execute(request);
            int statusCode = response.getStatusLine().getStatusCode();
            String respBodyStr = EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8);
            if (WxUtils.getLogger().isDebugEnabled()) {
                WxUtils.debug("请求成功:{}", respBodyStr);
            }
            // 成功相应
            if (STATUS_CODE == statusCode || OTHER_STATUS_CODE == statusCode) {
                return respBodyStr;
            } else {
                WxUtils.error("failed,resp code = {},return body = {}", statusCode, respBodyStr);
                throw new RuntimeException(respBodyStr);
            }
        } catch (ConnectTimeoutException e) {
            e.printStackTrace();
            throw new RuntimeException("接口超时");
        } catch (SocketTimeoutException e) {
            e.printStackTrace();
            throw new RuntimeException("读取接口数据超时");
        } catch (IOException e) {
            e.printStackTrace();
            throw new RuntimeException("接口请求失败,请尝试检查网络环境或请求接口是否能正常访问");
        } finally {
            // 关闭响应
            try {
                if (response != null) {
                    //关闭结果集
                    response.getEntity().getContent().close();
                    response.close();
                }
            } catch (IOException e) {
                throw new RuntimeException("关闭流异常");
            }
        }
    }
    /**
     * 微信结果确认应答
     * 支付通知http应答码为200或204才会当作正常接收,当回调处理异常时,应答的HTTP状态码应为500,或者4xx。
     *
     * @param
     * @throws IOException
     */
    public void ack() throws IOException {
        ack(true, null);
    }
    /**
     * 微信结果确认应答
     * 支付通知http应答码为200或204才会当作正常接收,当回调处理异常时,应答的HTTP状态码应为500,或者4xx。
     *
     * @param
     * @throws IOException
     */
    public void ack(boolean ackSucc, String erroMsg) throws IOException {
        HttpServletResponse response = WebUtils.response();
        PrintWriter writer = response.getWriter();
        if (ackSucc) {
            log.info("响应微信回调成功!");
            response.setStatus(200);
            writer.write("{\"code\": \"SUCCESS\",\"message\": \"成功\"}");
        } else {
            log.info("响应微信回调失败:{}!", erroMsg);
            response.setStatus(500);
            writer.write("{\"code\": \"FAIL\",\"message\": " + (StringUtils.hasLength(erroMsg) ? erroMsg : "业务处理失败") + "}");
        }
        // 关闭流
        if (Objects.nonNull(writer)) {
            writer.close();
        }
    }
}
ruoyi-service/ruoyi-payment/src/main/java/com/ruoyi/payment/wx/utils/WxJsonUtils.java
New file
@@ -0,0 +1,73 @@
package com.ruoyi.payment.wx.utils;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import lombok.extern.slf4j.Slf4j;
/**
 * Json转换工具类
 *
 * @author madman
 */
@Slf4j
public final class WxJsonUtils {
    private static final ObjectMapper OM = new ObjectMapper();
    private static final JavaTimeModule timeModule = new JavaTimeModule();
    /**
     * 设置 ObjectMapper
     *
     * @return
     */
    private static ObjectMapper getObjectMapper() {
        // 允许对象忽略json中不存在的属性
        OM.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        OM.registerModule(timeModule);
        return OM;
    }
    /**
     * 将对象序列化
     */
    public static <T> String toJsonString(T obj) {
        try {
            ObjectMapper om = getObjectMapper();
            // 忽略空值
            om.setSerializationInclusion(JsonInclude.Include.NON_EMPTY);
            return om.writeValueAsString(obj);
        } catch (JsonProcessingException e) {
            log.error("转json字符串失败:{}", obj);
            return null;
        }
    }
    /**
     * 反序列化对象字符串
     */
    public static <T> T parseObject(String json, Class<T> clazz) {
        try {
            ObjectMapper om = getObjectMapper();
            return om.readValue(json, clazz);
        } catch (JsonProcessingException e) {
            throw new RuntimeException("反序列化对象字符串失败");
        }
    }
    /**
     * 反序列化字符串成为对象
     */
    public static <T> T parseObject(String json, TypeReference<T> valueTypeRef) {
        try {
            ObjectMapper om = getObjectMapper();
            return om.readValue(json, valueTypeRef);
        } catch (JsonProcessingException e) {
            throw new RuntimeException("反序列化字符串成为对象失败");
        }
    }
}
ruoyi-service/ruoyi-payment/src/main/java/com/ruoyi/payment/wx/utils/WxTimeUtils.java
New file
@@ -0,0 +1,164 @@
package com.ruoyi.payment.wx.utils;
import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
import org.springframework.util.StringUtils;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Date;
import java.util.Objects;
import java.util.TimeZone;
/**
 * @author xiaochen
 * @ClassName WxTimeUtils
 * @Description
 * @date 2021-12-16 16:07
 */
public class WxTimeUtils {
    /**
     * 系统默认时区
     */
    private static final ZoneId ZONE = ZoneId.systemDefault();
    /**
     * yyyy-MM-dd'T'HH:mm:ssxxx 比如:2020-05-23T17:06:30+08:00 0时区时末尾 为+00:00
     */
    public static final DateTimeFormatter YYYY_MM_DD_T_HH_MM_SS_XXX_FMT = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ssxxx");
    /**
     * yyyy-MM-dd HH:mm:ss 比如:2020-05-23 17:06:30
     */
    public static final DateTimeFormatter YYYY_MM_DD_HH_MM_SS_FMT = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").withZone(ZONE);
    /**
     * 时间转 TimeZone
     *
     * @param date
     * @return
     * @throws Exception
     */
    public static String dateToTimeZone(Date date) throws Exception {
        String time;
        if (date == null) {
            throw new Exception("date is not null");
        }
        ZonedDateTime zonedDateTime = toZonedDateTime(date);
        time = format(zonedDateTime, YYYY_MM_DD_T_HH_MM_SS_XXX_FMT);
        return time;
    }
    /**
     * Date转ZonedDateTime,时区为系统默认时区
     *
     * @param date Date
     * @return ZonedDateTime
     */
    public static ZonedDateTime toZonedDateTime(Date date) {
        Objects.requireNonNull(date, "date");
        return Instant.ofEpochMilli(date.getTime()).atZone(ZoneId.systemDefault());
    }
    /**
     * 根据 formatter格式化 zonedDateTime
     *
     * @param zonedDateTime ZonedDateTime
     * @param formatter     DateTimeFormatter
     * @return String
     */
    public static String format(ZonedDateTime zonedDateTime, DateTimeFormatter formatter) {
        Objects.requireNonNull(zonedDateTime, "zonedDateTime");
        Objects.requireNonNull(formatter, "formatter");
        return zonedDateTime.format(formatter);
    }
    /**
     * TimeZone 时间转标准时间
     *
     * @param date
     * @return
     * @throws Exception
     */
    public static String toTimeZoneStr(String date) {
        String time;
        if (!StringUtils.hasLength(date)) {
            throw new RuntimeException("str is not null");
        }
        ZonedDateTime zonedDateTime = parseToZonedDateTime(date, YYYY_MM_DD_T_HH_MM_SS_XXX_FMT);
        if (zonedDateTime == null) {
            throw new RuntimeException("str to zonedDateTime fail");
        }
        time = zonedDateTime.format(YYYY_MM_DD_HH_MM_SS_FMT);
        return time;
    }
    /**
     * 转date
     *
     * @param date
     * @return
     * @throws Exception
     */
    public static Date toDate(String date) {
        String time;
        if (!StringUtils.hasLength(date)) {
            throw new RuntimeException("str is not null");
        }
        ZonedDateTime zonedDateTime = parseToZonedDateTime(date, YYYY_MM_DD_T_HH_MM_SS_XXX_FMT);
        if (zonedDateTime == null) {
            throw new RuntimeException("str to zonedDateTime fail");
        }
        return Date.from(zonedDateTime.toInstant());
    }
    /**
     * str --> Date
     *
     * @param date
     * @return java.util.Date
     * @author xiaochen
     * @date 2022-01-20 18:20
     */
    public static Date toRfc3339Date(String date) {
        DateTime dt2 = new DateTime(date);
        return dt2.toDate();
    }
    /**
     * 将 Date 转为 LocalDateTime
     *
     * @param date
     * @return java.time.LocalDateTime;
     */
    public static LocalDateTime dateToLocalDateTime(Date date) {
        return date.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();
    }
    /**
     * str --> Date
     *
     * @param date
     * @return java.util.Date
     * @author xiaochen
     * @date 2022-01-20 18:20
     */
    public static String toRfc3339Str(Date date) {
        DateTime dt1 = new DateTime(new Date(), DateTimeZone.forTimeZone(TimeZone.getTimeZone("Asia/Shanghai")));
        return dt1.toString();
    }
    /**
     * 根据 formatter解析为 ZonedDateTime
     *
     * @param text      待解析字符串
     * @param formatter DateTimeFormatter
     * @return ZonedDateTime
     */
    public static ZonedDateTime parseToZonedDateTime(String text, DateTimeFormatter formatter) {
        return ZonedDateTime.parse(text, formatter);
    }
}
ruoyi-service/ruoyi-payment/src/main/java/com/ruoyi/payment/wx/utils/WxUtils.java
New file
@@ -0,0 +1,217 @@
package com.ruoyi.payment.wx.utils;
import com.ruoyi.payment.wx.pojo.AppletUserDecodeData;
import lombok.extern.slf4j.Slf4j;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.util.encoders.Base64;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.security.AlgorithmParameters;
import java.security.SecureRandom;
import java.security.Security;
import java.util.Arrays;
import java.util.Random;
/**
 * @Description 获取用户信息工具类
 * @Author xiaochen
 * @Date 2021/8/12 15:45
 */
@Slf4j
public class WxUtils {
    /**
     * 随机字符
     */
    private static final String SYMBOLS = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
    private static final Random RANDOM = new SecureRandom();
    /**
     * 微信小程序API 用户数据的解密
     * @param encryptedData
     * @param sessionKey
     * @param iv
     * @return
     */
    public static AppletUserDecodeData encryptedData(String encryptedData, String sessionKey, String iv) {
        // 被加密的数据
        byte[] dataByte = Base64.decode(encryptedData);
        // 加密秘钥
        byte[] keyByte = Base64.decode(sessionKey);
        // 偏移量
        byte[] ivByte = Base64.decode(iv);
        try {
            // 如果密钥不足16位,那么就补足.  这个if 中的内容很重要
            int base = 16;
            if (keyByte.length % base != 0) {
                int groups = keyByte.length / base + (keyByte.length % base != 0 ? 1 : 0);
                byte[] temp = new byte[groups * base];
                Arrays.fill(temp, (byte) 0);
                System.arraycopy(keyByte, 0, temp, 0, keyByte.length);
                keyByte = temp;
            }
            // 初始化
            Security.addProvider(new BouncyCastleProvider());
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding", "BC");
            SecretKeySpec spec = new SecretKeySpec(keyByte, "AES");
            AlgorithmParameters parameters = AlgorithmParameters.getInstance("AES");
            parameters.init(new IvParameterSpec(ivByte));
            cipher.init(Cipher.DECRYPT_MODE, spec, parameters);
            byte[] resultByte = cipher.doFinal(dataByte);
            if (null != resultByte && resultByte.length > 0) {
                String result = new String(resultByte, "UTF-8");
                log.info("解密原串:{}",result);
                return WxJsonUtils.parseObject(result, AppletUserDecodeData.class);
            }
            throw new RuntimeException("解密的数据为空");
        } catch (Exception e) {
            log.error("解密失败. error = {}", e.getMessage(), e);
            throw new RuntimeException(e.getMessage());
        }
    }
    /**
     * 微信小程序API 用户数据的签名验证
     * signature = sha1( rawData + session_key )
     *
     * @param rawData    不包括敏感信息的原始数据字符串,用于计算签名。
     * @param sessionKey
     */
    public static void verifySignature(String rawData, String sessionKey, String signature) {
        String serverSignature = SHA1.getSHA1(rawData + sessionKey);
        System.out.println(rawData + sessionKey);
        log.info(rawData + ">>>>>>:" + sessionKey + " === " + serverSignature + "  ======" + signature);
        if (!signature.equals(serverSignature)) {
            throw new RuntimeException("数据验签不通过");
        }
    }
    /**
     * 根据流接收请求数据
     *
     * @param request
     * @return
     */
    public static String streamBodyByReceive(HttpServletRequest request) throws IOException {
        BufferedReader reader = null;
        StringBuffer sb = new StringBuffer();
        try {
            ServletInputStream stream = request.getInputStream();
            // 获取响应
            reader = new BufferedReader(new InputStreamReader(stream));
            String line;
            while ((line = reader.readLine()) != null) {
                sb.append(line);
            }
        } catch (IOException e) {
            throw new RuntimeException("读取微信支付接口数据流出现异常!");
        } finally {
            reader.close();
            WxUtils.info(sb.toString());
        }
        return sb.toString();
    }
    /**
     * 获取随机字符串 Nonce Str
     *
     * @return String 随机字符串
     */
    public static String generateNonceStr() {
        char[] nonceChars = new char[32];
        for (int index = 0; index < nonceChars.length; ++index) {
            nonceChars[index] = SYMBOLS.charAt(RANDOM.nextInt(SYMBOLS.length()));
        }
        return new String(nonceChars);
    }
    /**
     * 获取当前时间戳,单位秒
     *
     * @return
     */
    public static long getCurrentTimestamp() {
        return System.currentTimeMillis() / 1000;
    }
    /**
     * 获取当前时间戳,单位毫秒
     *
     * @return
     */
    public static long getCurrentTimestampMs() {
        return System.currentTimeMillis();
    }
    /**
     * 日志
     *
     * @return
     */
    public static Logger getLogger() {
        Logger logger = LoggerFactory.getLogger("wxpay java sdk  --->");
        return logger;
    }
    /**
     * debug
     *
     * @param msg
     * @param args
     */
    public static void debug(String msg, Object... args) {
        Logger log = getLogger();
        if (log.isDebugEnabled()) {
            log.debug(msg, args);
        }
    }
    /**
     * info
     *
     * @param msg
     * @param args
     */
    public static void info(String msg, Object... args) {
        Logger log = getLogger();
        if (log.isInfoEnabled()) {
            log.info(msg, args);
        }
    }
    /**
     * warn
     *
     * @param msg
     * @param args
     */
    public static void warn(String msg, Object... args) {
        Logger log = getLogger();
        if (log.isWarnEnabled()) {
            log.warn(msg, args);
        }
    }
    /**
     * error
     *
     * @param msg
     * @param args
     */
    public static void error(String msg, Object... args) {
        Logger log = getLogger();
        if (log.isErrorEnabled()) {
            log.error(msg, args);
        }
    }
}
ruoyi-service/ruoyi-payment/src/main/java/com/ruoyi/payment/wx/utils/WxV3Pay.java
New file
@@ -0,0 +1,197 @@
package com.ruoyi.payment.wx.utils;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
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.WxPaymentInfoModel;
import com.ruoyi.payment.wx.model.WxPaymentRefundModel;
import com.ruoyi.payment.wx.resp.NotifyV3PayDecodeRespBody;
import com.wechat.pay.contrib.apache.httpclient.WechatPayHttpClientBuilder;
import com.wechat.pay.contrib.apache.httpclient.auth.PrivateKeySigner;
import com.wechat.pay.contrib.apache.httpclient.auth.Verifier;
import com.wechat.pay.contrib.apache.httpclient.auth.WechatPay2Credentials;
import com.wechat.pay.contrib.apache.httpclient.auth.WechatPay2Validator;
import com.wechat.pay.contrib.apache.httpclient.cert.CertificatesManager;
import com.wechat.pay.contrib.apache.httpclient.util.PemUtil;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.client.methods.HttpEntityEnclosingRequestBase;
import org.apache.http.impl.client.CloseableHttpClient;
import javax.servlet.http.HttpServletRequest;
import java.nio.charset.StandardCharsets;
import java.security.PrivateKey;
import java.util.Map;
/**
 * @author xiaochen
 * @ClassName WxWifiV3Pay
 * @Description
 * @date 2021-11-13 21:10
 */
@Slf4j
public class WxV3Pay extends WxAbstractPay {
    @Getter
    private WeixinProperties config;
    @Getter
    private Verifier verifier;
    private WechatPayHttpClientBuilder builder;
    @Getter
    private CloseableHttpClient httpClient;
    private PrivateKeySigner privateKeySigner;
    private WechatPay2Validator validator;
    @Getter
    private PrivateKey privateKey;
    /**
     * 初始化
     *
     * @param config
     */
    public WxV3Pay(WeixinProperties config) {
        // 检查v3支付配置信息
        if (WxUtils.getLogger().isDebugEnabled()) {
            WxUtils.debug("开始检查v3支付配置信息....");
        }
        try {
            this.config = config;
            checkWxConfig();
            this.privateKey = PemUtil.loadPrivateKey(config.getV3().getPrivateKeyStream());
            this.privateKeySigner = new PrivateKeySigner(config.getV3().getMchSerialNo(), privateKey);
            // 获取证书管理器实例
            CertificatesManager certificatesManager = CertificatesManager.getInstance();
            // 向证书管理器增加需要自动更新平台证书的商户信息
            certificatesManager.putMerchant(config.getMchId(), new WechatPay2Credentials(config.getMchId(),
                    this.privateKeySigner), config.getV3().getApiKey().getBytes(StandardCharsets.UTF_8));
            // 从证书管理器中获取verifier
            this.verifier = certificatesManager.getVerifier(config.getMchId());
            this.validator = new WechatPay2Validator(verifier);
            this.builder = WechatPayHttpClientBuilder.create()
                    .withMerchant(config.getMchId(), config.getV3().getMchSerialNo(), this.privateKey)
                    .withValidator(this.validator);
            this.httpClient = this.builder.build();
        } catch (Exception e) {
            // 打印异常信息
            e.printStackTrace();
            WxUtils.warn("检查v3支付配置信息出现错误,直连商户商户号:{},{}", config.getMchId(), e.getMessage());
            return;
        }
        if (WxUtils.getLogger().isDebugEnabled()) {
            WxUtils.debug("检查v3支付配置信息完成,未出现异常....");
        }
    }
    /**
     * 检查支付配置信息
     *
     * @throws Exception
     */
    private void checkWxConfig() throws Exception {
        if (this.config == null) {
            throw new Exception("config is null");
        }
        if (config.getMchId() == null || config.getMchId().trim().length() == 0) {
            throw new Exception("MchID in config is empty");
        }
        if (config.getV3().getMchSerialNo() == null) {
            throw new Exception("mchSerialNo in config is empty");
        }
        if (config.getV3().getPrivateKeyStream() == null) {
            throw new Exception("cert stream in config is empty");
        }
        if (this.config.getHttpConnectTimeoutMs() < 10) {
            throw new Exception("http connect timeout is too small");
        }
        if (this.config.getHttpReadTimeoutMs() < 10) {
            throw new Exception("http read timeout is too small");
        }
    }
    /**
     * jsApi下单
     *
     * @param tradeNo     订单号
     * @param amount      金额 分
     * @param openid      openid
     * @param description 订单描述
     * @return java.util.Map<java.lang.String, java.lang.Object>
     * @author xiaochen
     * @date 2022-03-22 12:47
     */
    public Map<String, Object> jsApi(String tradeNo, Integer amount, String openid, String description) {
        WxPaymentInfoModel requestBody = WxPaymentInfoModel.builder()
                .mchid(this.config.getMchId())
                .appid(this.config.getAppId())
                .description(description)
                .out_trade_no(tradeNo)
//                .attach("")
                .amount(WxPaymentInfoModel.Amount.builder().total(amount).build())
                .payer(WxPaymentInfoModel.Payer.builder().openid(openid).build())
                // 分不分账
                //.settle_info(WxPaymentInfoModel.SettleInfo.builder().profit_sharing(true).build())
                .build();
        // 封装基础数据
        String reqBody = buildBaseParam(requestBody
                , this.config.getV3().getNotifyPayUrl());
        //请求URL
        HttpEntityEnclosingRequestBase httpPost = requestPost(
                "/v3/pay/transactions/jsapi"
                , this.config.getHttpReadTimeoutMs()
                , this.config.getHttpConnectTimeoutMs()
                , reqBody);
        String repBody = result(httpClient, httpPost);
        ObjectMapper om = new ObjectMapper();
        try {
            JsonNode rootNode = om.readTree(repBody);
            String prepayId = rootNode.path("prepay_id").asText();
            return wxTuneUp(this.privateKeySigner, requestBody.getAppid(), prepayId);
        } catch (JsonProcessingException e) {
            throw new RuntimeException("获取支付数据错误!");
        }
    }
    /**
     * 接收回调
     *
     * @param request
     * @return
     * @throws Exception
     */
    @Override
    public <T> T verifyNotify(HttpServletRequest request, TypeReference<T> valueTypeRef) throws Exception {
        return verifyNotify(request, this.verifier, this.config.getV3().getApiKey(), valueTypeRef);
    }
    /**
     * 订单查询
     *
     * @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) {
        String url =
                String.format("/v3/pay/transactions/out-trade-no/%s", out_trade_no) + String.format("?mchid=%s", mchid);
        return query(this.httpClient, this.config.getHttpReadTimeoutMs(), this.config.getHttpConnectTimeoutMs(), url);
    }
    /**
     * 退款
     *
     * @param refundModel
     * @return java.util.Map<java.lang.String, java.lang.Object>
     * @author xiaochen
     */
    @Override
    public Map<String, Object> refund(WxPaymentRefundModel refundModel) {
//        refundModel.setNotify_url(this.config.getV3().getNotifyRefundUrl());
        return refund(this.httpClient, "/v3/refund/domestic/refunds", this.config.getHttpReadTimeoutMs(), this.config.getHttpConnectTimeoutMs(), refundModel);
    }
}
ruoyi-service/ruoyi-payment/src/main/resources/bootstrap.yml
@@ -45,7 +45,7 @@
  enabled: true
  application-id: ${spring.application.name}
  tx-service-group: seata_tx_group    #此处配置自定义的seata事务分组名称
  enable-auto-data-source-proxy: true    #开启数据库代理
  enable-auto-data-source-proxy: false    #关闭数据库代理
  service:
    vgroup-mapping:
      seata_tx_group: default