# Conflicts:
#	yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/knowledge/AiKnowledgeDocumentStatusEnum.java
#	yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/music/AiMusicGenerateModeEnum.java
#	yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/music/AiMusicStatusEnum.java
#	yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/write/AiWriteTypeEnum.java
#	yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/chat/AiChatMessageController.http
#	yudao-module-ai/yudao-spring-boot-starter-ai/pom.xml
#	yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/ErrorCodeConstants.java
#	yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmBoundaryEventTypeEnum.java
#	yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmModelFormTypeEnum.java
#	yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmModelTypeEnum.java
#	yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmProcessListenerTypeEnum.java
#	yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmProcessListenerValueTypeEnum.java
#	yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmSimpleModeConditionTypeEnum.java
#	yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmSimpleModelNodeTypeEnum.java
#	yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmUserTaskApproveMethodEnum.java
#	yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmUserTaskApproveTypeEnum.java
#	yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmUserTaskAssignEmptyHandlerTypeEnum.java
#	yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmUserTaskAssignStartUserHandlerTypeEnum.java
#	yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmUserTaskRejectHandlerTypeEnum.java
#	yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmUserTaskTimeoutHandlerTypeEnum.java
#	yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/task/BpmProcessInstanceStatusEnum.java
#	yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/task/BpmReasonEnum.java
#	yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/BpmModelController.java
#	yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/BpmModelMetaInfoVO.java
#	yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/simple/BpmSimpleModelNodeVO.java
#	yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmProcessInstanceController.java
#	yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmProcessInstanceCopyController.java
#	yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmTaskController.java
#	yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/cc/BpmProcessInstanceCopyRespVO.java
#	yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/instance/BpmApprovalDetailRespVO.java
#	yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/instance/BpmProcessInstancePageReqVO.java
#	yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceRespVO.java
#	yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/task/BpmTaskApproveReqVO.java
#	yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/task/BpmTaskRejectReqVO.java
#	yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/task/BpmTaskRespVO.java
#	yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/convert/task/BpmProcessInstanceConvert.java
#	yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/convert/task/BpmTaskConvert.java
#	yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/dal/dataobject/definition/BpmProcessDefinitionInfoDO.java
#	yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/dal/dataobject/definition/BpmProcessListenerDO.java
#	yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/dal/dataobject/task/BpmProcessInstanceCopyDO.java
#	yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/dal/mysql/task/BpmProcessInstanceCopyMapper.java
#	yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/BpmTaskCandidateInvoker.java
#	yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/enums/BpmTaskCandidateStrategyEnum.java
#	yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/enums/BpmnModelConstants.java
#	yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/enums/BpmnVariableConstants.java
#	yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/listener/BpmProcessInstanceEventListener.java
#	yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/listener/BpmTaskEventListener.java
#	yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java
#	yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/FlowableUtils.java
#	yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java
#	yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmModelService.java
#	yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmModelServiceImpl.java
#	yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmProcessListenerServiceImpl.java
#	yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceCopyService.java
#	yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceCopyServiceImpl.java
#	yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceService.java
#	yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java
#	yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskService.java
#	yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java
#	yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/business/CrmBusinessEndStatusEnum.java
#	yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/common/CrmAuditStatusEnum.java
#	yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/common/CrmBizTypeEnum.java
#	yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/common/CrmSceneTypeEnum.java
#	yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/customer/CrmCustomerLevelEnum.java
#	yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/customer/CrmCustomerLimitConfigTypeEnum.java
#	yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/permission/CrmPermissionLevelEnum.java
#	yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/product/CrmProductStatusEnum.java
#	yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/receivable/CrmReceivableReturnTypeEnum.java
#	yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/CrmBusinessController.java
#	yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/permission/CrmPermissionController.http
#	yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/CrmStatisticsCustomerController.http
#	yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/CrmStatisticsRankController.http
#	yudao-module-erp/yudao-module-erp-api/src/main/java/cn/iocoder/yudao/module/erp/enums/ErpAuditStatus.java
#	yudao-module-erp/yudao-module-erp-api/src/main/java/cn/iocoder/yudao/module/erp/enums/common/ErpBizTypeEnum.java
#	yudao-module-erp/yudao-module-erp-api/src/main/java/cn/iocoder/yudao/module/erp/enums/stock/ErpStockRecordBizTypeEnum.java
#	yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/statistics/ErpSaleStatisticsController.http
#	yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/device/IotDeviceStatusEnum.java
#	yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/product/IotDataFormatEnum.java
#	yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/product/IotNetTypeEnum.java
#	yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/product/IotProductDeviceTypeEnum.java
#	yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/product/IotProductFunctionTypeEnum.java
#	yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/product/IotProductStatusEnum.java
#	yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/product/IotProtocolTypeEnum.java
#	yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/product/IotValidateTypeEnum.java
#	yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodelfunction/IotThinkModelFunctionController.http
#	yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/enums/comment/ProductCommentAuditStatusEnum.java
#	yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/enums/comment/ProductCommentScoresEnum.java
#	yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/enums/spu/ProductSpuStatusEnum.java
#	yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/ProductSpuController.http
#	yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/app/spu/AppProductSpuController.http
#	yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/enums/banner/BannerPositionEnum.java
#	yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/enums/bargain/BargainRecordStatusEnum.java
#	yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/enums/combination/CombinationRecordStatusEnum.java
#	yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/enums/common/PromotionActivityStatusEnum.java
#	yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/enums/common/PromotionConditionTypeEnum.java
#	yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/enums/common/PromotionDiscountTypeEnum.java
#	yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/enums/common/PromotionProductScopeEnum.java
#	yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/enums/common/PromotionTypeEnum.java
#	yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/enums/coupon/CouponStatusEnum.java
#	yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/enums/coupon/CouponTakeTypeEnum.java
#	yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/enums/coupon/CouponTemplateValidityTypeEnum.java
#	yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/enums/diy/DiyPageEnum.java
#	yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/enums/kefu/KeFuMessageContentTypeEnum.java
#	yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/activity/AppActivityController.http
#	yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/bargain/AppBargainHelpController.http
#	yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/bargain/AppBargainRecordController.http
#	yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/combination/CombinationRecordServiceImpl.java
#	yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/coupon/CouponServiceImpl.java
#	yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/point/PointActivityServiceImpl.java
#	yudao-module-mall/yudao-module-statistics-api/src/main/java/cn/iocoder/yudao/module/statistics/enums/TimeRangeTypeEnum.java
#	yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/ErrorCodeConstants.java
#	yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/aftersale/AfterSaleStatusEnum.java
#	yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/aftersale/AfterSaleTypeEnum.java
#	yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/aftersale/AfterSaleWayEnum.java
#	yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/brokerage/BrokerageBindModeEnum.java
#	yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/brokerage/BrokerageEnabledConditionEnum.java
#	yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/brokerage/BrokerageRecordBizTypeEnum.java
#	yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/brokerage/BrokerageRecordStatusEnum.java
#	yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/brokerage/BrokerageWithdrawStatusEnum.java
#	yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/brokerage/BrokerageWithdrawTypeEnum.java
#	yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/delivery/DeliveryExpressChargeModeEnum.java
#	yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/delivery/DeliveryTypeEnum.java
#	yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/order/TradeOrderCancelTypeEnum.java
#	yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/order/TradeOrderItemAfterSaleStatusEnum.java
#	yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/order/TradeOrderRefundStatusEnum.java
#	yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/order/TradeOrderStatusEnum.java
#	yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/order/TradeOrderTypeEnum.java
#	yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/aftersale/TradeAfterSaleController.http
#	yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/brokerage/BrokerageUserController.java
#	yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/order/TradeOrderController.http
#	yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/brokerage/AppBrokerageUserController.java
#	yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/cart/AppCartController.http
#	yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/AppTradeOrderController.http
#	yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/dal/mysql/brokerage/BrokerageWithdrawMapper.java
#	yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/dal/mysql/order/TradeOrderMapper.java
#	yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/brokerage/BrokerageWithdrawService.java
#	yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/brokerage/BrokerageWithdrawServiceImpl.java
#	yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderQueryService.java
#	yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderQueryServiceImpl.java
#	yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/handler/TradePointOrderHandler.java
#	yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradePointActivityPriceCalculator.java
#	yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradeSeckillActivityPriceCalculator.java
#	yudao-module-member/yudao-module-member-api/src/main/java/cn/iocoder/yudao/module/member/enums/point/MemberPointBizTypeEnum.java
#	yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/address/AppAddressController.http
#	yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/auth/AppAuthController.http
#	yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/user/AppMemberUserController.http
#	yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/controller/admin/material/MpMaterialController.http
#	yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/controller/admin/menu/MpMenuController.http
#	yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/controller/admin/message/MpAutoReplyController.http
#	yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/controller/admin/message/MpMessageController.http
#	yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/controller/admin/news/MpDraftController.http
#	yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/controller/admin/news/MpFreePublishController.http
#	yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/controller/admin/tag/MpTagController.http
#	yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/controller/admin/user/MpUserController.http
#	yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/enums/order/PayOrderStatusEnum.java
#	yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/enums/transfer/PayTransferTypeEnum.java
#	yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/enums/wallet/PayWalletBizTypeEnum.java
#	yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/app/order/AppPayOrderController.http
#	yudao-module-pay/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/AbstractWxPayClient.java
#	yudao-module-pay/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/enums/transfer/PayTransferTypeEnum.java
#	yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/auth/AdminAuthServiceImplTest.java
This commit is contained in:
YunaiV 2025-02-09 11:48:21 +08:00
commit 2f6fb86795
62 changed files with 839 additions and 173 deletions

BIN
.DS_Store vendored

Binary file not shown.

View File

@ -2,16 +2,16 @@
"local": {
"baseUrl": "http://127.0.0.1:48080/admin-api",
"token": "test1",
"adminTenentId": "1",
"adminTenantId": "1",
"appApi": "http://127.0.0.1:48080/app-api",
"appToken": "test247",
"appTenentId": "1"
"appTenantId": "1"
},
"gateway": {
"baseUrl": "http://127.0.0.1:8888/admin-api",
"token": "test1",
"adminTenentId": "1",
"adminTenantId": "1",
"appApi": "http://127.0.0.1:8888/app-api",
"appToken": "test1",

View File

@ -0,0 +1,15 @@
package cn.iocoder.yudao.framework.common.core;
/**
* 可生成 T 数组的接口
*
* @author HUIHUI
*/
public interface ArrayValuable<T> {
/**
* @return 数组
*/
T[] array();
}

View File

@ -1,15 +0,0 @@
package cn.iocoder.yudao.framework.common.core;
/**
* 可生成 Int 数组的接口
*
* @author 芋道源码
*/
public interface IntArrayValuable {
/**
* @return int 数组
*/
int[] array();
}

View File

@ -1,7 +1,7 @@
package cn.iocoder.yudao.framework.common.enums;
import cn.hutool.core.util.ObjUtil;
import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
import cn.iocoder.yudao.framework.common.core.ArrayValuable;
import lombok.AllArgsConstructor;
import lombok.Getter;
@ -14,12 +14,12 @@ import java.util.Arrays;
*/
@Getter
@AllArgsConstructor
public enum CommonStatusEnum implements IntArrayValuable {
public enum CommonStatusEnum implements ArrayValuable<Integer> {
ENABLE(0, "开启"),
DISABLE(1, "关闭");
public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(CommonStatusEnum::getStatus).toArray();
public static final Integer[] ARRAYS = Arrays.stream(values()).map(CommonStatusEnum::getStatus).toArray(Integer[]::new);
/**
* 状态值
@ -31,7 +31,7 @@ public enum CommonStatusEnum implements IntArrayValuable {
private final String name;
@Override
public int[] array() {
public Integer[] array() {
return ARRAYS;
}

View File

@ -1,7 +1,7 @@
package cn.iocoder.yudao.framework.common.enums;
import cn.hutool.core.util.ArrayUtil;
import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
import cn.iocoder.yudao.framework.common.core.ArrayValuable;
import lombok.AllArgsConstructor;
import lombok.Getter;
@ -14,7 +14,7 @@ import java.util.Arrays;
*/
@Getter
@AllArgsConstructor
public enum DateIntervalEnum implements IntArrayValuable {
public enum DateIntervalEnum implements ArrayValuable<Integer> {
DAY(1, ""),
WEEK(2, ""),
@ -23,7 +23,7 @@ public enum DateIntervalEnum implements IntArrayValuable {
YEAR(5, "")
;
public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(DateIntervalEnum::getInterval).toArray();
public static final Integer[] ARRAYS = Arrays.stream(values()).map(DateIntervalEnum::getInterval).toArray(Integer[]::new);
/**
* 类型
@ -35,7 +35,7 @@ public enum DateIntervalEnum implements IntArrayValuable {
private final String name;
@Override
public int[] array() {
public Integer[] array() {
return ARRAYS;
}

View File

@ -1,6 +1,6 @@
package cn.iocoder.yudao.framework.common.enums;
import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
import cn.iocoder.yudao.framework.common.core.ArrayValuable;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
@ -13,7 +13,7 @@ import java.util.Arrays;
*/
@RequiredArgsConstructor
@Getter
public enum TerminalEnum implements IntArrayValuable {
public enum TerminalEnum implements ArrayValuable<Integer> {
UNKNOWN(0, "未知"), // 目的在无法解析到 terminal 使用它
WECHAT_MINI_PROGRAM(10, "微信小程序"),
@ -22,7 +22,7 @@ public enum TerminalEnum implements IntArrayValuable {
APP(31, "手机 App"),
;
public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(TerminalEnum::getTerminal).toArray();
public static final Integer[] ARRAYS = Arrays.stream(values()).map(TerminalEnum::getTerminal).toArray(Integer[]::new);
/**
* 终端
@ -34,7 +34,7 @@ public enum TerminalEnum implements IntArrayValuable {
private final String name;
@Override
public int[] array() {
public Integer[] array() {
return ARRAYS;
}
}

View File

@ -1,7 +1,7 @@
package cn.iocoder.yudao.framework.common.enums;
import cn.hutool.core.util.ArrayUtil;
import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
import cn.iocoder.yudao.framework.common.core.ArrayValuable;
import lombok.AllArgsConstructor;
import lombok.Getter;
@ -12,12 +12,12 @@ import java.util.Arrays;
*/
@AllArgsConstructor
@Getter
public enum UserTypeEnum implements IntArrayValuable {
public enum UserTypeEnum implements ArrayValuable<Integer> {
MEMBER(1, "会员"), // 面向 c 普通用户
ADMIN(2, "管理员"); // 面向 b 管理后台
public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(UserTypeEnum::getValue).toArray();
public static final Integer[] ARRAYS = Arrays.stream(values()).map(UserTypeEnum::getValue).toArray(Integer[]::new);
/**
* 类型
@ -33,7 +33,7 @@ public enum UserTypeEnum implements IntArrayValuable {
}
@Override
public int[] array() {
public Integer[] array() {
return ARRAYS;
}
}

View File

@ -199,4 +199,12 @@ public class JsonUtils {
return JSONUtil.isTypeJSON(text);
}
/**
* 判断字符串是否为 JSON 类型的字符串
* @param str 字符串
*/
public static boolean isJsonObject(String str) {
return JSONUtil.isTypeJSONObject(str);
}
}

View File

@ -1,6 +1,6 @@
package cn.iocoder.yudao.framework.common.validation;
import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
import cn.iocoder.yudao.framework.common.core.ArrayValuable;
import javax.validation.Constraint;
import javax.validation.Payload;
@ -22,9 +22,9 @@ import java.lang.annotation.*;
public @interface InEnum {
/**
* @return 实现 EnumValuable 接口的
* @return 实现 ArrayValuable 接口的类
*/
Class<? extends IntArrayValuable> value();
Class<? extends ArrayValuable<?>> value();
String message() default "必须在指定范围 {value}";

View File

@ -1,7 +1,7 @@
package cn.iocoder.yudao.framework.common.validation;
import cn.hutool.core.collection.CollUtil;
import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
import cn.iocoder.yudao.framework.common.core.ArrayValuable;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
@ -9,29 +9,31 @@ import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
public class InEnumCollectionValidator implements ConstraintValidator<InEnum, Collection<Integer>> {
public class InEnumCollectionValidator implements ConstraintValidator<InEnum, Collection<?>> {
private List<Integer> values;
private List<?> values;
@Override
public void initialize(InEnum annotation) {
IntArrayValuable[] values = annotation.value().getEnumConstants();
ArrayValuable<?>[] values = annotation.value().getEnumConstants();
if (values.length == 0) {
this.values = Collections.emptyList();
} else {
this.values = Arrays.stream(values[0].array()).boxed().collect(Collectors.toList());
this.values = Arrays.asList(values[0].array());
}
}
@Override
public boolean isValid(Collection<Integer> list, ConstraintValidatorContext context) {
public boolean isValid(Collection<?> list, ConstraintValidatorContext context) {
if (list == null) {
return true;
}
// 校验通过
if (CollUtil.containsAll(values, list)) {
return true;
}
// 校验不通过自定义提示语句因为注解上的 value 是枚举类无法获得枚举类的实际值
// 校验不通过自定义提示语句
context.disableDefaultConstraintViolation(); // 禁用默认的 message 的值
context.buildConstraintViolationWithTemplate(context.getDefaultConstraintMessageTemplate()
.replaceAll("\\{value}", CollUtil.join(list, ","))).addConstraintViolation(); // 重新添加错误提示语句

View File

@ -1,30 +1,29 @@
package cn.iocoder.yudao.framework.common.validation;
import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
import cn.iocoder.yudao.framework.common.core.ArrayValuable;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
public class InEnumValidator implements ConstraintValidator<InEnum, Integer> {
public class InEnumValidator implements ConstraintValidator<InEnum, Object> {
private List<Integer> values;
private List<?> values;
@Override
public void initialize(InEnum annotation) {
IntArrayValuable[] values = annotation.value().getEnumConstants();
ArrayValuable<?>[] values = annotation.value().getEnumConstants();
if (values.length == 0) {
this.values = Collections.emptyList();
} else {
this.values = Arrays.stream(values[0].array()).boxed().collect(Collectors.toList());
this.values = Arrays.asList(values[0].array());
}
}
@Override
public boolean isValid(Integer value, ConstraintValidatorContext context) {
public boolean isValid(Object value, ConstraintValidatorContext context) {
// 为空时默认不校验即认为通过
if (value == null) {
return true;
@ -33,7 +32,7 @@ public class InEnumValidator implements ConstraintValidator<InEnum, Integer> {
if (values.contains(value)) {
return true;
}
// 校验不通过自定义提示语句因为注解上的 value 是枚举类无法获得枚举类的实际值
// 校验不通过自定义提示语句
context.disableDefaultConstraintViolation(); // 禁用默认的 message 的值
context.buildConstraintViolationWithTemplate(context.getDefaultConstraintMessageTemplate()
.replaceAll("\\{value}", values.toString())).addConstraintViolation(); // 重新添加错误提示语句

View File

@ -1,6 +1,6 @@
package cn.iocoder.yudao.framework.ip.core.enums;
import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
import cn.iocoder.yudao.framework.common.core.ArrayValuable;
import lombok.AllArgsConstructor;
import lombok.Getter;
@ -13,7 +13,7 @@ import java.util.Arrays;
*/
@AllArgsConstructor
@Getter
public enum AreaTypeEnum implements IntArrayValuable {
public enum AreaTypeEnum implements ArrayValuable<Integer> {
COUNTRY(1, "国家"),
PROVINCE(2, "省份"),
@ -21,7 +21,7 @@ public enum AreaTypeEnum implements IntArrayValuable {
DISTRICT(4, "地区"), // 区等
;
public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(AreaTypeEnum::getType).toArray();
public static final Integer[] ARRAYS = Arrays.stream(values()).map(AreaTypeEnum::getType).toArray(Integer[]::new);
/**
* 类型
@ -33,7 +33,7 @@ public enum AreaTypeEnum implements IntArrayValuable {
private final String name;
@Override
public int[] array() {
public Integer[] array() {
return ARRAYS;
}
}

View File

@ -128,7 +128,7 @@ public class YudaoWebSecurityConfigurerAdapter {
// 全局共享规则
.authorizeHttpRequests(c -> c
// 1.1 静态资源可匿名访问
.requestMatchers(HttpMethod.GET, "/*.html", "/*.html", "/*.css", "/*.js").permitAll()
.requestMatchers(HttpMethod.GET, "/*.html", "/*.css", "/*.js").permitAll()
// 1.2 设置 @PermitAll 无需认证
.requestMatchers(HttpMethod.GET, permitAllUrls.get(HttpMethod.GET).toArray(new String[0])).permitAll()
.requestMatchers(HttpMethod.POST, permitAllUrls.get(HttpMethod.POST).toArray(new String[0])).permitAll()

View File

@ -146,9 +146,11 @@ public class ApiAccessLogFilter extends ApiRequestFilter {
if (handlerMethod != null) {
Tag tagAnnotation = handlerMethod.getBeanType().getAnnotation(Tag.class);
Operation operationAnnotation = handlerMethod.getMethodAnnotation(Operation.class);
String operateModule = accessLogAnnotation != null ? accessLogAnnotation.operateModule() :
String operateModule = accessLogAnnotation != null && StrUtil.isNotBlank(accessLogAnnotation.operateModule()) ?
accessLogAnnotation.operateModule() :
tagAnnotation != null ? StrUtil.nullToDefault(tagAnnotation.name(), tagAnnotation.description()) : null;
String operateName = accessLogAnnotation != null ? accessLogAnnotation.operateName() :
String operateName = accessLogAnnotation != null && StrUtil.isNotBlank(accessLogAnnotation.operateName()) ?
accessLogAnnotation.operateName() :
operationAnnotation != null ? operationAnnotation.summary() : null;
OperateTypeEnum operateType = accessLogAnnotation != null && accessLogAnnotation.operateType().length > 0 ?
accessLogAnnotation.operateType()[0] : parseOperateLogType(request);

View File

@ -1,5 +1,6 @@
package cn.iocoder.yudao.framework.desensitize.core.slider.handler;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.common.util.spring.SpringExpressionUtils;
import cn.iocoder.yudao.framework.desensitize.core.base.handler.DesensitizationHandler;
@ -26,19 +27,14 @@ public abstract class AbstractSliderDesensitizationHandler<T extends Annotation>
int suffixKeep = getSuffixKeep(annotation);
String replacer = getReplacer(annotation);
int length = origin.length();
// 情况一原始字符串长度小于等于保留长度则原始字符串全部替换
if (prefixKeep >= length || suffixKeep >= length) {
return buildReplacerByLength(replacer, length);
}
// 情况二原始字符串长度小于等于前后缀保留字符串长度则原始字符串全部替换
if ((prefixKeep + suffixKeep) >= length) {
return buildReplacerByLength(replacer, length);
}
// 情况三原始字符串长度大于前后缀保留字符串长度则替换中间字符串
int interval = length - prefixKeep - suffixKeep;
// 情况一原始字符串长度小于等于前后缀保留字符串长度则原始字符串全部替换
if (interval <= 0) {
return buildReplacerByLength(replacer, length);
}
// 情况二原始字符串长度大于前后缀保留字符串长度则替换中间字符串
return origin.substring(0, prefixKeep) +
buildReplacerByLength(replacer, interval) +
origin.substring(prefixKeep + interval);
@ -52,11 +48,7 @@ public abstract class AbstractSliderDesensitizationHandler<T extends Annotation>
* @return 构建后的替换符
*/
private String buildReplacerByLength(String replacer, int length) {
StringBuilder builder = new StringBuilder();
for (int i = 0; i < length; i++) {
builder.append(replacer);
}
return builder.toString();
return StrUtil.repeat(replacer, length);
}
/**

View File

@ -20,8 +20,8 @@ public abstract class ApiRequestFilter extends OncePerRequestFilter {
@Override
protected boolean shouldNotFilter(HttpServletRequest request) {
// 只过滤 API 请求的地址
return !StrUtil.startWithAny(request.getRequestURI(), webProperties.getAdminApi().getPrefix(),
webProperties.getAppApi().getPrefix());
String apiUri = request.getRequestURI().substring(request.getContextPath().length());
return !StrUtil.startWithAny(apiUri, webProperties.getAdminApi().getPrefix(), webProperties.getAppApi().getPrefix());
}
}

View File

@ -0,0 +1,32 @@
package cn.iocoder.yudao.module.bpm.enums.definition;
import cn.iocoder.yudao.framework.common.core.ArrayValuable;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.Arrays;
/**
* BPM 自动去重的类型的枚举
*
* @author Lesan
*/
@Getter
@AllArgsConstructor
public enum BpmAutoApproveTypeEnum implements ArrayValuable<Integer> {
NONE(0, "不自动通过"),
APPROVE_ALL(1, "仅审批一次,后续重复的审批节点均自动通过"),
APPROVE_SEQUENT(2, "仅针对连续审批的节点自动通过");
public static final Integer[] ARRAYS = Arrays.stream(values()).map(BpmAutoApproveTypeEnum::getType).toArray(Integer[]::new);
private final Integer type;
private final String name;
@Override
public Integer[] array() {
return ARRAYS;
}
}

View File

@ -0,0 +1,31 @@
package cn.iocoder.yudao.module.bpm.enums.definition;
import cn.iocoder.yudao.framework.common.core.ArrayValuable;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.Arrays;
/**
* BPM 延迟器类型枚举
*
* @author Lesan
*/
@Getter
@AllArgsConstructor
public enum BpmDelayTimerTypeEnum implements ArrayValuable<Integer> {
FIXED_TIME_DURATION(1, "固定时长"),
FIXED_DATE_TIME(2, "固定日期");
private final Integer type;
private final String name;
public static final Integer[] ARRAYS = Arrays.stream(values()).map(BpmDelayTimerTypeEnum::getType).toArray(Integer[]::new);
@Override
public Integer[] array() {
return ARRAYS;
}
}

View File

@ -0,0 +1,31 @@
package cn.iocoder.yudao.module.bpm.enums.definition;
import cn.iocoder.yudao.framework.common.core.ArrayValuable;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.Arrays;
/**
* BPM HTTP 请求参数设置类型用于 Simple 设计器任务监听器和触发器配置
*
* @author Lesan
*/
@Getter
@AllArgsConstructor
public enum BpmHttpRequestParamTypeEnum implements ArrayValuable<Integer> {
FIXED_VALUE(1, "固定值"),
FROM_FORM(2, "表单");
private final Integer type;
private final String name;
public static final Integer[] ARRAYS = Arrays.stream(values()).map(BpmHttpRequestParamTypeEnum::getType).toArray(Integer[]::new);
@Override
public Integer[] array() {
return ARRAYS;
}
}

View File

@ -0,0 +1,43 @@
package cn.iocoder.yudao.module.bpm.enums.definition;
import cn.hutool.core.util.ArrayUtil;
import cn.iocoder.yudao.framework.common.core.ArrayValuable;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.Arrays;
/**
* BPM Simple 触发器类型枚举
*
* @author jason
*/
@Getter
@AllArgsConstructor
public enum BpmTriggerTypeEnum implements ArrayValuable<Integer> {
HTTP_REQUEST(1, "发起 HTTP 请求"),
UPDATE_NORMAL_FORM(2, "更新流程表单"); // TODO @jasonFORM_UPDATE
/**
* 触发器执行动作类型
*/
private final Integer type;
/**
* 触发器执行动作描述
*/
private final String desc;
public static final Integer[] ARRAYS = Arrays.stream(values()).map(BpmTriggerTypeEnum::getType).toArray(Integer[]::new);
@Override
public Integer[] array() {
return ARRAYS;
}
public static BpmTriggerTypeEnum typeOf(Integer type) {
return ArrayUtil.firstMatch(item -> item.getType().equals(type), values());
}
}

View File

@ -0,0 +1,24 @@
package cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.form;
import lombok.Data;
/**
* 流程表单字段 VO
*/
@Data
public class BpmFormFieldVO {
/**
* 字段类型
*/
private String type;
/**
* 字段标识
*/
private String field;
/**
* 字段标题
*/
private String title;
}

View File

@ -0,0 +1,59 @@
package cn.iocoder.yudao.module.bpm.dal.redis;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.BpmModelMetaInfoVO;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Repository;
import javax.annotation.Resource;
import java.time.Duration;
import java.time.LocalDateTime;
/**
* BPM 流程 Id 编码的 Redis DAO
*
* @author Lesan
*/
@Repository
public class BpmProcessIdRedisDAO {
@Resource
private StringRedisTemplate stringRedisTemplate;
/**
* 生成序号使用定义的 processIdRule 规则生成
*
* @param processIdRule 规则
* @return 序号
*/
public String generate(BpmModelMetaInfoVO.ProcessIdRule processIdRule) {
// 生成日期前缀
String infix = "";
switch (processIdRule.getInfix()) {
case "DAY":
infix = DateUtil.format(LocalDateTime.now(), "yyyyMMDD");
break;
case "HOUR":
infix = DateUtil.format(LocalDateTime.now(), "yyyyMMDDHH");
break;
case "MINUTE":
infix = DateUtil.format(LocalDateTime.now(), "yyyyMMDDHHmm");
break;
case "SECOND":
infix = DateUtil.format(LocalDateTime.now(), "yyyyMMDDHHmmss");
break;
}
// 生成序号
String noPrefix = processIdRule.getPrefix() + infix + processIdRule.getPostfix();
String key = RedisKeyConstants.BPM_PROCESS_ID + noPrefix;
Long no = stringRedisTemplate.opsForValue().increment(key);
if (StrUtil.isEmpty(infix)) {
// 特殊没有前缀则不能过期不能每次都是从 0 开始
stringRedisTemplate.expire(key, Duration.ofDays(1L));
}
return noPrefix + String.format("%0" + processIdRule.getLength() + "d", no);
}
}

View File

@ -0,0 +1,15 @@
package cn.iocoder.yudao.module.bpm.dal.redis;
/**
* BPM Redis Key 枚举类
*
* @author 芋道源码
*/
public interface RedisKeyConstants {
/**
* 流程 ID 的缓存
*/
String BPM_PROCESS_ID = "bpm:process_id:";
}

View File

@ -0,0 +1,55 @@
package cn.iocoder.yudao.module.bpm.framework.flowable.core.listener;
import cn.iocoder.yudao.module.bpm.enums.definition.BpmTriggerTypeEnum;
import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils;
import cn.iocoder.yudao.module.bpm.service.task.trigger.BpmTrigger;
import lombok.extern.slf4j.Slf4j;
import org.flowable.bpmn.model.FlowElement;
import org.flowable.engine.delegate.DelegateExecution;
import org.flowable.engine.delegate.JavaDelegate;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.util.EnumMap;
import java.util.List;
import static cn.iocoder.yudao.module.bpm.framework.flowable.core.listener.BpmTriggerTaskDelegate.BEAN_NAME;
/**
* 处理触发器任务 {@link JavaDelegate} 的实现类
* <p>
* 目前只有 Simple 设计器触发器节点使用
*
* @author jason
*/
@Component(BEAN_NAME)
@Slf4j
public class BpmTriggerTaskDelegate implements JavaDelegate {
public static final String BEAN_NAME = "bpmTriggerTaskDelegate";
@Resource
private List<BpmTrigger> triggers;
private final EnumMap<BpmTriggerTypeEnum, BpmTrigger> triggerMap = new EnumMap<>(BpmTriggerTypeEnum.class);
@PostConstruct
private void init() {
triggers.forEach(trigger -> triggerMap.put(trigger.getType(), trigger));
}
@Override
public void execute(DelegateExecution execution) {
FlowElement flowElement = execution.getCurrentFlowElement();
BpmTriggerTypeEnum bpmTriggerType = BpmnModelUtils.parserTriggerType(flowElement);
BpmTrigger bpmTrigger = triggerMap.get(bpmTriggerType);
if (bpmTrigger == null) {
log.error("[execute][FlowElement({}), {} 找不到匹配的触发器]", execution.getCurrentActivityId(), flowElement);
return;
}
bpmTrigger.execute(execution.getProcessInstanceId(), BpmnModelUtils.parserTriggerParam(flowElement));
}
}

View File

@ -0,0 +1,96 @@
package cn.iocoder.yudao.module.bpm.service.task.listener;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO;
import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.SimpleModelUtils;
import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.flowable.engine.delegate.TaskListener;
import org.flowable.engine.history.HistoricProcessInstance;
import org.flowable.engine.impl.el.FixedValue;
import org.flowable.task.service.delegate.DelegateTask;
import org.springframework.context.annotation.Scope;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestTemplate;
import javax.annotation.Resource;
import java.util.Map;
import static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.HEADER_TENANT_ID;
import static cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils.parseListenerConfig;
// TODO @芋艿可能会想换个包地址
/**
* BPM 用户任务通用监听器
*
* @author Lesan
*/
@Component
@Slf4j
@Scope("prototype")
public class BpmUserTaskListener implements TaskListener {
public static final String DELEGATE_EXPRESSION = "${bpmUserTaskListener}";
@Resource
private BpmProcessInstanceService processInstanceService;
@Resource
private RestTemplate restTemplate;
@Setter
private FixedValue listenerConfig;
@Override
public void notify(DelegateTask delegateTask) {
// 1. 获取所需基础信息
HistoricProcessInstance processInstance = processInstanceService.getHistoricProcessInstance(delegateTask.getProcessInstanceId());
BpmSimpleModelNodeVO.ListenerHandler listenerHandler = parseListenerConfig(listenerConfig);
// 2. 获取请求头和请求体
Map<String, Object> processVariables = processInstance.getProcessVariables();
MultiValueMap<String, String> headers = new LinkedMultiValueMap<>();
MultiValueMap<String, String> body = new LinkedMultiValueMap<>();
SimpleModelUtils.addHttpRequestParam(headers, listenerHandler.getHeader(), processVariables);
SimpleModelUtils.addHttpRequestParam(body, listenerHandler.getBody(), processVariables);
// 2.1 请求头默认参数
if (StrUtil.isNotEmpty(delegateTask.getTenantId())) {
headers.add(HEADER_TENANT_ID, delegateTask.getTenantId());
}
// 2.2 请求体默认参数
// TODO @芋艿哪些默认参数后续再调研下感觉可以搞个 task 字段把整个 delegateTask 放进去
body.add("processInstanceId", delegateTask.getProcessInstanceId());
body.add("assignee", delegateTask.getAssignee());
body.add("taskDefinitionKey", delegateTask.getTaskDefinitionKey());
body.add("taskId", delegateTask.getId());
// 3. 异步发起请求
// TODO @芋艿确认要同步还是异步
HttpEntity<MultiValueMap<String, String>> requestEntity = new HttpEntity<>(body, headers);
try {
ResponseEntity<String> responseEntity = restTemplate.exchange(listenerHandler.getPath(), HttpMethod.POST,
requestEntity, String.class);
log.info("[notify][监听器:{},事件类型:{},请求头:{},请求体:{},响应结果:{}]",
DELEGATE_EXPRESSION,
delegateTask.getEventName(),
headers,
body,
responseEntity);
} catch (RestClientException e) {
log.error("[error][监听器:{},事件类型:{},请求头:{},请求体:{},请求出错:{}]",
DELEGATE_EXPRESSION,
delegateTask.getEventName(),
headers,
body,
e.getMessage());
}
// 4. 是否需要后续操作TODO 芋艿待定
}
}

View File

@ -0,0 +1,124 @@
package cn.iocoder.yudao.module.bpm.service.task.trigger;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.common.core.KeyValue;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO.TriggerSetting.HttpRequestTriggerSetting;
import cn.iocoder.yudao.module.bpm.enums.definition.BpmTriggerTypeEnum;
import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.SimpleModelUtils;
import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService;
import com.fasterxml.jackson.core.type.TypeReference;
import lombok.extern.slf4j.Slf4j;
import org.flowable.engine.runtime.ProcessInstance;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestTemplate;
import javax.annotation.Resource;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.HEADER_TENANT_ID;
/**
* BPM 发送 HTTP 请求触发器
*
* @author jason
*/
@Component
@Slf4j
public class BpmHttpRequestTrigger implements BpmTrigger {
@Resource
private BpmProcessInstanceService processInstanceService;
@Resource
private RestTemplate restTemplate;
@Override
public BpmTriggerTypeEnum getType() {
return BpmTriggerTypeEnum.HTTP_REQUEST;
}
@Override
public void execute(String processInstanceId, String param) {
// 1. 解析 http 请求配置
HttpRequestTriggerSetting setting = JsonUtils.parseObject(param, HttpRequestTriggerSetting.class);
if (setting == null) {
log.error("[execute][流程({}) HTTP 触发器请求配置为空]", processInstanceId);
return;
}
// 2.1 设置请求头
ProcessInstance processInstance = processInstanceService.getProcessInstance(processInstanceId);
Map<String, Object> processVariables = processInstance.getProcessVariables();
MultiValueMap<String, String> headers = new LinkedMultiValueMap<>();
headers.add(HEADER_TENANT_ID, processInstance.getTenantId());
SimpleModelUtils.addHttpRequestParam(headers, setting.getHeader(), processVariables);
// 2.2 设置请求体
MultiValueMap<String, String> body = new LinkedMultiValueMap<>();
SimpleModelUtils.addHttpRequestParam(body, setting.getBody(), processVariables);
body.add("processInstanceId", processInstanceId);
// TODO @芋艿要不要抽象一个 Http 请求的工具类方便复用呢
// 3. 发起请求
HttpEntity<MultiValueMap<String, String>> requestEntity = new HttpEntity<>(body, headers);
ResponseEntity<String> responseEntity;
try {
responseEntity = restTemplate.exchange(setting.getUrl(), HttpMethod.POST,
requestEntity, String.class);
log.info("[execute][HTTP 触发器,请求头:{},请求体:{},响应结果:{}]", headers, body, responseEntity);
} catch (RestClientException e) {
log.error("[execute][HTTP 触发器,请求头:{},请求体:{},请求出错:{}]", headers, body, e.getMessage());
return;
}
// 4.1 判断是否需要解析返回值
if (StrUtil.isEmpty(responseEntity.getBody())
|| !responseEntity.getStatusCode().is2xxSuccessful()
|| CollUtil.isEmpty(setting.getResponse())) {
return;
}
// 4.2 解析返回值, 返回值必须符合 CommonResult 规范
CommonResult<Map<String, Object>> respResult = JsonUtils.parseObjectQuietly(
responseEntity.getBody(), new TypeReference<CommonResult<Map<String, Object>>>() {});
if (respResult == null || !respResult.isSuccess()){
return;
}
// 4.3 获取需要更新的流程变量
Map<String, Object> updateVariables = getNeedUpdatedVariablesFromResponse(respResult.getData(), setting.getResponse());
// 4.4 更新流程变量
if (CollUtil.isNotEmpty(updateVariables)) {
processInstanceService.updateProcessInstanceVariables(processInstanceId, updateVariables);
}
}
/**
* 从请求返回值获取需要更新的流程变量
*
* @param result 请求返回结果
* @param responseSettings 返回设置
* @return 需要更新的流程变量
*/
private Map<String, Object> getNeedUpdatedVariablesFromResponse(Map<String,Object> result,
List<KeyValue<String, String>> responseSettings) {
Map<String, Object> updateVariables = new HashMap<>();
if (CollUtil.isEmpty(result)) {
return updateVariables;
}
responseSettings.forEach(responseSetting -> {
if (StrUtil.isNotEmpty(responseSetting.getKey()) && result.containsKey(responseSetting.getValue())) {
updateVariables.put(responseSetting.getKey(), result.get(responseSetting.getValue()));
}
});
return updateVariables;
}
}

View File

@ -0,0 +1,30 @@
package cn.iocoder.yudao.module.bpm.service.task.trigger;
import cn.iocoder.yudao.module.bpm.enums.definition.BpmTriggerTypeEnum;
// TODO @芋艿可能会想换个包地址
/**
* BPM 触发器接口
* <p>
* 处理不同的动作
*
* @author jason
*/
public interface BpmTrigger {
/**
* 对应触发器类型
*
* @return 触发器类型
*/
BpmTriggerTypeEnum getType();
/**
* 触发器执行
*
* @param processInstanceId 流程实例编号
* @param param 触发器参数
*/
void execute(String processInstanceId, String param);
}

View File

@ -0,0 +1,45 @@
package cn.iocoder.yudao.module.bpm.service.task.trigger;
import cn.hutool.core.collection.CollUtil;
import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO.TriggerSetting.NormalFormTriggerSetting;
import cn.iocoder.yudao.module.bpm.enums.definition.BpmTriggerTypeEnum;
import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
// TODO @jason改成 BpmFormUpdateTrigger
/**
* BPM 更新流程表单触发器
*
* @author jason
*/
@Component
@Slf4j
public class BpmUpdateNormalFormTrigger implements BpmTrigger {
@Resource
private BpmProcessInstanceService processInstanceService;
@Override
public BpmTriggerTypeEnum getType() {
return BpmTriggerTypeEnum.UPDATE_NORMAL_FORM;
}
@Override
public void execute(String processInstanceId, String param) {
// 1. 解析更新流程表单配置
NormalFormTriggerSetting setting = JsonUtils.parseObject(param, NormalFormTriggerSetting.class);
if (setting == null) {
log.error("[execute][流程({}) 更新流程表单触发器配置为空]", processInstanceId);
return;
}
// 2.更新流程变量
if (CollUtil.isNotEmpty(setting.getUpdateFormFields())) {
processInstanceService.updateProcessInstanceVariables(processInstanceId, setting.getUpdateFormFields());
}
}
}

View File

@ -1,7 +1,7 @@
### 请求 /infra/file-config/create 接口 => 成功
POST {{baseUrl}}/infra/file-config/create
Content-Type: application/json
tenant-id: {{adminTenentId}}
tenant-id: {{adminTenantId}}
Authorization: Bearer {{token}}
{
@ -21,7 +21,7 @@ Authorization: Bearer {{token}}
### 请求 /infra/file-config/update 接口 => 成功
PUT {{baseUrl}}/infra/file-config/update
Content-Type: application/json
tenant-id: {{adminTenentId}}
tenant-id: {{adminTenantId}}
Authorization: Bearer {{token}}
{
@ -41,5 +41,5 @@ Authorization: Bearer {{token}}
### 请求 /infra/file-config/test 接口 => 成功
GET {{baseUrl}}/infra/file-config/test?id=2
Content-Type: application/json
tenant-id: {{adminTenentId}}
tenant-id: {{adminTenantId}}
Authorization: Bearer {{token}}

View File

@ -1,5 +1,5 @@
### 请求 /infra/job/sync 接口 => 成功
POST {{baseUrl}}/infra/job/sync
Content-Type: application/json
tenant-id: {{adminTenentId}}
tenant-id: {{adminTenantId}}
Authorization: Bearer {{token}}

View File

@ -17,7 +17,7 @@ public class ApiErrorLogRespVO {
@Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
@ExcelProperty("编号")
private Integer id;
private Long id;
@Schema(description = "链路追踪编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "66600cb6-7852-11eb-9439-0242ac130002")
@ExcelProperty("链路追踪编号")
@ -25,7 +25,7 @@ public class ApiErrorLogRespVO {
@Schema(description = "用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "666")
@ExcelProperty("用户编号")
private Integer userId;
private Long userId;
@Schema(description = "用户类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@ExcelProperty(value = "用户类型", converter = DictConvert.class)

View File

@ -1,4 +1,4 @@
### 请求 /infra/redis/get-monitor-info 接口 => 成功
GET {{baseUrl}}/infra/redis/get-monitor-info
Authorization: Bearer {{token}}
tenant-id: {{adminTenentId}}
tenant-id: {{adminTenantId}}

View File

@ -43,6 +43,7 @@ public interface ErrorCodeConstants {
ErrorCode USER_IS_DISABLE = new ErrorCode(1_002_003_006, "名字为【{}】的用户已被禁用");
ErrorCode USER_COUNT_MAX = new ErrorCode(1_002_003_008, "创建用户失败,原因:超过租户最大租户配额({})");
ErrorCode USER_IMPORT_INIT_PASSWORD = new ErrorCode(1_002_003_009, "初始密码不能为空");
ErrorCode USER_MOBILE_NOT_EXISTS = new ErrorCode(1_002_003_010, "该手机号尚未注册");
// ========== 部门模块 1-002-004-000 ==========
ErrorCode DEPT_NAME_DUPLICATE = new ErrorCode(1_002_004_000, "已经存在该名字的部门");

View File

@ -1,6 +1,6 @@
package cn.iocoder.yudao.module.system.enums.permission;
import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
import cn.iocoder.yudao.framework.common.core.ArrayValuable;
import lombok.AllArgsConstructor;
import lombok.Getter;
@ -15,7 +15,7 @@ import java.util.Arrays;
*/
@Getter
@AllArgsConstructor
public enum DataScopeEnum implements IntArrayValuable {
public enum DataScopeEnum implements ArrayValuable<Integer> {
ALL(1), // 全部数据权限
@ -30,10 +30,10 @@ public enum DataScopeEnum implements IntArrayValuable {
*/
private final Integer scope;
public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(DataScopeEnum::getScope).toArray();
public static final Integer[] ARRAYS = Arrays.stream(values()).map(DataScopeEnum::getScope).toArray(Integer[]::new);
@Override
public int[] array() {
public Integer[] array() {
return ARRAYS;
}

View File

@ -1,7 +1,7 @@
package cn.iocoder.yudao.module.system.enums.sms;
import cn.hutool.core.util.ArrayUtil;
import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
import cn.iocoder.yudao.framework.common.core.ArrayValuable;
import lombok.AllArgsConstructor;
import lombok.Getter;
@ -14,16 +14,18 @@ import java.util.Arrays;
*/
@Getter
@AllArgsConstructor
public enum SmsSceneEnum implements IntArrayValuable {
public enum SmsSceneEnum implements ArrayValuable<Integer> {
MEMBER_LOGIN(1, "user-sms-login", "会员用户 - 手机号登陆"),
MEMBER_UPDATE_MOBILE(2, "user-update-mobile", "会员用户 - 修改手机"),
MEMBER_UPDATE_PASSWORD(3, "user-update-password", "会员用户 - 修改密码"),
MEMBER_RESET_PASSWORD(4, "user-reset-password", "会员用户 - 忘记密码"),
ADMIN_MEMBER_LOGIN(21, "admin-sms-login", "后台用户 - 手机号登录");
ADMIN_MEMBER_LOGIN(21, "admin-sms-login", "后台用户 - 手机号登录"),
ADMIN_MEMBER_REGISTER(22, "admin-sms-register", "后台用户 - 手机号注册"),
ADMIN_MEMBER_RESET_PASSWORD(23, "admin-reset-password", "后台用户 - 忘记密码");
public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(SmsSceneEnum::getScene).toArray();
public static final Integer[] ARRAYS = Arrays.stream(values()).map(SmsSceneEnum::getScene).toArray(Integer[]::new);
/**
* 验证场景的编号
@ -39,7 +41,7 @@ public enum SmsSceneEnum implements IntArrayValuable {
private final String description;
@Override
public int[] array() {
public Integer[] array() {
return ARRAYS;
}

View File

@ -1,7 +1,7 @@
package cn.iocoder.yudao.module.system.enums.social;
import cn.hutool.core.util.ArrayUtil;
import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
import cn.iocoder.yudao.framework.common.core.ArrayValuable;
import lombok.AllArgsConstructor;
import lombok.Getter;
@ -14,7 +14,7 @@ import java.util.Arrays;
*/
@Getter
@AllArgsConstructor
public enum SocialTypeEnum implements IntArrayValuable {
public enum SocialTypeEnum implements ArrayValuable<Integer> {
/**
* Gitee
@ -55,7 +55,7 @@ public enum SocialTypeEnum implements IntArrayValuable {
WECHAT_MINI_APP(34, "WECHAT_MINI_APP"),
;
public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(SocialTypeEnum::getType).toArray();
public static final Integer[] ARRAYS = Arrays.stream(values()).map(SocialTypeEnum::getType).toArray(Integer[]::new);
/**
* 类型
@ -67,7 +67,7 @@ public enum SocialTypeEnum implements IntArrayValuable {
private final String source;
@Override
public int[] array() {
public Integer[] array() {
return ARRAYS;
}

View File

@ -1,7 +1,7 @@
### 请求 /login 接口 => 成功
POST {{baseUrl}}/system/auth/login
Content-Type: application/json
tenant-id: {{adminTenentId}}
tenant-id: {{adminTenantId}}
tag: Yunai.local
{
@ -14,7 +14,7 @@ tag: Yunai.local
### 请求 /login 接口 => 成功(无验证码)
POST {{baseUrl}}/system/auth/login
Content-Type: application/json
tenant-id: {{adminTenentId}}
tenant-id: {{adminTenantId}}
{
"username": "admin",
@ -24,10 +24,10 @@ tenant-id: {{adminTenentId}}
### 请求 /get-permission-info 接口 => 成功
GET {{baseUrl}}/system/auth/get-permission-info
Authorization: Bearer {{token}}
tenant-id: {{adminTenentId}}
tenant-id: {{adminTenantId}}
### 请求 /list-menus 接口 => 成功
GET {{baseUrl}}/system/list-menus
Authorization: Bearer {{token}}
#Authorization: Bearer a6aa7714a2e44c95aaa8a2c5adc2a67a
tenant-id: {{adminTenentId}}
tenant-id: {{adminTenantId}}

View File

@ -139,6 +139,14 @@ public class AuthController {
return success(true);
}
@PostMapping("/reset-password")
@PermitAll
@Operation(summary = "重置密码")
public CommonResult<Boolean> resetPassword(@RequestBody @Valid AuthResetPasswordReqVO reqVO) {
authService.resetPassword(reqVO);
return success(true);
}
// ========== 社交登录相关 ==========
@GetMapping("/social-auth-redirect")

View File

@ -19,7 +19,7 @@ import javax.validation.constraints.Pattern;
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class AuthLoginReqVO {
public class AuthLoginReqVO extends CaptchaVerificationReqVO {
@Schema(description = "账号", requiredMode = Schema.RequiredMode.REQUIRED, example = "yudaoyuanma")
@NotEmpty(message = "登录账号不能为空")
@ -32,13 +32,6 @@ public class AuthLoginReqVO {
@Length(min = 4, max = 16, message = "密码长度为 4-16 位")
private String password;
// ========== 图片验证码相关 ==========
@Schema(description = "验证码,验证码开启时,需要传递", requiredMode = Schema.RequiredMode.REQUIRED,
example = "PfcH6mgr8tpXuMWFjvW6YVaqrswIuwmWI5dsVZSg7sGpWtDCUbHuDEXl3cFB1+VvCC/rAkSwK8Fad52FSuncVg==")
@NotEmpty(message = "验证码不能为空", groups = CodeEnableGroup.class)
private String captchaVerification;
// ========== 绑定社交登录时需要传递如下参数 ==========
@Schema(description = "社交平台的类型,参见 SocialTypeEnum 枚举值", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
@ -51,11 +44,6 @@ public class AuthLoginReqVO {
@Schema(description = "state", requiredMode = Schema.RequiredMode.REQUIRED, example = "9b2ffbc1-7425-4155-9894-9d5c08541d62")
private String socialState;
/**
* 开启验证码的 Group
*/
public interface CodeEnableGroup {}
@AssertTrue(message = "授权码不能为空")
public boolean isSocialCodeValid() {
return socialType == null || StrUtil.isNotEmpty(socialCode);

View File

@ -12,7 +12,7 @@ import javax.validation.constraints.Size;
@Schema(description = "管理后台 - Register Request VO")
@Data
public class AuthRegisterReqVO {
public class AuthRegisterReqVO extends CaptchaVerificationReqVO {
@Schema(description = "用户账号", requiredMode = Schema.RequiredMode.REQUIRED, example = "yudao")
@NotBlank(message = "用户账号不能为空")
@ -29,12 +29,4 @@ public class AuthRegisterReqVO {
@NotEmpty(message = "密码不能为空")
@Length(min = 4, max = 16, message = "密码长度为 4-16 位")
private String password;
// ========== 图片验证码相关 ==========
@Schema(description = "验证码,验证码开启时,需要传递", requiredMode = Schema.RequiredMode.REQUIRED,
example = "PfcH6mgr8tpXuMWFjvW6YVaqrswIuwmWI5dsVZSg7sGpWtDCUbHuDEXl3cFB1+VvCC/rAkSwK8Fad52FSuncVg==")
@NotEmpty(message = "验证码不能为空", groups = AuthLoginReqVO.CodeEnableGroup.class)
private String captchaVerification;
}

View File

@ -0,0 +1,33 @@
package cn.iocoder.yudao.module.system.controller.admin.auth.vo;
import cn.iocoder.yudao.framework.common.validation.Mobile;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.hibernate.validator.constraints.Length;
import javax.validation.constraints.NotEmpty;
@Schema(description = "管理后台 - 短信重置账号密码 Request VO")
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class AuthResetPasswordReqVO {
@Schema(description = "密码", requiredMode = Schema.RequiredMode.REQUIRED, example = "1234")
@NotEmpty(message = "密码不能为空")
@Length(min = 4, max = 16, message = "密码长度为 4-16 位")
private String password;
@Schema(description = "手机号", requiredMode = Schema.RequiredMode.REQUIRED, example = "13312341234")
@NotEmpty(message = "手机号不能为空")
@Mobile
private String mobile;
@Schema(description = "手机短信验证码", requiredMode = Schema.RequiredMode.REQUIRED, example = "123456")
@NotEmpty(message = "手机手机短信验证码不能为空")
private String code;
}

View File

@ -17,7 +17,7 @@ import javax.validation.constraints.NotNull;
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class AuthSmsSendReqVO {
public class AuthSmsSendReqVO extends CaptchaVerificationReqVO {
@Schema(description = "手机号", requiredMode = Schema.RequiredMode.REQUIRED, example = "yudaoyuanma")
@NotEmpty(message = "手机号不能为空")

View File

@ -0,0 +1,23 @@
package cn.iocoder.yudao.module.system.controller.admin.auth.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import javax.validation.constraints.NotEmpty;
@Schema(description = "管理后台 - 验证码 Request VO")
@Data
public class CaptchaVerificationReqVO {
// ========== 图片验证码相关 ==========
@Schema(description = "验证码,验证码开启时,需要传递", requiredMode = Schema.RequiredMode.REQUIRED,
example = "PfcH6mgr8tpXuMWFjvW6YVaqrswIuwmWI5dsVZSg7sGpWtDCUbHuDEXl3cFB1+VvCC/rAkSwK8Fad52FSuncVg==")
@NotEmpty(message = "验证码不能为空", groups = CodeEnableGroup.class)
private String captchaVerification;
/**
* 开启验证码的 Group
*/
public interface CodeEnableGroup {
}
}

View File

@ -1,4 +1,4 @@
### 请求 /menu/list 接口 => 成功
GET {{baseUrl}}/system/dict-data/list-all-simple
Authorization: Bearer {{token}}
tenant-id: {{adminTenentId}}
tenant-id: {{adminTenantId}}

View File

@ -1,5 +1,5 @@
### 获得地区树
GET {{baseUrl}}/system/area/tree
Authorization: Bearer {{token}}
tenant-id: {{adminTenentId}}
tenant-id: {{adminTenantId}}

View File

@ -1,4 +1,4 @@
### 请求 /system/operate-log/page 接口 => 成功
GET {{baseUrl}}/system/operate-log/page
Authorization: Bearer {{token}}
tenant-id: {{adminTenentId}}
tenant-id: {{adminTenantId}}

View File

@ -2,7 +2,7 @@
POST {{baseUrl}}/system/mail-template/send-mail
Authorization: Bearer {{token}}
Content-Type: application/json
tenant-id: {{adminTenentId}}
tenant-id: {{adminTenantId}}
{
"templateCode": "test_01",

View File

@ -2,7 +2,7 @@
POST {{baseUrl}}/system/oauth2-client/create
Content-Type: application/json
Authorization: Bearer {{token}}
tenant-id: {{adminTenentId}}
tenant-id: {{adminTenantId}}
{
"id": "1",

View File

@ -1,13 +1,13 @@
### 请求 /system/oauth2/authorize 接口 => 成功
GET {{baseUrl}}/system/oauth2/authorize?clientId=default
Authorization: Bearer {{token}}
tenant-id: {{adminTenentId}}
tenant-id: {{adminTenantId}}
### 请求 /system/oauth2/authorize + token 接口 => 成功
POST {{baseUrl}}/system/oauth2/authorize
Content-Type: application/x-www-form-urlencoded
Authorization: Bearer {{token}}
tenant-id: {{adminTenentId}}
tenant-id: {{adminTenantId}}
response_type=token&client_id=default&scope={"user.read": true}&redirect_uri=https://www.iocoder.cn&auto_approve=true
@ -15,7 +15,7 @@ response_type=token&client_id=default&scope={"user.read": true}&redirect_uri=htt
POST {{baseUrl}}/system/oauth2/authorize
Content-Type: application/x-www-form-urlencoded
Authorization: Bearer {{token}}
tenant-id: {{adminTenentId}}
tenant-id: {{adminTenantId}}
response_type=code&client_id=default&scope={"user.read": true}&redirect_uri=https://www.iocoder.cn&auto_approve=false
@ -23,7 +23,7 @@ response_type=code&client_id=default&scope={"user.read": true}&redirect_uri=http
POST {{baseUrl}}/system/oauth2/token
Content-Type: application/x-www-form-urlencoded
Authorization: Basic ZGVmYXVsdDphZG1pbjEyMw==
tenant-id: {{adminTenentId}}
tenant-id: {{adminTenantId}}
grant_type=authorization_code&redirect_uri=https://www.iocoder.cn&code=189956c07a174588a97157eabef2f93a
@ -31,7 +31,7 @@ grant_type=authorization_code&redirect_uri=https://www.iocoder.cn&code=189956c07
POST {{baseUrl}}/system/oauth2/token
Content-Type: application/x-www-form-urlencoded
Authorization: Basic ZGVmYXVsdDphZG1pbjEyMw==
tenant-id: {{adminTenentId}}
tenant-id: {{adminTenantId}}
grant_type=password&username=admin&password=admin123&scope=user.read
@ -39,16 +39,16 @@ grant_type=password&username=admin&password=admin123&scope=user.read
POST {{baseUrl}}/system/oauth2/token
Content-Type: application/x-www-form-urlencoded
Authorization: Basic ZGVmYXVsdDphZG1pbjEyMw==
tenant-id: {{adminTenentId}}
tenant-id: {{adminTenantId}}
grant_type=refresh_token&refresh_token=00895465d6994f72a9d926ceeed0f588
### 请求 /system/oauth2/token + DELETE 接口 => 成功
DELETE {{baseUrl}}/system/oauth2/token?token=ca8a188f464441d6949c51493a2b7596
Authorization: Basic ZGVmYXVsdDphZG1pbjEyMw==
tenant-id: {{adminTenentId}}
tenant-id: {{adminTenantId}}
### 请求 /system/oauth2/check-token 接口 => 成功
POST {{baseUrl}}/system/oauth2/check-token?token=620d307c5b4148df8a98dd6c6c547106
Authorization: Basic ZGVmYXVsdDphZG1pbjEyMw==
tenant-id: {{adminTenentId}}
tenant-id: {{adminTenantId}}

View File

@ -1,13 +1,13 @@
### 请求 /system/oauth2/user/get 接口 => 成功
GET {{baseUrl}}/system/oauth2/user/get
Authorization: Bearer 47f9c74ec11041f193b777ebb95c3b0d
tenant-id: {{adminTenentId}}
tenant-id: {{adminTenantId}}
### 请求 /system/oauth2/user/update 接口 => 成功
PUT {{baseUrl}}/system/oauth2/user/update
Content-Type: application/json
Authorization: Bearer 47f9c74ec11041f193b777ebb95c3b0d
tenant-id: {{adminTenentId}}
tenant-id: {{adminTenantId}}
{
"nickname": "芋道源码"

View File

@ -1,4 +1,4 @@
### 请求 /menu/list 接口 => 成功
GET {{baseUrl}}/system/menu/list
Authorization: Bearer {{token}}
tenant-id: {{adminTenentId}}
tenant-id: {{adminTenantId}}

View File

@ -2,7 +2,7 @@
POST {{baseUrl}}/system/role/create
Authorization: Bearer {{token}}
Content-Type: application/json
tenant-id: {{adminTenentId}}
tenant-id: {{adminTenantId}}
{
"name": "测试角色",
@ -14,7 +14,7 @@ tenant-id: {{adminTenentId}}
POST {{baseUrl}}/system/role/update
Authorization: Bearer {{token}}
Content-Type: application/json
tenant-id: {{adminTenentId}}
tenant-id: {{adminTenantId}}
{
"id": 100,
@ -26,7 +26,7 @@ tenant-id: {{adminTenentId}}
POST {{baseUrl}}/system/role/delete
Content-Type: application/x-www-form-urlencoded
Authorization: Bearer {{token}}
tenant-id: {{adminTenentId}}
tenant-id: {{adminTenantId}}
roleId=14
@ -34,9 +34,9 @@ roleId=14
GET {{baseUrl}}/system/role/get?id=100
Content-Type: application/x-www-form-urlencoded
Authorization: Bearer {{token}}
tenant-id: {{adminTenentId}}
tenant-id: {{adminTenantId}}
### /role/page 成功
GET {{baseUrl}}/system/role/page?pageNo=1&pageSize=10
Authorization: Bearer {{token}}
tenant-id: {{adminTenentId}}
tenant-id: {{adminTenantId}}

View File

@ -2,7 +2,7 @@
POST {{baseUrl}}/system/sms-template/send-sms
Authorization: Bearer {{token}}
Content-Type: application/json
tenant-id: {{adminTenentId}}
tenant-id: {{adminTenantId}}
{
"templateCode": "test_01",

View File

@ -3,7 +3,7 @@ POST {{baseUrl}}/system/social-client/send-subscribe-message
Authorization: Bearer {{token}}
Content-Type: application/json
#Authorization: Bearer test100
tenant-id: {{adminTenentId}}
tenant-id: {{adminTenantId}}
{
"userId": 247,

View File

@ -5,7 +5,7 @@ GET {{baseUrl}}/system/tenant/get-id-by-name?name=芋道源码
POST {{baseUrl}}/system/tenant/create
Content-Type: application/json
Authorization: Bearer {{token}}
tenant-id: {{adminTenentId}}
tenant-id: {{adminTenantId}}
{
"name": "芋道",

View File

@ -2,4 +2,4 @@
GET {{baseUrl}}/system/user/page?pageNo=1&pageSize=10
Authorization: Bearer {{token}}
#Authorization: Bearer test100
tenant-id: {{adminTenentId}}
tenant-id: {{adminTenantId}}

View File

@ -1,4 +1,4 @@
### 请求 /system/user/profile/get 接口 => 没有权限
GET {{baseUrl}}/system/user/profile/get
Authorization: Bearer {{token}}
tenant-id: {{adminTenentId}}
tenant-id: {{adminTenantId}}

View File

@ -23,7 +23,7 @@ public class UserSaveReqVO {
@Schema(description = "用户账号", requiredMode = Schema.RequiredMode.REQUIRED, example = "yudao")
@NotBlank(message = "用户账号不能为空")
@Pattern(regexp = "^[a-zA-Z0-9]{4,30}$", message = "用户账号由 数字、字母 组成")
@Pattern(regexp = "^[a-zA-Z0-9]+$", message = "用户账号由 数字、字母 组成")
@Size(min = 4, max = 30, message = "用户账号长度为 4-30 个字符")
@DiffLogField(name = "用户账号")
private String username;

View File

@ -52,7 +52,7 @@ public interface AdminAuthService {
* @param reqVO 登录信息
* @return 登录结果
*/
AuthLoginRespVO smsLogin(AuthSmsLoginReqVO reqVO) ;
AuthLoginRespVO smsLogin(AuthSmsLoginReqVO reqVO);
/**
* 社交快捷登录使用 code 授权码
@ -78,4 +78,11 @@ public interface AdminAuthService {
*/
AuthLoginRespVO register(AuthRegisterReqVO createReqVO);
/**
* 重置密码
*
* @param reqVO 验证码信息
*/
void resetPassword(AuthResetPasswordReqVO reqVO);
}

View File

@ -8,6 +8,7 @@ import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils;
import cn.iocoder.yudao.framework.common.util.validation.ValidationUtils;
import cn.iocoder.yudao.module.system.api.logger.dto.LoginLogCreateReqDTO;
import cn.iocoder.yudao.module.system.api.sms.SmsCodeApi;
import cn.iocoder.yudao.module.system.api.sms.dto.code.SmsCodeUseReqDTO;
import cn.iocoder.yudao.module.system.api.social.dto.SocialUserBindReqDTO;
import cn.iocoder.yudao.module.system.api.social.dto.SocialUserRespDTO;
import cn.iocoder.yudao.module.system.controller.admin.auth.vo.*;
@ -27,9 +28,11 @@ import com.google.common.annotations.VisibleForTesting;
import com.xingyuv.captcha.model.common.ResponseModel;
import com.xingyuv.captcha.model.vo.CaptchaVO;
import com.xingyuv.captcha.service.CaptchaService;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
import javax.validation.Validator;
@ -69,6 +72,7 @@ public class AdminAuthServiceImpl implements AdminAuthService {
* 验证码的开关默认为 true
*/
@Value("${yudao.captcha.enable:true}")
@Setter // 为了单测开启或者关闭验证码
private Boolean captchaEnable;
@Override
@ -111,6 +115,14 @@ public class AdminAuthServiceImpl implements AdminAuthService {
@Override
public void sendSmsCode(AuthSmsSendReqVO reqVO) {
// 如果是重置密码场景需要校验图形验证码是否正确
if (Objects.equals(SmsSceneEnum.ADMIN_MEMBER_RESET_PASSWORD.getScene(), reqVO.getScene())) {
ResponseModel response = doValidateCaptcha(reqVO);
if (!response.isSuccess()) {
throw exception(AUTH_REGISTER_CAPTCHA_CODE_ERROR, response.getRepMsg());
}
}
// 登录场景验证是否存在
if (userService.getUserByMobile(reqVO.getMobile()) == null) {
throw exception(AUTH_MOBILE_NOT_EXISTS);
@ -174,16 +186,8 @@ public class AdminAuthServiceImpl implements AdminAuthService {
@VisibleForTesting
void validateCaptcha(AuthLoginReqVO reqVO) {
// 如果验证码关闭则不进行校验
if (!captchaEnable) {
return;
}
ResponseModel response = doValidateCaptcha(reqVO);
// 校验验证码
ValidationUtils.validate(validator, reqVO, AuthLoginReqVO.CodeEnableGroup.class);
CaptchaVO captchaVO = new CaptchaVO();
captchaVO.setCaptchaVerification(reqVO.getCaptchaVerification());
ResponseModel response = captchaService.verification(captchaVO);
// 验证不通过
if (!response.isSuccess()) {
// 创建登录失败日志验证码不正确)
createLoginLog(null, reqVO.getUsername(), LoginLogTypeEnum.LOGIN_USERNAME, LoginResultEnum.CAPTCHA_CODE_ERROR);
@ -191,6 +195,17 @@ public class AdminAuthServiceImpl implements AdminAuthService {
}
}
private ResponseModel doValidateCaptcha(CaptchaVerificationReqVO reqVO) {
// 如果验证码关闭则不进行校验
if (!captchaEnable) {
return ResponseModel.success();
}
ValidationUtils.validate(validator, reqVO, CaptchaVerificationReqVO.CodeEnableGroup.class);
CaptchaVO captchaVO = new CaptchaVO();
captchaVO.setCaptchaVerification(reqVO.getCaptchaVerification());
return captchaService.verification(captchaVO);
}
private AuthLoginRespVO createTokenAfterLoginSuccess(Long userId, String username, LoginLogTypeEnum logType) {
// 插入登陆日志
createLoginLog(userId, username, logType, LoginResultEnum.SUCCESS);
@ -261,19 +276,28 @@ public class AdminAuthServiceImpl implements AdminAuthService {
@VisibleForTesting
void validateCaptcha(AuthRegisterReqVO reqVO) {
// 如果验证码关闭则不进行校验
if (!captchaEnable) {
return;
}
// 校验验证码
ValidationUtils.validate(validator, reqVO, AuthLoginReqVO.CodeEnableGroup.class);
CaptchaVO captchaVO = new CaptchaVO();
captchaVO.setCaptchaVerification(reqVO.getCaptchaVerification());
ResponseModel response = captchaService.verification(captchaVO);
ResponseModel response = doValidateCaptcha(reqVO);
// 验证不通过
if (!response.isSuccess()) {
throw exception(AUTH_REGISTER_CAPTCHA_CODE_ERROR, response.getRepMsg());
}
}
@Override
@Transactional(rollbackFor = Exception.class)
public void resetPassword(AuthResetPasswordReqVO reqVO) {
AdminUserDO userByMobile = userService.getUserByMobile(reqVO.getMobile());
if (userByMobile == null) {
throw exception(USER_MOBILE_NOT_EXISTS);
}
smsCodeApi.useSmsCode(new SmsCodeUseReqDTO()
.setCode(reqVO.getCode())
.setMobile(reqVO.getMobile())
.setScene(SmsSceneEnum.ADMIN_MEMBER_RESET_PASSWORD.getScene())
.setUsedIp(getClientIP())
);
userService.updateUserPassword(userByMobile.getId(), reqVO.getPassword());
}
}

View File

@ -86,6 +86,7 @@ public class RoleServiceImpl implements RoleService {
roleMapper.updateById(updateObj);
// 3. 记录操作日志上下文
LogRecordContext.putVariable(DiffParseFunction.OLD_OBJECT, BeanUtils.toBean(role, RoleSaveReqVO.class));
LogRecordContext.putVariable("role", role);
}
@ -118,7 +119,6 @@ public class RoleServiceImpl implements RoleService {
permissionService.processRoleDeleted(id);
// 3. 记录操作日志上下文
LogRecordContext.putVariable(DiffParseFunction.OLD_OBJECT, BeanUtils.toBean(role, RoleSaveReqVO.class));
LogRecordContext.putVariable("role", role);
}