WangHan
2025-04-02 a8ba678a3fe5a39da2c732014cebbb66e408e97c
问题与漏洞修改
465个文件已添加
128个文件已修改
57232 ■■■■■ 已修改文件
admin-web/public/static/config.js 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
admin-web/src/api/foudation/material.js 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
admin-web/src/views/foundation/material/edit.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
consum-base/pom.xml 29 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
consum-base/src/main/java/com/consum/base/aop/DictDataAop.java 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
consum-base/src/main/java/com/consum/base/controller/BaseCategoryController.java 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
consum-base/src/main/java/com/consum/base/controller/BaseGoodsModelsController.java 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
consum-base/src/main/java/com/consum/base/controller/BaseGoodsTemplateController.java 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
consum-base/src/main/java/com/consum/base/controller/BaseWarehouseController.java 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
consum-base/src/main/java/com/consum/base/controller/BaseWarehouseManagerController.java 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
consum-base/src/main/java/com/consum/base/controller/DepFormScrappedController.java 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
consum-base/src/main/java/com/consum/base/controller/FinDepartLedgerController.java 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
consum-base/src/main/java/com/consum/base/controller/FinFileController.java 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
consum-base/src/main/java/com/consum/base/controller/FinSysServerController.java 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
consum-base/src/main/java/com/consum/base/controller/FinSysTenantController.java 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
consum-base/src/main/java/com/consum/base/controller/FinSysTenantDepartmentController.java 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
consum-base/src/main/java/com/consum/base/controller/FinSysTenantUserController.java 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
consum-base/src/main/java/com/consum/base/controller/FinWarehouseLedgerController.java 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
consum-base/src/main/java/com/consum/base/controller/LOrgSupplierController.java 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
consum-base/src/main/java/com/consum/base/controller/LWarehouseFlowController.java 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
consum-base/src/main/java/com/consum/base/controller/LWhFormInventoryController.java 70 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
consum-base/src/main/java/com/consum/base/controller/LWhFormInventoryGoodsController.java 7 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
consum-base/src/main/java/com/consum/base/controller/LWhFormOutputController.java 38 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
consum-base/src/main/java/com/consum/base/controller/LWhFormProcureController.java 34 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
consum-base/src/main/java/com/consum/base/controller/LWhFormScrappedController.java 37 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
consum-base/src/main/java/com/consum/base/controller/LWhFormTransferController.java 86 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
consum-base/src/main/java/com/consum/base/controller/LWhGoodsStatisticsController.java 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
consum-base/src/main/java/com/consum/base/controller/LWhHomeStatisticsController.java 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
consum-base/src/main/java/com/consum/base/controller/LWhProcureModelController.java 7 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
consum-base/src/main/java/com/consum/base/controller/LWhWarningConfigController.java 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
consum-base/src/main/java/com/consum/base/controller/LWhWarningController.java 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
consum-base/src/main/java/com/consum/base/controller/UsingFormBackController.java 14 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
consum-base/src/main/java/com/consum/base/core/CodeGeneratorService.java 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
consum-base/src/main/java/com/consum/base/core/utils/CommonUtil.java 5 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
consum-base/src/main/java/com/consum/base/pojo/DepFormScrappedGoodsParam.java 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
consum-base/src/main/java/com/consum/base/pojo/DepFormScrappedModelParam.java 24 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
consum-base/src/main/java/com/consum/base/pojo/FinSysTenantDepartmentParam.java 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
consum-base/src/main/java/com/consum/base/pojo/LDeptFormScrappedParam.java 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
consum-base/src/main/java/com/consum/base/pojo/LWFormsOutputGoodsInfoParam.java 12 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
consum-base/src/main/java/com/consum/base/pojo/LWFormsOutputGoodsModelParam.java 16 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
consum-base/src/main/java/com/consum/base/pojo/LWarehouseFlowParam.java 38 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
consum-base/src/main/java/com/consum/base/pojo/LWhFormOutputParam.java 14 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
consum-base/src/main/java/com/consum/base/pojo/LWhFormScrappedGoodsInfoParam.java 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
consum-base/src/main/java/com/consum/base/pojo/LWhFormScrappedGoodsModelParams.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
consum-base/src/main/java/com/consum/base/pojo/LWhFormScrappedParam.java 20 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
consum-base/src/main/java/com/consum/base/pojo/LWhFormTransferGoodsInfoParam.java 10 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
consum-base/src/main/java/com/consum/base/pojo/LWhProcureModelParam.java 18 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
consum-base/src/main/java/com/consum/base/pojo/LWhProcureModelUserParam.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
consum-base/src/main/java/com/consum/base/pojo/LWhTransferModelParam.java 22 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
consum-base/src/main/java/com/consum/base/pojo/WarehouseManagerInfo.java 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
consum-base/src/main/java/com/consum/base/pojo/WarnConfImEntity.java 18 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
consum-base/src/main/java/com/consum/base/pojo/WhWarningConfigParam.java 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
consum-base/src/main/java/com/consum/base/pojo/query/FormProcureQry.java 30 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
consum-base/src/main/java/com/consum/base/pojo/query/LDeptFormScrappedQry.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
consum-base/src/main/java/com/consum/base/pojo/query/LWhFormInventoryQry.java 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
consum-base/src/main/java/com/consum/base/pojo/query/LWhFormOutputQry.java 36 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
consum-base/src/main/java/com/consum/base/pojo/query/LWhFormScrappedQry.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
consum-base/src/main/java/com/consum/base/pojo/query/LWhGoodsStatisQry.java 24 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
consum-base/src/main/java/com/consum/base/pojo/query/LWhLedgerQry.java 24 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
consum-base/src/main/java/com/consum/base/pojo/query/LWhProcureModelQry.java 12 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
consum-base/src/main/java/com/consum/base/pojo/query/TransferQry.java 38 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
consum-base/src/main/java/com/consum/base/pojo/query/UsingFormBackQry.java 30 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
consum-base/src/main/java/com/consum/base/pojo/query/WarehouseQry.java 18 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
consum-base/src/main/java/com/consum/base/pojo/query/WhWarningConfigQry.java 22 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
consum-base/src/main/java/com/consum/base/pojo/query/WhWarningQry.java 28 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
consum-base/src/main/java/com/consum/base/pojo/request/FormInventoryParam.java 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
consum-base/src/main/java/com/consum/base/pojo/request/LWhFormInventoryParam.java 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
consum-base/src/main/java/com/consum/base/pojo/request/LWhFormTransferParam.java 28 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
consum-base/src/main/java/com/consum/base/pojo/request/RecordInfoParam.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
consum-base/src/main/java/com/consum/base/pojo/request/UsingFormBackGoodsInfo.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
consum-base/src/main/java/com/consum/base/pojo/request/UsingFormBackParam.java 20 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
consum-base/src/main/java/com/consum/base/pojo/request/WarehouseManagerParam.java 10 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
consum-base/src/main/java/com/consum/base/pojo/request/baseGoodModel.java 12 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
consum-base/src/main/java/com/consum/base/pojo/response/DepartGoodsUseInfo.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
consum-base/src/main/java/com/consum/base/pojo/response/FinSysTenantUserVO.java 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
consum-base/src/main/java/com/consum/base/pojo/response/FormInventoryDetailVO.java 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
consum-base/src/main/java/com/consum/base/pojo/response/FormInventoryGoodsVO.java 24 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
consum-base/src/main/java/com/consum/base/pojo/response/FormInventoryVO.java 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
consum-base/src/main/java/com/consum/base/pojo/response/FormOutputVO.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
consum-base/src/main/java/com/consum/base/pojo/response/FormProcureVO.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
consum-base/src/main/java/com/consum/base/pojo/response/FormScrappedGoodsDetailVO.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
consum-base/src/main/java/com/consum/base/pojo/response/FormTransferVO.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
consum-base/src/main/java/com/consum/base/pojo/response/GoodsModelVO.java 18 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
consum-base/src/main/java/com/consum/base/pojo/response/GoodsStatisticsInfoVO.java 26 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
consum-base/src/main/java/com/consum/base/pojo/response/GoodsTemplateCountVO.java 20 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
consum-base/src/main/java/com/consum/base/pojo/response/GoodsTemplateInfoVO.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
consum-base/src/main/java/com/consum/base/pojo/response/GoodsTemplateVO.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
consum-base/src/main/java/com/consum/base/pojo/response/LWHFromTransferExtendVO.java 64 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
consum-base/src/main/java/com/consum/base/pojo/response/LWhFormOutputExtendVO.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
consum-base/src/main/java/com/consum/base/pojo/response/LWhFormScrappedExtendVO.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
consum-base/src/main/java/com/consum/base/pojo/response/LWhFormScrappedVO.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
consum-base/src/main/java/com/consum/base/pojo/response/TransferInfoVO.java 18 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
consum-base/src/main/java/com/consum/base/pojo/response/UseInfo.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
consum-base/src/main/java/com/consum/base/pojo/response/UsingFormBackDetailListVO.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
consum-base/src/main/java/com/consum/base/pojo/response/UsingFormBackGoodsTemplateInfo.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
consum-base/src/main/java/com/consum/base/pojo/response/UsingFormBackModelInfo.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
consum-base/src/main/java/com/consum/base/pojo/response/WarehouseFlowVO.java 26 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
consum-base/src/main/java/com/consum/base/service/LGoodsUserRecordCoreService.java 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
consum-base/src/main/java/com/consum/base/service/core/DepFormScrappedCoreService.java 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
consum-base/src/main/java/com/consum/base/service/core/DepUsingFormBackCoreService.java 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
consum-base/src/main/java/com/consum/base/service/core/LWarehouseFlowCoreService.java 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
consum-base/src/main/java/com/consum/base/service/core/LWhFormInventoryCoreService.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
consum-base/src/main/java/com/consum/base/service/core/LWhFormOutputCoreService.java 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
consum-base/src/main/java/com/consum/base/service/core/LWhFormProcureCoreService.java 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
consum-base/src/main/java/com/consum/base/service/core/LWhFormTransferCoreService.java 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
consum-base/src/main/java/com/consum/base/service/core/LWhWarningCoreService.java 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
consum-base/src/main/java/com/consum/base/service/impl/DepFormScrappedServiceImpl.java 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
consum-base/src/main/java/com/consum/base/service/impl/LWhFormInventoryServiceImpl.java 4 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
consum-base/src/main/java/com/consum/base/service/impl/LWhFormProcureServiceImpl.java 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
consum-base/src/main/java/com/consum/base/service/impl/LWhGoodsRecordDetailsService.java 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
consum-base/src/main/java/com/consum/base/service/impl/UsingFormBackServiceImpl.java 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
consum-model-pojo/pom.xml 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
deploy-jar-single/pom.xml 88 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
deploy-jar-single/src/main/resources/application-dev.yml 22 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
deploy-jar-single/src/main/resources/application-prod.yml 12 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
deploy-jar-single/src/main/resources/application-test.yml 12 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
deploy-jar-single/src/main/resources/application.yml 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base-admin/pom.xml 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base-admin/src/main/java/com/iplatform/base/IdUtil.java 78 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base-admin/src/main/java/com/iplatform/base/Test1.java 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base-admin/src/main/java/com/iplatform/base/config/SwaggerConfig.java 70 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base-admin/src/main/java/com/iplatform/base/config/SwaggerProperties.java 35 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base-admin/src/main/java/com/iplatform/base/controller/ApiTimeController.java 49 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base-admin/src/main/java/com/iplatform/base/controller/CacheController.java 154 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base-admin/src/main/java/com/iplatform/base/controller/CategoryController.java 155 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base-admin/src/main/java/com/iplatform/base/controller/CodeController.java 237 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base-admin/src/main/java/com/iplatform/base/controller/ConfigController.java 175 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base-admin/src/main/java/com/iplatform/base/controller/ConfigFormController.java 127 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base-admin/src/main/java/com/iplatform/base/controller/DeptController.java 283 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base-admin/src/main/java/com/iplatform/base/controller/GroupController.java 164 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base-admin/src/main/java/com/iplatform/base/controller/LoginInfoController.java 47 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base-admin/src/main/java/com/iplatform/base/controller/MenuController.java 154 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base-admin/src/main/java/com/iplatform/base/controller/NotificationController.java 133 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base-admin/src/main/java/com/iplatform/base/controller/OnlineUserController.java 48 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base-admin/src/main/java/com/iplatform/base/controller/OperateLogController.java 57 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base-admin/src/main/java/com/iplatform/base/controller/PermitController.java 128 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base-admin/src/main/java/com/iplatform/base/controller/RoleController.java 189 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base-admin/src/main/java/com/iplatform/base/controller/SchedulerController.java 74 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base-admin/src/main/java/com/iplatform/base/controller/UserController.java 386 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base-admin/src/main/java/com/iplatform/base/controller/UserProfileController.java 201 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base-admin/src/main/java/com/iplatform/base/excel/UserDataImportor.java 126 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base-security-consum/pom.xml 66 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base-security-consum/src/main/java/com/iplatform/security/DefaultAuthenticationFailureHandler.java 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base-security-consum/src/main/java/com/iplatform/security/DefaultAuthenticationFilter.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base-security-consum/src/main/java/com/iplatform/security/DefaultAuthenticationSuccessHandler.java 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base-security-consum/src/main/java/com/iplatform/security/DefaultLogoutSuccessHandler.java 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base-security-consum/src/main/java/com/iplatform/security/DefaultSecuritySpi.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base-security-consum/src/main/java/com/iplatform/security/FailedAuthenticationEntryPoint.java 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base-security-consum/src/main/java/com/iplatform/security/JwtAuthenticationTokenFilter.java 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base-security-consum/src/main/java/com/iplatform/security/config/WebSecurityConfig.java 128 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base-security-consum/src/main/java/com/iplatform/security/controller/SecurityController.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base-tcp/pom.xml 225 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base-tcp/src/main/java/com/iplatform/CtomsTcpApplication.java 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base-tcp/src/main/java/com/iplatform/tcp/Constants.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base-tcp/src/main/java/com/iplatform/tcp/EngineType.java 37 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base-tcp/src/main/java/com/iplatform/tcp/EquipmentCacheProvider.java 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base-tcp/src/main/java/com/iplatform/tcp/EquipmentStatusCacheProvider.java 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base-tcp/src/main/java/com/iplatform/tcp/LiveStatus.java 44 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base-tcp/src/main/java/com/iplatform/tcp/TcpBaseController.java 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base-tcp/src/main/java/com/iplatform/tcp/cache/LocalEquipStatusCacheProvider.java 57 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base-tcp/src/main/java/com/iplatform/tcp/cache/LocalEquipmentCacheProvider.java 57 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base-tcp/src/main/java/com/iplatform/tcp/cache/RedisEquipStatusCacheProvider.java 69 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base-tcp/src/main/java/com/iplatform/tcp/cache/RedisEquipmentCacheProvider.java 69 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base-tcp/src/main/java/com/iplatform/tcp/config/LoadBalanceMQConfig.java 82 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base-tcp/src/main/java/com/iplatform/tcp/config/LocalTcpCacheConfig.java 43 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base-tcp/src/main/java/com/iplatform/tcp/config/RedisTcpCacheConfig.java 69 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base-tcp/src/main/java/com/iplatform/tcp/config/TcpBeanPostProcessorConfig.java 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base-tcp/src/main/java/com/iplatform/tcp/config/TcpConfig.java 213 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base-tcp/src/main/java/com/iplatform/tcp/config/WebsocketConfig.java 147 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base-tcp/src/main/java/com/iplatform/tcp/controller/ConnectionManagerController.java 42 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base-tcp/src/main/java/com/iplatform/tcp/controller/EquipController.java 43 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base-tcp/src/main/java/com/iplatform/tcp/controller/TestTcpController.java 179 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base-tcp/src/main/java/com/iplatform/tcp/lb/DefaultLbConnectionManager.java 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base-tcp/src/main/java/com/iplatform/tcp/lb/DefaultMqListener.java 96 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base-tcp/src/main/java/com/iplatform/tcp/lb/DefaultResponseWriter.java 51 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base-tcp/src/main/java/com/iplatform/tcp/lb/MqConnectionMeta.java 81 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base-tcp/src/main/java/com/iplatform/tcp/lb/MqResponseUtils.java 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base-tcp/src/main/java/com/iplatform/tcp/lb/SendStatusCallback.java 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base-tcp/src/main/java/com/iplatform/tcp/lb/SimpleMqListener.java 144 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base-tcp/src/main/java/com/iplatform/tcp/pojo/EquipParam.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base-tcp/src/main/java/com/iplatform/tcp/service/TcpEquipServiceImpl.java 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base-tcp/src/main/java/com/iplatform/tcp/service/TcpEquipStatusServiceImpl.java 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base-tcp/src/main/java/com/iplatform/tcp/support/PersistentConnectionManager.java 133 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base-tcp/src/main/java/com/iplatform/tcp/support/PlatformSharpProtocolResolver.java 55 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base-tcp/src/main/java/com/iplatform/tcp/support/TestConnectionCallback.java 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base-tcp/src/main/java/com/iplatform/tcp/support/WebSocketPush.java 131 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base-tcp/src/main/java/com/iplatform/tcp/util/LocationNowAction.java 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base-tcp/src/main/java/com/iplatform/tcp/util/ws/LoginAction.java 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base-tcp/src/main/java/com/iplatform/tcp/util/ws/LoginRequest.java 34 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base-tcp/src/main/java/com/iplatform/tcp/util/ws/LoginResponse.java 43 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base-tcp/src/main/java/com/iplatform/tcp/util/ws/OfflineResponse.java 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base-tcp/src/main/java/com/iplatform/tcp/util/ws/OnlineResponse.java 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base-tcp/src/main/java/com/iplatform/tcp/util/ws/StepTimeFilter.java 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base-tcp/src/main/java/com/iplatform/tcp/util/ws/TestHelloAction.java 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base-tcp/src/main/java/com/iplatform/tcp/util/ws/WebBroadCastResponse.java 50 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base-tcp/src/main/java/com/iplatform/tcp/util/ws/WebDataResponse.java 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base-tcp/src/main/resources/application-dev.yml 476 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base-tcp/src/main/resources/application-line-dev.yml 480 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base-tcp/src/main/resources/application-line-prod.yml 480 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base-tcp/src/main/resources/application-test.yml 485 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base-tcp/src/main/resources/application.yml 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base-tcp/src/main/resources/wk_sn_lic.bin 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base-tcp/src/test/java/com/ctoms/AppTest.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base-tcp/src/test/java/com/ctoms/tcp/DemoLoginResponse.java 51 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base-tcp/src/test/java/com/ctoms/tcp/DemoWebsocketClient.java 81 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base-tcp/src/test/java/com/ctoms/tcp/WebsocketClientTest.java 101 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base-tcp/src/test/java/com/ctoms/tcp/WebsocketDemo.java 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/doc/iplatform_base.sql 35 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/pom.xml 277 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/AbstractController.java 360 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/AbstractFileOperateSpiController.java 339 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/AbstractSecurityController.java 129 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/ArgumentsConstants.java 103 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/AsyncManager.java 67 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/CategoryCacheProvider.java 34 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/Constants.java 214 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/DefaultUserPrincipal.java 148 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/DeptCacheProvider.java 47 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/FileOperateSpi.java 82 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/LocalDatabaseMetaEngine.java 100 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/NotificationTemplateCache.java 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/NotifyConstants.java 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/PlatformAdapterController.java 189 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/PlatformLoginCallback.java 61 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/PlatformRuntimeException.java 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/PlatformUserCallback.java 34 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/PushCacheProvider.java 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/PushController.java 106 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/PushData.java 99 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/RootConfigBean.java 83 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/SecurityConstants.java 38 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/SecuritySpi.java 81 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/SystemController.java 551 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/SystemGroupCache.java 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/ThirdPartyAuthentication.java 38 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/ThirdPartyManager.java 57 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/UserCacheProvider.java 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/UserLoginCache.java 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/VariableConstants.java 37 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/WechatBaseController.java 217 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/WechatCacheProvider.java 56 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/WechatConstants.java 490 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/api/UserAndDeptApi.java 40 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/cache/DictCacheProvider.java 277 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/cache/FormCacheProvider.java 135 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/cache/LocalCaptchaCacheProvider.java 47 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/cache/LocalCategoryCacheProvider.java 144 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/cache/LocalDeptCacheProvider.java 146 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/cache/LocalHostCacheProvider.java 50 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/cache/LocalNotificationTemplateCache.java 88 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/cache/LocalPushCacheProvider.java 43 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/cache/LocalSystemGroupCache.java 81 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/cache/LocalUserCacheProvider.java 81 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/cache/LocalUserLoginCache.java 46 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/cache/LocalUserOnlineProvider.java 88 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/cache/LocalWechatCache.java 63 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/cache/MenuCacheProvider.java 597 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/cache/RedisCaptchaCacheProvider.java 35 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/cache/RedisCategoryCacheProvider.java 123 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/cache/RedisDeptCacheProvider.java 213 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/cache/RedisHostCacheProvider.java 61 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/cache/RedisMenuUpdateCache.java 42 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/cache/RedisNotificationTemplateCache.java 103 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/cache/RedisPushCacheProvider.java 35 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/cache/RedisSystemGroupCache.java 95 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/cache/RedisUserCacheProvider.java 133 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/cache/RedisUserLoginCache.java 44 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/cache/RedisUserOnlineProvider.java 169 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/cache/RedisWechatCache.java 66 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/callback/AfterLoginCallback.java 42 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/callback/GeneralLoginCallback.java 92 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/callback/PlatformCallbackPostProcessor.java 120 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/callback/SecurityCallback.java 69 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/callback/TestAfterLoginCallback.java 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/callback/TestUserCallback.java 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/callback/UserProfileCallback.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/captcha/AbstractCaptchaProvider.java 78 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/captcha/BlockPuzzleCaptchaProvider.java 375 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/captcha/DefaultCaptchaProvider.java 37 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/captcha/JigsawCaptchaProvider.java 101 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/captcha/JigsawResult.java 48 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/captcha/NoneCaptchaProvider.java 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/captcha/SmsCaptchaProvider.java 83 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/captcha/TextCaptchaProvider.java 37 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/captcha/ThirdPartyCaptchaProvider.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/config/ApiProperties.java 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/config/BeanPostProcessorConfig.java 82 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/config/CacheConfiguration.java 64 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/config/CacheProperties.java 63 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/config/CaptchaConfig.java 103 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/config/CaptchaProperties.java 42 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/config/DataImportConfig.java 51 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/config/DatabaseMetaConfig.java 43 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/config/DruidMonitorConfig.java 119 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/config/FileProperties.java 109 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/config/JacksonConfig.java 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/config/LocalCacheConfig.java 176 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/config/LogProperties.java 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/config/LoginStrategyProperties.java 41 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/config/PushConfig.java 129 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/config/PushProperties.java 83 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/config/PushWechatConfig.java 36 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/config/RedisCacheConfig.java 288 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/config/RestTemplateProperties.java 73 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/config/SecurityUserProperties.java 36 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/config/SpiConfig.java 97 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/config/TcpProperties.java 163 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/config/ThirdPartyConfig.java 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/config/ThreadPoolConfig.java 58 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/config/WebCommonConfig.java 282 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/controller/CaptchaController.java 246 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/controller/IndexController.java 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/controller/TestMenuController.java 57 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/controller/WechatCallbackApi.java 47 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/di/AbstractDataImportEngine.java 78 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/di/DataImportEngine.java 44 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/di/FileDataImportEngine.java 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/di/JdbcExcelDataImportor.java 183 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/di/PlatformDataImportEngine.java 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/di/PlatformExcelTemplateGenerator.java 71 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/di/TemplateInfo.java 43 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/event/RoleSecurityChangeEvent.java 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/exception/CaptchaException.java 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/exception/LoginException.java 37 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/pojo/CaptchaParam.java 88 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/pojo/ConfigParam.java 39 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/pojo/DeptParam.java 59 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/pojo/GroupDataParam.java 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/pojo/KeywordsParam.java 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/pojo/MenuParam.java 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/pojo/RequestLogin.java 184 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/pojo/UserInfo.java 112 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/pojo/UserInfoRequest.java 43 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/pojo/UserParam.java 48 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/pojo/dict/DictParam.java 83 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/pojo/form/FormData.java 56 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/pojo/form/FormDataItem.java 59 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/pojo/form/RequestForm.java 54 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/pojo/group/GroupData.java 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/pojo/log/LoginLogParam.java 147 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/pojo/log/OperateLogParam.java 154 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/pojo/notify/InfoParam.java 35 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/pojo/notify/NotificationParam.java 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/pojo/role/RoleAuthParam.java 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/pojo/role/RoleParam.java 42 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/pojo/role/RoleUserParam.java 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/push/DefaultPushListener.java 184 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/push/DefaultPushManager.java 90 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/push/MockSmsPush.java 48 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/push/SystemPush.java 45 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/service/ApiTimeServiceImpl.java 96 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/service/CategoryServiceImpl.java 212 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/service/CodeServiceImpl.java 187 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/service/CommonServiceImpl.java 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/service/ConfigArgumentServiceImpl.java 70 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/service/ConfigFormServiceImpl.java 35 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/service/DataImportServiceImpl.java 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/service/DeptServiceImpl.java 105 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/service/GroupServiceImpl.java 78 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/service/LogServiceImpl.java 112 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/service/LoginServiceImpl.java 99 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/service/MenuServiceImpl.java 107 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/service/NotificationServiceImpl.java 41 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/service/PushServiceImpl.java 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/service/RoleServiceImpl.java 303 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/service/UserDeptApiServiceImpl.java 152 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/service/UserServiceImpl.java 386 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/service_api/UserAndDeptServiceApi.java 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/support/CtomsDataSource.java 167 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/support/DatabaseArgumentsManager.java 116 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/support/DeptTreeGenerator.java 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/support/DictTreeGenerator.java 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/support/LoadBalanceFileOperateSpi.java 103 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/support/LogAspect.java 233 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/support/NothingSecuritySpi.java 65 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/support/PlatformOperationInterceptor.java 54 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/support/RedisArgumentsManager.java 158 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/support/TimeStatisticsInterceptor.java 95 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/support/strategy/AbstractLoginStrategy.java 41 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/support/strategy/LoginStrategyManager.java 81 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/support/strategy/MobileOnceLoginStrategy.java 51 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/support/strategy/WebOnceLoginStrategy.java 48 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/util/ArgumentsManagerUtils.java 40 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/util/CaptchaUtils.java 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/util/CategoryUtils.java 87 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/util/ConfigFormValidateUtils.java 83 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/util/DataImportUtils.java 220 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/util/DeptUtils.java 56 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/util/MenuUtils.java 272 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/util/NotificationUtils.java 167 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/util/PlatformRSAUtils.java 134 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/util/RandomUtils.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/util/ResponseValueUtils.java 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/util/RestTemplateUtils.java 61 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/util/RoleUtils.java 41 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/util/TextUtils.java 83 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/util/TokenUtils.java 129 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/util/UserUtils.java 244 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/util/VerifyImgUtil.java 209 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/util/cache/CacheInfo.java 69 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/util/cache/CategorySortComparator.java 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/util/dept/SystemDept.java 42 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/util/dict/SystemDictData.java 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/util/menu/MenuOrderNumComparator.java 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/util/menu/MenuTree.java 67 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/util/menu/ParentMenuComparator.java 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/util/menu/SystemMenu.java 41 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/util/role/SystemRole.java 52 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/java/com/iplatform/base/util/user/SystemUser.java 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/resources/images/demo.txt 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/resources/images/jigsaw/original/1.png 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/resources/images/jigsaw/original/2.png 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/resources/images/jigsaw/original/3.png 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/resources/images/jigsaw/original/4.png 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/resources/images/jigsaw/original/5.png 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/resources/images/jigsaw/original/6.png 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/resources/images/jigsaw/slidingBlock/1.png 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/resources/images/jigsaw/slidingBlock/2.png 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/resources/images/jigsaw/slidingBlock/3.png 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/resources/images/jigsaw/slidingBlock/4.png 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/resources/images/jigsaw/slidingBlock/5.png 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/resources/images/jigsaw/slidingBlock/6.png 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/resources/images/jigsaw_bg/1.jpg 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/resources/images/jigsaw_bg/10.jpg 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/resources/images/jigsaw_bg/11.jpg 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/resources/images/jigsaw_bg/11.png 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/resources/images/jigsaw_bg/12.jpg 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/resources/images/jigsaw_bg/13.jpg 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/resources/images/jigsaw_bg/14.jpg 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/resources/images/jigsaw_bg/15.jpg 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/resources/images/jigsaw_bg/16.jpg 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/resources/images/jigsaw_bg/2.jpg 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/resources/images/jigsaw_bg/3.jpg 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/resources/images/jigsaw_bg/4.jpg 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/resources/images/jigsaw_bg/5.jpg 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/resources/images/jigsaw_bg/6.jpg 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/resources/images/jigsaw_bg/7.jpg 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/resources/images/jigsaw_bg/8.jpg 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/resources/images/jigsaw_bg/9.jpg 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/resources/images/jigsaw_block/1.png 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/resources/images/jigsaw_block/10.png 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/resources/images/jigsaw_block/11.png 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/resources/images/jigsaw_block/12.png 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/resources/images/jigsaw_block/13.png 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/resources/images/jigsaw_block/14.png 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/resources/images/jigsaw_block/15.png 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/resources/images/jigsaw_block/16.png 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/resources/images/jigsaw_block/17.png 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/resources/images/jigsaw_block/18.png 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/resources/images/jigsaw_block/19.png 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/resources/images/jigsaw_block/2.png 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/resources/images/jigsaw_block/20.png 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/resources/images/jigsaw_block/21.png 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/resources/images/jigsaw_block/22.png 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/resources/images/jigsaw_block/3.png 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/resources/images/jigsaw_block/4.png 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/resources/images/jigsaw_block/5.png 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/resources/images/jigsaw_block/6.png 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/resources/images/jigsaw_block/7.png 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/resources/images/jigsaw_block/8.png 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/main/resources/images/jigsaw_block/9.png 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/test/java/com/iplatform/base/MenuCacheProvider.java 529 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/test/java/com/iplatform/base/TestCaptcha.java 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/test/java/com/iplatform/base/TestEntity.java 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/test/java/com/iplatform/base/TestMenu.java 194 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-base/src/test/java/com/iplatform/base/redis/MenuCacheProvider.java 541 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-core/pom.xml 196 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-core/src/main/java/com/iplatform/core/BeanContextAware.java 51 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-core/src/main/java/com/iplatform/core/LoginStrategy.java 39 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-core/src/main/java/com/iplatform/core/PlatformConfiguration.java 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-core/src/main/java/com/iplatform/core/PlatformException.java 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-core/src/main/java/com/iplatform/core/RegularConstants.java 97 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-core/src/main/java/com/iplatform/core/SimpleVariable.java 124 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-core/src/main/java/com/iplatform/core/TokenAwareContext.java 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-core/src/main/java/com/iplatform/core/TokenEntity.java 43 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-core/src/main/java/com/iplatform/core/UserMerchantType.java 78 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-core/src/main/java/com/iplatform/core/config/BasicConfig.java 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-core/src/main/java/com/iplatform/core/config/LoadBalanceProperties.java 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-core/src/main/java/com/iplatform/core/config/PropertyPostProcessorConfig.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-core/src/main/java/com/iplatform/core/config/enc/EncryptionWrapperDetector.java 40 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-core/src/main/java/com/iplatform/core/config/enc/PropertySourcePostProcessor.java 89 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-core/src/main/java/com/iplatform/core/config/enc/PropertySourceWrapper.java 40 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-core/src/main/java/com/iplatform/core/util/AESUtils.java 197 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-core/src/main/java/com/iplatform/core/util/ThreadUtils.java 98 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-core/src/main/java/com/iplatform/core/workflow/AbstractContext.java 126 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-core/src/main/java/com/iplatform/core/workflow/Actorable.java 66 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-core/src/main/java/com/iplatform/core/workflow/Constants.java 92 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-core/src/main/java/com/iplatform/core/workflow/Context.java 96 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-core/src/main/java/com/iplatform/core/workflow/WorkflowCallback.java 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-core/src/main/java/com/iplatform/core/workflow/WorkflowForm.java 157 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-file-server/pom.xml 40 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-file-server/src/main/java/com/iplatform/App.java 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-file-server/src/main/java/com/iplatform/file/DefaultFileOperateSpi.java 231 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-file-server/src/main/java/com/iplatform/file/FileEngineFactory.java 301 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-file-server/src/main/java/com/iplatform/file/FileStoreCallback.java 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-file-server/src/main/java/com/iplatform/file/config/FileConfig.java 208 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-file-server/src/main/java/com/iplatform/file/config/FileWebConfig.java 40 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-file-server/src/main/java/com/iplatform/file/config/FtpProperties.java 66 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-file-server/src/main/java/com/iplatform/file/controller/AttachmentController.java 121 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-file-server/src/main/java/com/iplatform/file/controller/FileController.java 44 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-file-server/src/main/java/com/iplatform/file/controller/OssFileApi.java 116 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-file-server/src/main/java/com/iplatform/file/service/FileServiceImpl.java 72 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-file-server/src/main/java/com/iplatform/file/support/AbstractOssFileEngine.java 58 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-file-server/src/main/java/com/iplatform/file/support/AliOssFileEngine.java 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-file-server/src/main/java/com/iplatform/file/support/AttachmentJdbcCallback.java 130 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-file-server/src/main/java/com/iplatform/file/support/AwsOssFileEngine.java 359 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-file-server/src/main/java/com/iplatform/file/support/DefaultFileSystemEngine.java 34 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-file-server/src/main/java/com/iplatform/file/support/DefaultFtpFileEngine.java 39 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-file-server/src/main/java/com/iplatform/file/support/JdbcCallback.java 124 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-file-server/src/main/java/com/iplatform/file/support/QnOssFileEngine.java 53 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-file-server/src/main/java/com/iplatform/file/support/TxOssFileEngine.java 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-file-server/src/main/java/com/iplatform/file/util/FileResultUtils.java 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-file-server/src/main/java/com/iplatform/file/util/FileStoreUtils.java 51 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-file-server/src/main/java/com/iplatform/file/util/ImageUtils.java 84 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-file-server/src/main/java/com/iplatform/model/po/S_file.java 310 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-file-server/src/main/java/com/iplatform/model/po/S_file_mapper.java 341 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-file-server/src/main/java/com/iplatform/model/po/SfAttachment.java 376 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-file-server/src/main/java/com/iplatform/model/po/SfAttachment_mapper.java 387 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-file-server/src/main/java/com/iplatform/model/vo/FileResultVo.java 75 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-file-server/src/test/java/com/iplatform/file/TestOss.java 65 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-model-pojo/pom.xml 73 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-model-pojo/src/main/java/com/iplatform/model/po/S_category.java 332 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-model-pojo/src/main/java/com/iplatform/model/po/S_category_mapper.java 371 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-model-pojo/src/main/java/com/iplatform/model/po/S_chat.java 231 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-model-pojo/src/main/java/com/iplatform/model/po/S_chat_mapper.java 311 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-model-pojo/src/main/java/com/iplatform/model/po/S_class_ext.java 155 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-model-pojo/src/main/java/com/iplatform/model/po/S_class_ext_mapper.java 247 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-model-pojo/src/main/java/com/iplatform/model/po/S_code.java 243 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-model-pojo/src/main/java/com/iplatform/model/po/S_code_mapper.java 309 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-model-pojo/src/main/java/com/iplatform/model/po/S_config.java 288 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-model-pojo/src/main/java/com/iplatform/model/po/S_config_form.java 200 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-model-pojo/src/main/java/com/iplatform/model/po/S_config_form_mapper.java 279 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-model-pojo/src/main/java/com/iplatform/model/po/S_config_mapper.java 327 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-model-pojo/src/main/java/com/iplatform/model/po/S_dept.java 442 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-model-pojo/src/main/java/com/iplatform/model/po/S_dept_mapper.java 435 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-model-pojo/src/main/java/com/iplatform/model/po/S_dialog_history.java 273 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-model-pojo/src/main/java/com/iplatform/model/po/S_dialog_history_mapper.java 339 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-model-pojo/src/main/java/com/iplatform/model/po/S_dialog_run.java 231 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-model-pojo/src/main/java/com/iplatform/model/po/S_dialog_run_mapper.java 307 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-model-pojo/src/main/java/com/iplatform/model/po/S_dict_data.java 353 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-model-pojo/src/main/java/com/iplatform/model/po/S_dict_data_mapper.java 371 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-model-pojo/src/main/java/com/iplatform/model/po/S_dict_type.java 221 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-model-pojo/src/main/java/com/iplatform/model/po/S_dict_type_mapper.java 291 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-model-pojo/src/main/java/com/iplatform/model/po/S_group.java 200 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-model-pojo/src/main/java/com/iplatform/model/po/S_group_data.java 222 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-model-pojo/src/main/java/com/iplatform/model/po/S_group_data_mapper.java 303 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-model-pojo/src/main/java/com/iplatform/model/po/S_group_mapper.java 283 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-model-pojo/src/main/java/com/iplatform/model/po/S_host.java 265 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-model-pojo/src/main/java/com/iplatform/model/po/S_host_mapper.java 319 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-model-pojo/src/main/java/com/iplatform/model/po/S_login_info.java 604 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-model-pojo/src/main/java/com/iplatform/model/po/S_login_info_mapper.java 527 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-model-pojo/src/main/java/com/iplatform/model/po/S_menu.java 453 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-model-pojo/src/main/java/com/iplatform/model/po/S_menu_mapper.java 439 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-model-pojo/src/main/java/com/iplatform/model/po/S_message.java 441 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-model-pojo/src/main/java/com/iplatform/model/po/S_message_mapper.java 431 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-model-pojo/src/main/java/com/iplatform/model/po/S_oper_log.java 744 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-model-pojo/src/main/java/com/iplatform/model/po/S_oper_log_mapper.java 623 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-model-pojo/src/main/java/com/iplatform/model/po/S_role.java 336 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-model-pojo/src/main/java/com/iplatform/model/po/S_role_mapper.java 387 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-model-pojo/src/main/java/com/iplatform/model/po/S_user_core.java 948 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-model-pojo/src/main/java/com/iplatform/model/po/S_user_core_mapper.java 771 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-model-pojo/src/main/java/com/iplatform/model/po/S_user_login.java 199 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-model-pojo/src/main/java/com/iplatform/model/po/S_user_login_mapper.java 279 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-model-pojo/src/main/java/com/iplatform/model/po/S_user_profile.java 463 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-model-pojo/src/main/java/com/iplatform/model/po/S_user_profile_mapper.java 439 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-model-pojo/src/main/java/com/iplatform/model/po/SfNotification.java 326 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-model-pojo/src/main/java/com/iplatform/model/po/SfNotification_mapper.java 373 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-model-pojo/src/main/java/com/iplatform/model/po/SfTemplateMessage.java 260 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-model-pojo/src/main/java/com/iplatform/model/po/SfTemplateMessage_mapper.java 321 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-model-pojo/src/main/java/com/iplatform/model/po/TcpEquip.java 332 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-model-pojo/src/main/java/com/iplatform/model/po/TcpEquipHistory.java 332 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-model-pojo/src/main/java/com/iplatform/model/po/TcpEquipHistory_mapper.java 381 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-model-pojo/src/main/java/com/iplatform/model/po/TcpEquipStatus.java 332 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-model-pojo/src/main/java/com/iplatform/model/po/TcpEquipStatus_mapper.java 381 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-model-pojo/src/main/java/com/iplatform/model/po/TcpEquip_mapper.java 365 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-model-pojo/src/main/java/com/iplatform/model/to/UserAndDeptTo.java 165 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-model-pojo/src/main/java/com/iplatform/model/to/UserAndDeptToResult.java 47 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-model-pojo/src/main/java/com/iplatform/model/vo/ApiTime.java 75 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-model-pojo/src/main/java/com/iplatform/model/vo/CategoryTreeVo.java 137 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-model-pojo/src/main/java/com/iplatform/model/vo/ConfigFormItemConfigRegListVo.java 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-model-pojo/src/main/java/com/iplatform/model/vo/ConfigFormItemConfigVo.java 152 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-model-pojo/src/main/java/com/iplatform/model/vo/ConfigFormItemVo.java 80 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-model-pojo/src/main/java/com/iplatform/model/vo/ConfigFormVo.java 140 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-model-pojo/src/main/java/com/iplatform/model/vo/CopyRightVo.java 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-model-pojo/src/main/java/com/iplatform/model/vo/MenuVo.java 129 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-model-pojo/src/main/java/com/iplatform/model/vo/MetaVo.java 102 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-model-pojo/src/main/java/com/iplatform/model/vo/NotificationConfigVo.java 88 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-model-pojo/src/main/java/com/iplatform/model/vo/NotificationTemplateVo.java 105 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-model-pojo/src/main/java/com/iplatform/model/vo/OrderCenterNumVo.java 77 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-model-pojo/src/main/java/com/iplatform/model/vo/RouterVo.java 148 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-model-pojo/src/main/java/com/iplatform/model/vo/SystemGroupVo.java 42 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
iplatform-model-pojo/src/main/java/com/iplatform/model/vo/WeChatAccessTokenVo.java 63 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pom.xml 162 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
admin-web/public/static/config.js
@@ -9,8 +9,8 @@
  // 接口请求地址
  // ftpUrl: 'http://172.16.20.9:8083/lowConsum',//开发
  // apiBaseURL: 'http://172.16.20.9:8083/lowConsum',//开发
  ftpUrl: 'http://127.0.0.1:8083/lowConsum',//开发
  apiBaseURL: 'http://127.0.0.1:8083/lowConsum',//开发
  // apiBaseURL: 'http://172.16.60.110:8083/lowConsum',//开发
@@ -20,8 +20,8 @@
  // apiBaseURL: protocol + '//'+ host + '/lowapi',// 正式,
  ftpUrl: protocol + '//'+ host + '/lowapi',// 正式,
  apiBaseURL: protocol + '//'+ host + '/lowapi',// 正式,
  // ftpUrl: protocol + '//'+ host + '/lowapi',// 正式,
  // apiBaseURL: protocol + '//'+ host + '/lowapi',// 正式,
  debug: false //调试开关  true时会输出请求日志
};
admin-web/src/api/foudation/material.js
@@ -37,10 +37,11 @@
/*
* 物品详情
* */
export function goodsDetail(data) {
export function goodsDetail(params) {
  return request({
    url: `/pc/base/goods/template/detail/?id=${data}`,
    url: `/pc/base/goods/template/detail`,
    method: 'get',
    params
  })
}
admin-web/src/views/foundation/material/edit.vue
@@ -107,7 +107,7 @@
  },
  async created() {
    if (this.setting.id) {
      await goodsDetail(this.setting.id).then(res => {
      await goodsDetail({'id': this.setting.id}).then(res => {
        this.formData = res
        if (!this.formData.agencyId) {
          this.formData.agencyId = this.userInfo.tenantId
consum-base/pom.xml
@@ -29,7 +29,22 @@
                    <artifactId>snakeyaml</artifactId>
                    <groupId>org.yaml</groupId>
                </exclusion>
                <exclusion>
                    <artifactId>jjwt</artifactId>
                    <groupId>io.jsonwebtoken</groupId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
            <version>0.12.6</version>
        </dependency>
        <!-- EasyExcel 核心库 -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>easyexcel</artifactId>
            <version>3.2.1</version>
        </dependency>
        <dependency>
            <groupId>org.yaml</groupId>
@@ -38,8 +53,9 @@
        </dependency>
        <!-- 引入web安全认证模块,如果不引入则不会做任何权限拦截(这种模式通常可用于快速测试), 2022/10/31 -->
        <dependency>
            <groupId>com.iplatform</groupId>
            <groupId>com.consum</groupId>
            <artifactId>iplatform-base-security-consum</artifactId>
            <version>3.2.0</version>
            <exclusions>
                <exclusion>
                    <artifactId>spring-security-crypto</artifactId>
@@ -63,7 +79,7 @@
        <dependency>
            <artifactId>spring-security-crypto</artifactId>
            <groupId>org.springframework.security</groupId>
            <version>5.7.12</version>
            <version>6.2.7</version>
        </dependency>
        <dependency>
            <artifactId>spring-security-core</artifactId>
@@ -74,7 +90,6 @@
                </exclusion>
            </exclusions>
            <groupId>org.springframework.security</groupId>
            <version>5.7.12</version>
        </dependency>
        <dependency>
            <artifactId>spring-security-config</artifactId>
@@ -85,7 +100,7 @@
                </exclusion>
            </exclusions>
            <groupId>org.springframework.security</groupId>
            <version>5.7.12</version>
            <version>6.2.7</version>
        </dependency>
        <dependency>
            <artifactId>spring-security-web</artifactId>
@@ -96,7 +111,7 @@
                </exclusion>
            </exclusions>
            <groupId>org.springframework.security</groupId>
            <version>5.7.12</version>
            <version>6.2.7</version>
        </dependency>
        <!-- 系统基础模块界面管理控制模块,如果不引入也不会有swagger文档功能!,2023/03/05 -->
@@ -115,7 +130,9 @@
        <dependency>
            <groupId>com.walkersoft</groupId>
            <artifactId>walker-support-redis</artifactId>
            <optional>true</optional>
            <version>3.2.0</version>
            <systemPath>${project.basedir}/src/main/resources/lib/walker-support-redis-3.2.0.jar</systemPath>
            <scope>system</scope>
            <exclusions>
                <exclusion>
                    <groupId>com.walkersoft</groupId>
consum-base/src/main/java/com/consum/base/aop/DictDataAop.java
@@ -1,6 +1,7 @@
package com.consum.base.aop;
import cn.hutool.core.util.ReflectUtil;
import com.consum.base.core.utils.IdUtil;
import com.iplatform.base.cache.DictCacheProvider;
import com.iplatform.core.BeanContextAware;
import com.iplatform.model.po.S_dict_data;
@@ -54,7 +55,7 @@
        try {
            S_dict_data dictData = (S_dict_data) args[0];
            if (dictData.getDict_code() == null) {
                dictData.setDict_code(NumberGenerator.getLongSequenceNumber());
                dictData.setDict_code(IdUtil.generateId());
            }
        } catch (Exception e) {
            e.getMessage();
consum-base/src/main/java/com/consum/base/controller/BaseCategoryController.java
@@ -29,7 +29,6 @@
import com.walker.db.page.GenericPager;
import com.walker.infrastructure.utils.StringUtils;
import com.walker.web.ResponseValue;
import io.swagger.annotations.Api;
import org.springframework.web.multipart.MultipartFile;
/**
@@ -37,7 +36,7 @@
 * @Author 卢庆阳
 * @Date 2023/10/23
 */
@Api(value = "物品分类", tags = "物品分类")
// @Api(value = "物品分类", tags = "物品分类")
@RestController
@RequestMapping("/pc/base/category")
public class BaseCategoryController extends BaseController {
consum-base/src/main/java/com/consum/base/controller/BaseGoodsModelsController.java
@@ -18,14 +18,12 @@
import com.walker.infrastructure.utils.StringUtils;
import com.walker.web.ResponseValue;
import io.swagger.annotations.Api;
/**
 * @Description 规格型号
 * @Author 卢庆阳
 * @Date 2023/10/25
 */
@Api(value = "规格型号", tags = "规格型号")
// @Api(value = "规格型号", tags = "规格型号")
@RestController
@RequestMapping("/pc/base/goods/models")
public class BaseGoodsModelsController extends BaseController {
@@ -82,7 +80,7 @@
    /**
     * 修改状态
     *
     *
     * @author 卢庆阳
     * @date 2023/10/25
     */
consum-base/src/main/java/com/consum/base/controller/BaseGoodsTemplateController.java
@@ -43,17 +43,12 @@
import com.walker.infrastructure.utils.StringUtils;
import com.walker.web.ResponseValue;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
/**
 * @Description 物品模板
 * @Author 卢庆阳
 * @Date 2023/10/24
 */
@Api(value = "物品模板", tags = "物品模板")
// @Api(value = "物品模板", tags = "物品模板")
@RestController
@RequestMapping("/pc/base/goods/template")
public class BaseGoodsTemplateController extends BaseController {
@@ -272,12 +267,12 @@
        return ResponseValue.success("查询成功!", list);
    }
    @ApiOperation(value = "调拨查询机构下所有仓库下的分类模板信息", notes = "调拨查询机构下所有仓库下的分类模板信息")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "agencyId", value = "父级机构id", required = true, dataType = "java.lang.Long",
                    paramType = "query"),
            @ApiImplicitParam(name = "categoryId", value = "分类id", required = true, dataType = "Long",
                    paramType = "query")})
//    // @ApiOperation(value = "调拨查询机构下所有仓库下的分类模板信息", notes = "调拨查询机构下所有仓库下的分类模板信息")
//    @ApiImplicitParams({
//            @ApiImplicitParam(name = "agencyId", value = "父级机构id", required = true, dataType = "java.lang.Long",
//                    paramType = "query"),
//            @ApiImplicitParam(name = "categoryId", value = "分类id", required = true, dataType = "Long",
//                    paramType = "query")})
    @GetMapping("/query/warehouse/goods")
    public ResponseValue queryWarehouseGoods(Long agencyId, Long categoryId) {
consum-base/src/main/java/com/consum/base/controller/BaseWarehouseController.java
@@ -30,17 +30,12 @@
import com.walker.infrastructure.utils.StringUtils;
import com.walker.web.ResponseValue;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
/**
 * @Description 仓库管理
 * @Author 卢庆阳
 * @Date 2023/10/26
 */
@Api(value = "仓库管理", tags = "仓库管理")
// @Api(value = "仓库管理", tags = "仓库管理")
@RestController
@RequestMapping("/pc/base/warehouse")
@Slf4j
@@ -253,9 +248,9 @@
        return ResponseValue.success("查询成功!", list);
    }
    @ApiOperation(value = "根据仓库id和型号id 查询库存", notes = "根据仓库id和型号id 查询库存")
    @ApiImplicitParams({@ApiImplicitParam(name = "warehouseQry", value = "仓库id和型号id", required = true,
            dataType = "WarehouseQry", paramType = "query")})
//    // @ApiOperation(value = "根据仓库id和型号id 查询库存", notes = "根据仓库id和型号id 查询库存")
//    @ApiImplicitParams({@ApiImplicitParam(name = "warehouseQry", value = "仓库id和型号id", required = true,
//            dataType = "WarehouseQry", paramType = "query")})
    @GetMapping("/select/number")
    public ResponseValue selectNumber() {
        WarehouseQry warehouseQry = CommonUtil.getObjFromReq(WarehouseQry.class);
@@ -279,9 +274,9 @@
        return ResponseValue.success(num);
    }
    @ApiOperation(value = "根据仓库id和型号id 查询库存", notes = "根据仓库id和型号id 查询库存")
    @ApiImplicitParams({@ApiImplicitParam(name = "warehouseQry", value = "仓库id和型号id", required = true,
            dataType = "WarehouseQry", paramType = "query")})
//    // @ApiOperation(value = "根据仓库id和型号id 查询库存", notes = "根据仓库id和型号id 查询库存")
//    @ApiImplicitParams({@ApiImplicitParam(name = "warehouseQry", value = "仓库id和型号id", required = true,
//            dataType = "WarehouseQry", paramType = "query")})
    @GetMapping("/select/in/warehouse/num")
    public ResponseValue selectAllNumber() {
        WarehouseQry warehouseQry = CommonUtil.getObjFromReq(WarehouseQry.class);
consum-base/src/main/java/com/consum/base/controller/BaseWarehouseManagerController.java
@@ -17,10 +17,6 @@
import com.consum.base.core.utils.CommonUtil;
import com.consum.base.pojo.request.WarehouseManagerParam;
import com.walker.web.ResponseValue;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
/**
 * @Description 库管员
@@ -28,7 +24,7 @@
 * @Date 2023/10/26
 * <p>
 */
@Api(value = "库管员", tags = "库管员")
// @Api(value = "库管员", tags = "库管员")
@RestController
@RequestMapping("/pc/base/warehouse/manager")
@Slf4j
@@ -48,9 +44,9 @@
     * @Author 卢庆阳
     * @Date 2023/10/26
     */
    @ApiOperation(value = "新增", notes = "新增")
    @ApiImplicitParam(name = "warehouseManagerParam", value = "库管员信息", required = true,
            dataType = "WarehouseManagerParam", paramType = "body")
//    // @ApiOperation(value = "新增", notes = "新增")
//    @ApiImplicitParam(name = "warehouseManagerParam", value = "库管员信息", required = true,
//            dataType = "WarehouseManagerParam", paramType = "body")
    @PostMapping("/save")
    public ResponseValue add() throws Exception {
        WarehouseManagerParam param = CommonUtil.getObjFromReqBody(WarehouseManagerParam.class);
@@ -447,8 +443,8 @@
    }
    @ApiOperation(value = "根据仓库id查询库管员", notes = "根据仓库id查询库管员")
    @ApiImplicitParam(name = "warehouseId", value = "仓库id", required = true, dataType = "Long")
//    // @ApiOperation(value = "根据仓库id查询库管员", notes = "根据仓库id查询库管员")
//    @ApiImplicitParam(name = "warehouseId", value = "仓库id", required = true, dataType = "Long")
    @GetMapping("/list")
    public ResponseValue select(Long warehouseId) {
        List<BaseWarehouseManager> baseWarehouseManagerList =
@@ -456,9 +452,9 @@
        return ResponseValue.success(baseWarehouseManagerList);
    }
    @ApiOperation(value = "根据仓库,机构id查询库管员", notes = "根据仓库,机构id查询库管员")
    @ApiImplicitParams({@ApiImplicitParam(name = "warehouseId", value = "仓库id", required = true, dataType = "Long"),
            @ApiImplicitParam(name = "agencyId", value = "机构id", required = true, dataType = "Long")})
//    // @ApiOperation(value = "根据仓库,机构id查询库管员", notes = "根据仓库,机构id查询库管员")
//    @ApiImplicitParams({@ApiImplicitParam(name = "warehouseId", value = "仓库id", required = true, dataType = "Long"),
//            @ApiImplicitParam(name = "agencyId", value = "机构id", required = true, dataType = "Long")})
    @GetMapping("/query")
    public ResponseValue selectManagerList(Long warehouseId, Long agencyId) {
        List<BaseWarehouseManager> baseWarehouseManagerList =
consum-base/src/main/java/com/consum/base/controller/DepFormScrappedController.java
@@ -1,15 +1,12 @@
package com.consum.base.controller;
import java.io.OutputStream;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletResponse;
import org.apache.poi.ss.usermodel.Workbook;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
@@ -32,10 +29,6 @@
import cn.afterturn.easypoi.excel.entity.TemplateExportParams;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.ReflectUtil;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
/**
 * @ClassName DepFormScrappedController
@@ -44,14 +37,14 @@
 * @Description
 * @Version 1.0
 **/
@Api(value = "部门报废", tags = "部门报废")
// @Api(value = "部门报废", tags = "部门报废")
@RestController
@RequestMapping("/pc/l/wh/form/scrapped")
public class DepFormScrappedController extends BaseController {
    @Resource
    private DepFormScrappedService depFormScrappedService;
    @ApiOperation(value = "新增报废单", notes = "新增报废单")
    // @ApiOperation(value = "新增报废单", notes = "新增报废单")
    @PostMapping("/deptAdd")
    public ResponseValue deptAdd() throws Exception {
        LDeptFormScrappedParam param = CommonUtil.getObjFromReqBody(LDeptFormScrappedParam.class);
@@ -69,9 +62,9 @@
     * @Description 列表查询 1.查询报废单 2.查询报废单物品
     * @Date
     */
    @ApiOperation(value = "列表查询", notes = "列表查询")
    @ApiImplicitParams({
        @ApiImplicitParam(name = "param", value = "查询条件", dataType = "LWhFormScrappedQry", paramType = "query")})
    // @ApiOperation(value = "列表查询", notes = "列表查询")
//    @ApiImplicitParams({
//        @ApiImplicitParam(name = "param", value = "查询条件", dataType = "LWhFormScrappedQry", paramType = "query")})
    @GetMapping("/deptList")
    public ResponseValue queryList() {
        LDeptFormScrappedQry param = CommonUtil.getObjFromReq(LDeptFormScrappedQry.class);
@@ -133,8 +126,8 @@
    /**
     * @Description 根据id查询详情
     */
    @ApiOperation(value = "根据id查询详情", notes = "根据id查询详情")
    @ApiImplicitParams({@ApiImplicitParam(name = "id", value = "报废单id", dataType = "Long", paramType = "query")})
    // @ApiOperation(value = "根据id查询详情", notes = "根据id查询详情")
    // @ApiImplicitParams({@ApiImplicitParam(name = "id", value = "报废单id", dataType = "Long", paramType = "query")})
    @GetMapping("/deptDetail")
    public ResponseValue getById(Long id) {
        if (id == null) {
@@ -143,8 +136,8 @@
        return ResponseValue.success(this.depFormScrappedService.getById(id));
    }
    @ApiOperation(value = "导出报废单", notes = "导出报废单")
    @ApiImplicitParams({@ApiImplicitParam(name = "id", value = "报废单id", dataType = "Long", paramType = "query")})
    // @ApiOperation(value = "导出报废单", notes = "导出报废单")
    // @ApiImplicitParams({@ApiImplicitParam(name = "id", value = "报废单id", dataType = "Long", paramType = "query")})
    @GetMapping("/deptList/export")
    public ResponseValue<String> export(Long id, HttpServletResponse response) throws Exception {
        if (id == null) {
consum-base/src/main/java/com/consum/base/controller/FinDepartLedgerController.java
@@ -10,15 +10,14 @@
import com.iplatform.model.po.S_user_core;
import com.walker.db.page.GenericPager;
import com.walker.web.ResponseValue;
import io.swagger.annotations.Api;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletResponse;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.core.io.ClassPathResource;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URLEncoder;
@@ -29,7 +28,7 @@
/**
 * 部门台账
 */
@Api(value = "部门台账", tags = "部门台账")
// @Api(value = "部门台账", tags = "部门台账")
@RestController
@RequestMapping("/pc/fin/warehouse/departLedger")
public class FinDepartLedgerController extends BaseController {
consum-base/src/main/java/com/consum/base/controller/FinFileController.java
@@ -3,11 +3,11 @@
import com.consum.base.BaseController;
import com.consum.base.service.FinFileService;
import com.consum.model.po.FinFile;
import com.iplatform.base.IdUtil;
import com.walker.file.FileInfo;
import com.walker.infrastructure.utils.DateUtils;
import com.walker.infrastructure.utils.NumberGenerator;
import com.walker.web.ResponseValue;
import io.swagger.annotations.Api;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
@@ -20,7 +20,7 @@
 * @Author wh
 * @Date 2023/8/1 10:33
 */
@Api(value = "文件上传", tags = "文件上传")
// @Api(value = "文件上传", tags = "文件上传")
@RestController
@RequestMapping("/pc/fin/file")
public class FinFileController extends BaseController {
@@ -44,7 +44,7 @@
        }
        if (fileInfo != null) {
            FinFile finFile = new FinFile();
            finFile.setId(NumberGenerator.getLongSequenceNumberNano());
            finFile.setId(IdUtil.generateId());
            finFile.setCreateTime(DateUtils.getDateTimeNumber(System.currentTimeMillis()));
            finFile.setUserName("-1");
            finFile.setContentType(finFile.getContentType());
@@ -75,7 +75,7 @@
            for (int i = 0; i < uploadfs.length; i++) {
                FileInfo fileInfo = uploadfs[i];
                FinFile finFile = new FinFile();
                finFile.setId(NumberGenerator.getLongSequenceNumberNano());
                finFile.setId(IdUtil.generateId());
                finFile.setCreateTime(DateUtils.getDateTimeNumber(System.currentTimeMillis()));
                finFile.setUserName("-1");
                finFile.setContentType(finFile.getContentType());
consum-base/src/main/java/com/consum/base/controller/FinSysServerController.java
@@ -2,6 +2,7 @@
import java.util.List;
import com.consum.base.core.utils.IdUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
@@ -21,9 +22,9 @@
import com.walker.infrastructure.utils.NumberGenerator;
import com.walker.web.ResponseValue;
import io.swagger.annotations.Api;
//
@Api(value = "系统服务信息", tags = "系统服务信息")
// @Api(value = "系统服务信息", tags = "系统服务信息")
@RestController
@RequestMapping("/pc/fin/sys/server")
public class FinSysServerController extends BaseController {
@@ -104,7 +105,7 @@
        if (param == null) {
            return ResponseValue.error("参数为空");
        }
        param.setId(NumberGenerator.getLongSequenceNumber());
        param.setId(IdUtil.generateId());
        param.setCreatedTime(DateUtils.getDateTimeNumber(System.currentTimeMillis()));
        param.setCreatedBy(this.getCurrentUser().getUser_name());
        param.setLv(param.getParentId() + 1);
consum-base/src/main/java/com/consum/base/controller/FinSysTenantController.java
@@ -12,6 +12,7 @@
import java.util.Objects;
import java.util.TreeMap;
import cn.hutool.core.convert.Convert;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.ClassPathResource;
@@ -49,17 +50,12 @@
import com.walker.infrastructure.utils.StringUtils;
import com.walker.web.ResponseValue;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
/**
 * @Description 机构
 * @Author wh
 * @Date 2023/7/13 19:51
 */
@Api(value = "机构管理", tags = "机构管理")
// @Api(value = "机构管理", tags = "机构管理")
@RestController
@RequestMapping("/pc/fin/sys/tenant")
public class FinSysTenantController extends BaseController {
@@ -102,9 +98,9 @@
        // 获取当前用户信息
        String tenantId = user.getTenantId();
        FinSysTenant finSysTenant = new FinSysTenant();
        finSysTenant.setId(new Long(tenantId));
        finSysTenant.setId(Convert.toLong(tenantId));
        // 主键是TempId
        finSysTenant.setTempId(new Long(tenantId));
        finSysTenant.setTempId(Convert.toLong(tenantId));
        // 查询当前用户的机构
        FinSysTenant userTenant = finSysTenantService.get(finSysTenant);
        // 树列表
@@ -113,7 +109,7 @@
            if (userTenant.getLv() != 1 && userTenant.getParentId() != 0) {
                rootMap.clear();
                this.childMap.clear();
                this.defaultParentId = new Long(user.getTenantId());
                this.defaultParentId = Convert.toLong(user.getTenantId());
                // 根据父级获取子集
                setEntityList(finSysTenantList);
                treeRootList = getTreeRootList();
@@ -635,9 +631,7 @@
        return ResponseValue.success(finSysTenantUserResults);
    }
    @ApiOperation(value = "获取父级机构", notes = "获取父级机构")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "Authorization", value = "token", dataType = "String", paramType = "header"),})
    // @ApiOperation(value = "获取父级机构", notes = "获取父级机构")
    @GetMapping("/get/parent/tenant")
    public ResponseValue getParentTenant() {
        FinSysTenantUser sysInfo = getSysInfo();
consum-base/src/main/java/com/consum/base/controller/FinSysTenantDepartmentController.java
@@ -21,17 +21,12 @@
import com.walker.infrastructure.utils.StringUtils;
import com.walker.web.ResponseValue;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
/**
 * @Description 部门管理
 * @Author 卢庆阳
 * @Date 2023/10/26
 */
@Api(value = "部门管理", tags = "部门管理")
// @Api(value = "部门管理", tags = "部门管理")
@RestController
@RequestMapping("/pc/fin/sys/tenant/department")
public class FinSysTenantDepartmentController extends BaseController {
@@ -44,9 +39,9 @@
     * @Author 卢庆阳
     * @Date 2023/10/26
     */
    @ApiOperation(value = "新增", notes = "新增")
    @ApiImplicitParams(value = {
        @ApiImplicitParam(name = "param", value = "部门信息", required = true, dataType = "FinSysTenantDepartmentParam")})
    // @ApiOperation(value = "新增", notes = "新增")
//    @ApiImplicitParams(value = {
//        @ApiImplicitParam(name = "param", value = "部门信息", required = true, dataType = "FinSysTenantDepartmentParam")})
    @PostMapping("/add")
    public ResponseValue add() {
        FinSysTenantDepartmentParam param = CommonUtil.getObjFromReqBody(FinSysTenantDepartmentParam.class);
@@ -106,9 +101,9 @@
        return ResponseValue.success(pager);
    }
    @ApiOperation(value = "根据机构id查询所有部门列表信息", notes = "根据机构id查询所有部门列表信息")
    @ApiImplicitParams(value = {
        @ApiImplicitParam(name = "param", value = "部门信息", required = true, dataType = "FinSysTenantDepartmentParam")})
    // @ApiOperation(value = "根据机构id查询所有部门列表信息", notes = "根据机构id查询所有部门列表信息")
//    @ApiImplicitParams(value = {
//        @ApiImplicitParam(name = "param", value = "部门信息", required = true, dataType = "FinSysTenantDepartmentParam")})
    @GetMapping("/list/all")
    public ResponseValue queryAllDepartment(Long tenantId) {
        FinSysTenantUser sysInfo = this.getSysInfo();
consum-base/src/main/java/com/consum/base/controller/FinSysTenantUserController.java
@@ -13,6 +13,7 @@
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.consum.base.core.utils.IdUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.compress.utils.Lists;
import org.apache.commons.lang3.ObjectUtils;
@@ -65,17 +66,12 @@
import com.walker.infrastructure.utils.PhoneNumberUtils;
import com.walker.infrastructure.utils.StringUtils;
import com.walker.web.ResponseValue;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
/**
 * @Description 系统用户
 * @Author wh
 * @Date 2023/7/17 14:16
 */
@Api(value = "机构用户信息", tags = "机构用户信息")
@RestController
@RequestMapping("/pc/fin/sys/tenant/user")
@Slf4j
@@ -134,9 +130,8 @@
        return ResponseValue.success(pager);
    }
    @ApiOperation(value = "根据机构id查询用户", notes = "根据机构id查询用户")
    @ApiImplicitParam(name = "tenantId", value = "机构id", required = true, dataType = "Long", paramType = "query")
    @GetMapping("/query/user")
    // @ApiOperation(value = "根据机构id查询用户", notes = "根据机构id查询用户")
   @GetMapping("/query/user")
    public ResponseValue queryUserByTenantId(Long tenantId) {
        FinSysTenantUser finSysTenantUser = new FinSysTenantUser();
@@ -438,14 +433,14 @@
    private void buildTenantUser(FinSysTenantUser tenantUser, FinSysTenantUser sysInfo) {
        tenantUser.setId(NumberGenerator.getLongSequenceNumber());
        tenantUser.setId(IdUtil.generateId());
        tenantUser.setCreateTime(DateUtils.getDateTimeNumber(System.currentTimeMillis()));
        tenantUser.setCreateBy(this.getCurrentUser().getUser_name());
        tenantUser.setUpdateTime(DateUtils.getDateTimeNumber(System.currentTimeMillis()));
        tenantUser.setUpdateBy(this.getCurrentUser().getUser_name());
        // 是否删除 0是 1否
        tenantUser.setIsDelete(0);
        tenantUser.setSysUserId(NumberGenerator.getLongSequenceNumber());
        tenantUser.setSysUserId(IdUtil.generateId());
        setUserPhone(tenantUser, tenantUser.getUserPhone());
        String tenantId = tenantUser.getTenantId();
        FinSysTenant finSysTenant = finSysTenantService.selectById(Long.valueOf(tenantId));
@@ -577,14 +572,14 @@
                                roles.stream().filter(role -> role.getRole_name().equals(data.getRoleName())).findFirst();
                        user.setRoleList(Arrays.asList(sRole.orElse(new S_role()).getRole_id()));
                        user.setId(NumberGenerator.getLongSequenceNumber());
                        user.setId(IdUtil.generateId());
                        user.setCreateTime(DateUtils.getDateTimeNumber(System.currentTimeMillis()));
                        user.setCreateBy(currentUser.getUser_name());
                        user.setUpdateTime(DateUtils.getDateTimeNumber(System.currentTimeMillis()));
                        user.setUpdateBy(currentUser.getUser_name());
                        user.setStatus(1);// 0禁用 1启用
                        user.setIsDelete(0);// 是否删除 0是 1否
                        user.setSysUserId(NumberGenerator.getLongSequenceNumber());
                        user.setSysUserId(IdUtil.generateId());
                        user.setRemark("批量导入");
                        // 加密手机号
                        setUserPhone(user, data.getUserPhone());
@@ -1066,9 +1061,7 @@
        return ResponseValue.success(1);
    }
    @ApiOperation(value = "根据机构id查询部门中的用户", notes = "根据机构id查询部门中的用户")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "agencyId", value = "机构id", required = true, dataType = "Long", paramType = "query")})
    // @ApiOperation(value = "根据机构id查询部门中的用户", notes = "根据机构id查询部门中的用户")
    @GetMapping("/select/department")
    public ResponseValue selectDepartment(Long agencyId, String isFilter) {
        FinSysTenantVO finSysTenantVO = new FinSysTenantVO();
consum-base/src/main/java/com/consum/base/controller/FinWarehouseLedgerController.java
@@ -10,15 +10,14 @@
import com.iplatform.model.po.S_user_core;
import com.walker.db.page.GenericPager;
import com.walker.web.ResponseValue;
import io.swagger.annotations.Api;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletResponse;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.core.io.ClassPathResource;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URLEncoder;
@@ -34,7 +33,6 @@
 * @Description 仓库台账
 * @Version 1.0
 **/
@Api(value = "仓库台账", tags = "仓库台账")
@RestController
@RequestMapping("/pc/fin/warehouse/ledger")
public class FinWarehouseLedgerController extends BaseController {
consum-base/src/main/java/com/consum/base/controller/LOrgSupplierController.java
@@ -4,12 +4,10 @@
import com.consum.base.service.LOrgSupplierServiceImpl;
import com.iplatform.model.po.S_user_core;
import com.walker.web.ResponseValue;
import io.swagger.annotations.Api;
import jakarta.annotation.Resource;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
/**
 * @ClassName LOrgSupplierController
@@ -18,7 +16,7 @@
 * @Description
 * @Version 1.0
 **/
@Api(value = "供应商", tags = "供应商")
// @Api(value = "供应商", tags = "供应商")
@RestController
@RequestMapping("/pc/orgSupplier")
public class LOrgSupplierController extends BaseController {
consum-base/src/main/java/com/consum/base/controller/LWarehouseFlowController.java
@@ -4,8 +4,7 @@
import java.util.ArrayList;
import java.util.Map;
import javax.annotation.Resource;
import jakarta.annotation.Resource;
import org.apache.commons.compress.utils.Lists;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
@@ -23,7 +22,6 @@
import com.walker.web.ResponseValue;
import cn.hutool.core.util.ReflectUtil;
import io.swagger.annotations.Api;
/**
 * @ClassName lWarehouseFlowController
@@ -31,7 +29,7 @@
 * @Description
 * @Version 1.0
 **/
@Api(value = "仓库流水", tags = "仓库流水")
// @Api(value = "仓库流水", tags = "仓库流水")
@RestController
@RequestMapping("/pc/warehouse/flow")
public class LWarehouseFlowController extends BaseController {
consum-base/src/main/java/com/consum/base/controller/LWhFormInventoryController.java
@@ -6,8 +6,7 @@
import java.util.Objects;
import java.util.Optional;
import javax.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpServletResponse;
import org.apache.poi.ss.usermodel.Workbook;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
@@ -39,18 +38,13 @@
import cn.afterturn.easypoi.excel.ExcelExportUtil;
import cn.afterturn.easypoi.excel.entity.TemplateExportParams;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiResponse;
/**
 * @Description 盘点
 * @Author 卢庆阳
 * @Date 2023/10/23
 */
@Api(value = "盘点", tags = "盘点")
// @Api(value = "盘点", tags = "盘点")
@RestController
@RequestMapping("/pc/l/wh/form/inventory")
public class LWhFormInventoryController extends BaseController {
@@ -65,8 +59,8 @@
     * @Author 卢庆阳
     * @Date 2023/10/31
     */
    @ApiOperation(value = "新增盘点", notes = "新增盘点")
    @ApiImplicitParams({@ApiImplicitParam(name = "param", value = "盘点单信息", dataType = "FormInventoryParam")})
    // @ApiOperation(value = "新增盘点", notes = "新增盘点")
    // @ApiImplicitParams({@ApiImplicitParam(name = "param", value = "盘点单信息", dataType = "FormInventoryParam")})
    @PostMapping("/add")
    public ResponseValue add() {
@@ -101,9 +95,9 @@
     * @Author 卢庆阳
     * @Date 2023/10/31
     */
    @ApiOperation(value = "盘点单列表查询", notes = "盘点单列表查询")
    @ApiImplicitParams({@ApiImplicitParam(name = "param", value = "盘点条件", dataType = "LWhFormInventoryParam",
            required = true, paramType = "query")})
    // @ApiOperation(value = "盘点单列表查询", notes = "盘点单列表查询")
    // @ApiImplicitParams({@ApiImplicitParam(name = "param", value = "盘点条件", dataType = "LWhFormInventoryParam",
    //        required = true, paramType = "query")})
    @GetMapping("/list")
    public ResponseValue queryList() {
        LWhFormInventoryQry param = CommonUtil.getObjFromReq(LWhFormInventoryQry.class);
@@ -124,9 +118,9 @@
     *
     * @return
     */
    @ApiOperation(value = "查询此账号角色,是否现在还可以增加盘点任务", notes = "查询此账号角色,是否现在还可以增加盘点任务")
    @ApiImplicitParams({@ApiImplicitParam(name = "param", value = "盘点条件", dataType = "LWhFormInventoryParam",
            required = true, paramType = "query")})
    // @ApiOperation(value = "查询此账号角色,是否现在还可以增加盘点任务", notes = "查询此账号角色,是否现在还可以增加盘点任务")
//    @ApiImplicitParams({@ApiImplicitParam(name = "param", value = "盘点条件", dataType = "LWhFormInventoryParam",
//            required = true, paramType = "query")})
    @GetMapping("/isAddInventory")
    public ResponseValue isAddInventory() {
        LWhFormInventoryQry param = CommonUtil.getObjFromReq(LWhFormInventoryQry.class);
@@ -141,10 +135,10 @@
        return ResponseValue.success(res);
    }
    @ApiOperation(value = "盘点单物品列表查询", notes = "盘点单物品列表查询", response = FormInventoryVO.class)
    @ApiImplicitParams({
            @ApiImplicitParam(name = "id", value = "盘点单id", dataType = "Long", required = true, paramType = "query")})
    @ApiResponse(code = 200, message = "成功", response = FormInventoryVO.class)
    // @ApiOperation(value = "盘点单物品列表查询", notes = "盘点单物品列表查询", response = FormInventoryVO.class)
//    @ApiImplicitParams({
//            @ApiImplicitParam(name = "id", value = "盘点单id", dataType = "Long", required = true, paramType = "query")})
//    @ApiResponse(code = 200, message = "成功", response = FormInventoryVO.class)
    @GetMapping("/query")
    public ResponseValue queryInventBaseGoodTemplate(Long id) {
        FinSysTenantUser sysInfo = this.getSysInfo();
@@ -167,8 +161,8 @@
     * @Author 卢庆阳
     * @Date 2023/10/31
     */
    @ApiOperation(value = "编辑盘点", notes = "编辑盘点")
    @ApiImplicitParams({@ApiImplicitParam(name = "param", value = "盘点单信息", dataType = "FormInventoryParam")})
    // @ApiOperation(value = "编辑盘点", notes = "编辑盘点")
    // @ApiImplicitParams({@ApiImplicitParam(name = "param", value = "盘点单信息", dataType = "FormInventoryParam")})
    @PostMapping("/edit")
    public ResponseValue edit() {
        FormInventoryParam param = CommonUtil.getObjFromReqBody(FormInventoryParam.class);
@@ -208,9 +202,9 @@
    /**
     * @Description 根据id删除
     */
    @ApiOperation(value = "根据id删除盘点", notes = "根据id删除盘点")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "id", value = "盘点单id", dataType = "Long", required = true, paramType = "query")})
    // @ApiOperation(value = "根据id删除盘点", notes = "根据id删除盘点")
//    @ApiImplicitParams({
//            @ApiImplicitParam(name = "id", value = "盘点单id", dataType = "Long", required = true, paramType = "query")})
    @DeleteMapping("/del")
    public ResponseValue delById(Long id) {
        if (id == null) {
@@ -237,8 +231,8 @@
     * @author 卢庆阳
     * @date 2023/10/31
     */
    @ApiOperation(value = "盘点暂存", notes = "盘点暂存")
    @ApiImplicitParams({@ApiImplicitParam(name = "param", value = "盘点单信息", dataType = "LWhFormInventoryDto")})
    // @ApiOperation(value = "盘点暂存", notes = "盘点暂存")
    // @ApiImplicitParams({@ApiImplicitParam(name = "param", value = "盘点单信息", dataType = "LWhFormInventoryDto")})
    @PostMapping("/temporary/storage")
    public ResponseValue updateTemporaryStorage() {
        LWhFormInventoryParam param = CommonUtil.getObjFromReqBody(LWhFormInventoryParam.class);
@@ -260,8 +254,8 @@
     * @author 卢庆阳
     * @date 2023/10/31
     */
    @ApiOperation(value = "完成盘点", notes = "完成盘点")
    @ApiImplicitParams({@ApiImplicitParam(name = "param", value = "盘点单信息", dataType = "LWhFormInventoryDto")})
    // @ApiOperation(value = "完成盘点", notes = "完成盘点")
    // @ApiImplicitParams({@ApiImplicitParam(name = "param", value = "盘点单信息", dataType = "LWhFormInventoryDto")})
    @PostMapping("/finish")
    public ResponseValue updateFinishPd() {
        LWhFormInventoryParam param = CommonUtil.getObjFromReqBody(LWhFormInventoryParam.class);
@@ -284,9 +278,9 @@
     * @Author 卢庆阳
     * @Date 2023/11/1
     */
    @ApiOperation(value = "异常明细列表查询", notes = "异常明细列表查询")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "param", value = "盘点条件", dataType = "LWhFormInventoryParam", paramType = "query")})
    // @ApiOperation(value = "异常明细列表查询", notes = "异常明细列表查询")
//    @ApiImplicitParams({
//            @ApiImplicitParam(name = "param", value = "盘点条件", dataType = "LWhFormInventoryParam", paramType = "query")})
    @GetMapping("/list/PdDetail")
    public ResponseValue queryPdDetailList() {
        LWhFormInventoryQry param = CommonUtil.getObjFromReq(LWhFormInventoryQry.class);
@@ -302,9 +296,9 @@
        return ResponseValue.success(result);
    }
    @ApiOperation(value = "根据id查询盘点物品详细信息", notes = "根据id查询盘点物品详细信息")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "id", value = "盘点id", dataType = "Long", required = true, paramType = "query")})
    // @ApiOperation(value = "根据id查询盘点物品详细信息", notes = "根据id查询盘点物品详细信息")
//    @ApiImplicitParams({
//            @ApiImplicitParam(name = "id", value = "盘点id", dataType = "Long", required = true, paramType = "query")})
    @GetMapping("/detail")
    public ResponseValue selectDetailById(Long id) {
        FinSysTenantUser sysInfo = this.getSysInfo();
@@ -325,9 +319,9 @@
        return ResponseValue.success(formInventoryVO);
    }
    @ApiOperation(value = "盘点单导出", notes = "盘点单导出")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "id", value = "盘点单id", dataType = "Long", required = true, paramType = "query")})
    // @ApiOperation(value = "盘点单导出", notes = "盘点单导出")
//    @ApiImplicitParams({
//            @ApiImplicitParam(name = "id", value = "盘点单id", dataType = "Long", required = true, paramType = "query")})
    @GetMapping("/list/export")
    public ResponseValue<String> export(Long id, HttpServletResponse response) throws Exception {
consum-base/src/main/java/com/consum/base/controller/LWhFormInventoryGoodsController.java
@@ -13,15 +13,12 @@
import com.consum.model.po.LWhFormInventoryGoods;
import com.walker.web.ResponseValue;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
/**
 * @Description 盘点单物品
 * @Author 卢庆阳
 * @Date 2023/10/31
 */
@Api(value = "盘点", tags = "盘点")
// @Api(value = "盘点", tags = "盘点")
@RestController
@RequestMapping("/pc/l/wh/form/inventory/goods")
public class LWhFormInventoryGoodsController extends BaseController {
@@ -35,7 +32,7 @@
     * @Author 卢庆阳
     * @Date 2023/10/31
     */
    @ApiOperation(value = "查询盘点单物品(继续盘点)", notes = "查询盘点单物品(继续盘点)")
    // @ApiOperation(value = "查询盘点单物品(继续盘点)", notes = "查询盘点单物品(继续盘点)")
    @GetMapping("/list")
    public ResponseValue queryList(Long id) {
        FinSysTenantUser sysInfo = this.getSysInfo();
consum-base/src/main/java/com/consum/base/controller/LWhFormOutputController.java
@@ -9,9 +9,8 @@
import java.util.Optional;
import java.util.stream.Collectors;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletResponse;
import org.apache.commons.compress.utils.Lists;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.Workbook;
@@ -50,17 +49,14 @@
import cn.afterturn.easypoi.excel.ExcelExportUtil;
import cn.afterturn.easypoi.excel.entity.TemplateExportParams;
import cn.hutool.core.util.ReflectUtil;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
/**
 * @Description 出库单
 * @Author 卢庆阳
 * @Date 2023/10/27
 */
@Api(value = "出库单", tags = "出库单")
// @Api(value = "出库单", tags = "出库单")
@RestController
@RequestMapping("/pc/l/wh/form/output")
public class LWhFormOutputController extends BaseController {
@@ -77,9 +73,9 @@
     * @Author 卢庆阳
     * @Date 2023/10/27
     */
    @ApiOperation(value = "新增出库单", notes = "新增出库单")
    @ApiImplicitParams({@ApiImplicitParam(name = "param", value = "出库单实体", dataType = "lWhFormOutputParam",
        dataTypeClass = LWhFormOutputParam.class, paramType = "body")})
    // @ApiOperation(value = "新增出库单", notes = "新增出库单")
//    @ApiImplicitParams({@ApiImplicitParam(name = "param", value = "出库单实体", dataType = "lWhFormOutputParam",
//        dataTypeClass = LWhFormOutputParam.class, paramType = "body")})
    @PostMapping("/add")
    public ResponseValue add() {
        LWhFormOutputParam param = CommonUtil.getObjFromReqBody(LWhFormOutputParam.class);
@@ -97,9 +93,9 @@
    /**
     * @Description 列表查询
     */
    @ApiOperation(value = "列表查询", notes = "列表查询")
    @ApiImplicitParam(name = "param", value = "出库单实体", required = true, dataType = "LWhFormOutputParam",
        paramType = "query")
    // @ApiOperation(value = "列表查询", notes = "列表查询")
//    @ApiImplicitParam(name = "param", value = "出库单实体", required = true, dataType = "LWhFormOutputParam",
//        paramType = "query")
    @GetMapping("/list")
    public ResponseValue queryFormOutputList() {
        LWhFormOutputQry param = CommonUtil.getObjFromReq(LWhFormOutputQry.class);
@@ -142,8 +138,8 @@
    /**
     * 根据id查询详情
     */
    @ApiOperation(value = "根据id查询详情", notes = "根据id查询详情")
    @ApiImplicitParam(name = "id", value = "出库单id", required = true, dataType = "Long", paramType = "query")
    // @ApiOperation(value = "根据id查询详情", notes = "根据id查询详情")
    // @ApiImplicitParam(name = "id", value = "出库单id", required = true, dataType = "Long", paramType = "query")
    @GetMapping("/detail")
    public ResponseValue getById(Long id) throws Exception {
        if (id == null) {
@@ -195,9 +191,9 @@
        return goodsTemplateInfoVO;
    }
    @ApiOperation(value = "查询出库单详情明细", notes = "查询出库单详情明细")
    @ApiImplicitParam(name = "formOutputQry", value = "出库单详情查询条件", required = true, dataType = "LWhFormOutputQry",
        paramType = "query")
    // @ApiOperation(value = "查询出库单详情明细", notes = "查询出库单详情明细")
//    @ApiImplicitParam(name = "formOutputQry", value = "出库单详情查询条件", required = true, dataType = "LWhFormOutputQry",
//        paramType = "query")
    @GetMapping("/detail/list")
    public ResponseValue queryFormOutputDetailList() {
        LWhFormOutputQry formOutputQry = CommonUtil.getObjFromReq(LWhFormOutputQry.class);
@@ -210,8 +206,8 @@
        return ResponseValue.success(genericPager);
    }
    @ApiOperation(value = "出库单导出", notes = "出库单导出")
    @ApiImplicitParam(name = "id", value = "出库单id", required = true, dataType = "Long", paramType = "query")
    // @ApiOperation(value = "出库单导出", notes = "出库单导出")
    // @ApiImplicitParam(name = "id", value = "出库单id", required = true, dataType = "Long", paramType = "query")
    @GetMapping("/list/export")
    public ResponseValue export(Long id, HttpServletResponse response) throws Exception {
        FinSysTenantUser sysInfo = this.getSysInfo();
consum-base/src/main/java/com/consum/base/controller/LWhFormProcureController.java
@@ -8,8 +8,6 @@
import java.util.List;
import java.util.Map;
import java.util.Optional;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
@@ -20,6 +18,8 @@
import com.consum.base.service.*;
import com.consum.base.util.DateUtil;
import com.consum.model.po.*;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletResponse;
import org.apache.commons.compress.utils.Lists;
import org.apache.poi.ss.usermodel.Workbook;
import org.springframework.beans.BeanUtils;
@@ -55,10 +55,10 @@
import cn.afterturn.easypoi.excel.ExcelExportUtil;
import cn.afterturn.easypoi.excel.entity.TemplateExportParams;
import cn.hutool.core.util.ReflectUtil;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
/**
 * @ClassName LWhFormProcureController
@@ -66,7 +66,7 @@
 * @Description
 * @Version 1.0
 **/
@Api(value = "仓库表单采购", tags = "仓库表单采购")
//@Api(value = "仓库表单采购", tags = "仓库表单采购")
@RestController
@RequestMapping("/pc/whForm/procure")
public class LWhFormProcureController extends BaseController {
@@ -130,9 +130,9 @@
    /**
     * @Description 列表查询
     */
    @ApiOperation(value = "采购单列表查询", notes = "采购单列表查询")
    @ApiImplicitParams({@ApiImplicitParam(name = "param", value = "采购查询条件", required = true,
            dataType = "FormProcureQryDto", paramType = "query")})
    // @ApiOperation(value = "采购单列表查询", notes = "采购单列表查询")
//    @ApiImplicitParams({@ApiImplicitParam(name = "param", value = "采购查询条件", required = true,
//            dataType = "FormProcureQryDto", paramType = "query")})
    @GetMapping("/list")
    public ResponseValue queryFormProcureList() {
        FormProcureQry param = CommonUtil.getObjFromReq(FormProcureQry.class);
@@ -291,9 +291,9 @@
        return goodsModelVO;
    }
    @ApiOperation(value = "采购单明细查询", notes = "采购单明细查询")
    @ApiImplicitParams({@ApiImplicitParam(name = "param", value = "采购单明细查询", required = true,
            dataType = "FormProcureQryDto", paramType = "query")})
    // @ApiOperation(value = "采购单明细查询", notes = "采购单明细查询")
//    @ApiImplicitParams({@ApiImplicitParam(name = "param", value = "采购单明细查询", required = true,
//            dataType = "FormProcureQryDto", paramType = "query")})
    @GetMapping("detail/list")
    public ResponseValue queryFormProcureDetailList() {
        FormProcureQry formProcureQry = CommonUtil.getObjFromReq(FormProcureQry.class);
@@ -312,9 +312,9 @@
        return ResponseValue.success(genericPager);
    }
    @ApiOperation(value = "采购单导出", notes = "采购单导出")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "id", value = "采购单id", required = true, dataType = "Long", paramType = "query")})
    // @ApiOperation(value = "采购单导出", notes = "采购单导出")
//    @ApiImplicitParams({
//            @ApiImplicitParam(name = "id", value = "采购单id", required = true, dataType = "Long", paramType = "query")})
    @GetMapping("/list/export")
    public ResponseValue<String> export(Long id, HttpServletResponse response) throws Exception {
        TemplateExportParams params = new TemplateExportParams("import/采购入库单.xls");
@@ -358,7 +358,7 @@
     * @param file
     * @return
     */
    @ApiOperation(value = "采购单导入", notes = "采购单导入")
    // @ApiOperation(value = "采购单导入", notes = "采购单导入")
    @PostMapping("/import")
    public ResponseValue upload(MultipartFile file) {
        String originalFilename = file.getOriginalFilename();
consum-base/src/main/java/com/consum/base/controller/LWhFormScrappedController.java
@@ -5,8 +5,7 @@
import java.util.Map;
import java.util.Optional;
import javax.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpServletResponse;
import org.apache.commons.compress.utils.Lists;
import org.apache.poi.ss.usermodel.Workbook;
import org.springframework.beans.BeanUtils;
@@ -34,17 +33,17 @@
import cn.afterturn.easypoi.excel.ExcelExportUtil;
import cn.afterturn.easypoi.excel.entity.TemplateExportParams;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
/**
 * @Description 报废单
 * @Author 卢庆阳
 * @Date 2023/11/1
 */
@Api(value = "报废单", tags = "报废单")
// @Api(value = "报废单", tags = "报废单")
@RestController
@RequestMapping("/pc/l/wh/form/scrapped")
public class LWhFormScrappedController extends BaseController {
@@ -57,8 +56,8 @@
     * @Author 卢庆阳
     * @Date 2023/11/1
     */
    @ApiOperation(value = "新增报废单", notes = "新增报废单")
    @ApiImplicitParams({@ApiImplicitParam(name = "param")})
    // @ApiOperation(value = "新增报废单", notes = "新增报废单")
    // @ApiImplicitParams({@ApiImplicitParam(name = "param")})
    @PostMapping("/add")
    public ResponseValue add() {
        LWhFormScrappedParam param = CommonUtil.getObjFromReqBody(LWhFormScrappedParam.class);
@@ -80,9 +79,9 @@
     *       <p>
     *       </>1.查询报废单 2.查询报废单物品
     */
    @ApiOperation(value = "列表查询", notes = "列表查询")
    @ApiImplicitParams({
        @ApiImplicitParam(name = "param", value = "查询条件", dataType = "LWhFormScrappedQry", paramType = "query")})
    // @ApiOperation(value = "列表查询", notes = "列表查询")
//    @ApiImplicitParams({
//        @ApiImplicitParam(name = "param", value = "查询条件", dataType = "LWhFormScrappedQry", paramType = "query")})
    @GetMapping("/list")
    public ResponseValue queryList() {
        LWhFormScrappedQry param = CommonUtil.getObjFromReq(LWhFormScrappedQry.class);
@@ -117,8 +116,8 @@
     * @Author 卢庆阳
     * @Date 2023/11/2
     */
    @ApiOperation(value = "根据id查询详情", notes = "根据id查询详情")
    @ApiImplicitParams({@ApiImplicitParam(name = "id", value = "报废单id", dataType = "Long", paramType = "query")})
    // @ApiOperation(value = "根据id查询详情", notes = "根据id查询详情")
    // @ApiImplicitParams({@ApiImplicitParam(name = "id", value = "报废单id", dataType = "Long", paramType = "query")})
    @GetMapping("/detail")
    public ResponseValue getById(Long id) {
        if (id == null) {
@@ -134,9 +133,9 @@
     * @param
     * @return
     */
    @ApiOperation(value = "报废明细", notes = "报废明细")
    @ApiImplicitParams({
        @ApiImplicitParam(name = "param", value = "查询条件", dataType = "LWhFormScrappedQry", paramType = "query")})
    // @ApiOperation(value = "报废明细", notes = "报废明细")
//    @ApiImplicitParams({
//        @ApiImplicitParam(name = "param", value = "查询条件", dataType = "LWhFormScrappedQry", paramType = "query")})
    @GetMapping("/list/detailList")
    public ResponseValue queryDetailList() {
        LWhFormScrappedQry param = CommonUtil.getObjFromReq(LWhFormScrappedQry.class);
@@ -152,8 +151,8 @@
        return ResponseValue.success(pageUtil);
    }
    @ApiOperation(value = "导出报废单", notes = "导出报废单")
    @ApiImplicitParams({@ApiImplicitParam(name = "id", value = "报废单id", dataType = "Long", paramType = "query")})
    // @ApiOperation(value = "导出报废单", notes = "导出报废单")
//    @ApiImplicitParams({@ApiImplicitParam(name = "id", value = "报废单id", dataType = "Long", paramType = "query")})
    @GetMapping("/list/export")
    public ResponseValue<String> export(Long id, HttpServletResponse response) throws Exception {
        if (id == null) {
consum-base/src/main/java/com/consum/base/controller/LWhFormTransferController.java
@@ -1,7 +1,6 @@
package com.consum.base.controller;
import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
@@ -10,9 +9,6 @@
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import javax.servlet.http.HttpServletResponse;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.StrUtil;
import com.alibaba.excel.EasyExcelFactory;
@@ -22,13 +18,13 @@
import com.alibaba.excel.exception.ExcelDataConvertException;
import com.alibaba.fastjson.JSONObject;
import com.consum.base.pojo.*;
import com.consum.base.pojo.excel.ImportProcureOrderTemplate;
import com.consum.base.pojo.excel.LWhFormTransferTemplate;
import com.consum.base.pojo.query.WarehouseQry;
import com.consum.base.service.*;
import com.consum.base.util.DateUtil;
import com.consum.base.util.ExcelStyleUtil;
import com.consum.model.po.*;
import jakarta.servlet.http.HttpServletResponse;
import org.apache.commons.compress.utils.Lists;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.poi.ss.usermodel.Workbook;
@@ -62,10 +58,6 @@
import cn.afterturn.easypoi.excel.ExcelExportUtil;
import cn.afterturn.easypoi.excel.entity.TemplateExportParams;
import cn.hutool.core.util.ReflectUtil;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import org.springframework.web.multipart.MultipartFile;
/**
@@ -75,7 +67,7 @@
 */
@RestController
@RequestMapping("/pc/l/wh/form/transfer")
@Api(value = "调拨分发管理", tags = "调拨分发管理")
// @Api(value = "调拨分发管理", tags = "调拨分发管理")
public class LWhFormTransferController extends BaseController {
    @Autowired
@@ -106,8 +98,8 @@
    /**
     * @Description 新增
     */
    @ApiOperation(value = "单据新增", notes = "单据新增")
    @ApiImplicitParam(name = "param", value = "单据新增", required = true, dataType = "LWhFormTransferParam")
    // @ApiOperation(value = "单据新增", notes = "单据新增")
    // @ApiImplicitParam(name = "param", value = "单据新增", required = true, dataType = "LWhFormTransferParam")
    @PostMapping("/add")
    @Transactional(rollbackFor = Exception.class)
    public ResponseValue add() throws Exception {
@@ -237,7 +229,7 @@
     * @return
     * @throws Exception
     */
    @ApiImplicitParams({@ApiImplicitParam(name = "id", value = "调拨单id", required = true, dataType = "Long"),})
    // @ApiImplicitParams({@ApiImplicitParam(name = "id", value = "调拨单id", required = true, dataType = "Long"),})
    @Transactional(rollbackFor = Exception.class)
    @PostMapping("import")
    public ResponseValue upload(@RequestBody String idStr) throws Exception {
@@ -310,10 +302,10 @@
     * <p>
     * 2.查询物品型号
     */
    @ApiOperation(value = "单据列表查询", notes = "单据列表查询")
    @ApiImplicitParams({@ApiImplicitParam(name = "page", value = "页码", required = true, dataType = "int"),
            @ApiImplicitParam(name = "size", value = "每页条数", required = true, dataType = "int"),
            @ApiImplicitParam(name = "param", value = "条件参数", required = true, dataType = "TransferQry"),})
    // @ApiOperation(value = "单据列表查询", notes = "单据列表查询")
//    @ApiImplicitParams({@ApiImplicitParam(name = "page", value = "页码", required = true, dataType = "int"),
//            @ApiImplicitParam(name = "size", value = "每页条数", required = true, dataType = "int"),
//            @ApiImplicitParam(name = "param", value = "条件参数", required = true, dataType = "TransferQry"),})
    @GetMapping("/list")
    public ResponseValue queryFormTransferList() {
        TransferQry param = CommonUtil.getObjFromReq(TransferQry.class);
@@ -359,8 +351,8 @@
     * @Author 卢庆阳
     * @Date 2023/10/30
     */
    @ApiOperation(value = "根据id查询详情", notes = "根据id查询详情")
    @ApiImplicitParams({@ApiImplicitParam(name = "id", value = "调拨单id", required = true, dataType = "Long"),})
    // @ApiOperation(value = "根据id查询详情", notes = "根据id查询详情")
    // @ApiImplicitParams({@ApiImplicitParam(name = "id", value = "调拨单id", required = true, dataType = "Long"),})
    @GetMapping("/detail")
    public ResponseValue getById(Long id) {
        if (id == null) {
@@ -370,8 +362,8 @@
        return ResponseValue.success(vo);
    }
    @ApiOperation(value = "调拨明细列表", notes = "调拨明细列表")
    @ApiImplicitParams({@ApiImplicitParam(name = "param", value = "条件", required = true, dataType = "TransferQry"),})
    // @ApiOperation(value = "调拨明细列表", notes = "调拨明细列表")
    // @ApiImplicitParams({@ApiImplicitParam(name = "param", value = "条件", required = true, dataType = "TransferQry"),})
    @GetMapping("/detail/list")
    public ResponseValue queryFormTransferDetailList() {
        TransferQry param = CommonUtil.getObjFromReq(TransferQry.class);
@@ -398,8 +390,8 @@
     * @author 卢庆阳
     * @date 2023/10/31
     */
    @ApiOperation(value = "撤销", notes = "撤销")
    @ApiImplicitParams({@ApiImplicitParam(name = "id", value = "调拨单id", required = true, dataType = "Long"),})
    // @ApiOperation(value = "撤销", notes = "撤销")
    // @ApiImplicitParams({@ApiImplicitParam(name = "id", value = "调拨单id", required = true, dataType = "Long"),})
    @PostMapping("/updStatus")
    public ResponseValue updateStatus(Long id) {
        if (id == null) {
@@ -419,8 +411,8 @@
     * @Author 卢庆阳
     * @Date 2023/10/31
     */
    @ApiOperation(value = "调拨入库", notes = "调拨入库")
    @ApiImplicitParams({@ApiImplicitParam(name = "id", value = "调拨单id", required = true, dataType = "Long"),})
    // @ApiOperation(value = "调拨入库", notes = "调拨入库")
    // @ApiImplicitParams({@ApiImplicitParam(name = "id", value = "调拨单id", required = true, dataType = "Long"),})
    @PostMapping("/income")
    public ResponseValue income(Long id) throws Exception {
        lWhFormTransferCoreService.doTransferInPut(id, getCurrentUser(), null);
@@ -432,8 +424,8 @@
     * @Author 卢庆阳
     * @Date 2023/10/31
     */
    @ApiOperation(value = "调拨出库", notes = "调拨出库")
    @ApiImplicitParams({@ApiImplicitParam(name = "id", value = "调拨单id", required = true, dataType = "Long"),})
    // @ApiOperation(value = "调拨出库", notes = "调拨出库")
    // @ApiImplicitParams({@ApiImplicitParam(name = "id", value = "调拨单id", required = true, dataType = "Long"),})
    @PostMapping("/output")
    public ResponseValue output(Long id) throws Exception {
@@ -464,9 +456,9 @@
        return ResponseValue.success();
    }
    @ApiOperation(value = "调拨单导出", notes = "调拨单导出")
    @ApiImplicitParams({@ApiImplicitParam(name = "id", value = "调拨单id", required = true, dataType = "Long"),
            @ApiImplicitParam(name = "type", value = "导出类型 1 入库 2 出库", required = true, dataType = "Integer"),})
    // @ApiOperation(value = "调拨单导出", notes = "调拨单导出")
//    @ApiImplicitParams({@ApiImplicitParam(name = "id", value = "调拨单id", required = true, dataType = "Long"),
//            @ApiImplicitParam(name = "type", value = "导出类型 1 入库 2 出库", required = true, dataType = "Integer"),})
    @GetMapping("/list/export")
    public ResponseValue<String> export(Long id, Integer type, HttpServletResponse response) throws Exception {
@@ -517,8 +509,8 @@
     * @param
     * @return
     */
    @ApiOperation(value = "部门物品分发列表明细", notes = "部门物品分发列表明细")
    @ApiImplicitParams({@ApiImplicitParam(name = "transferQryDto", value = "调拨单查询条件", required = true)})
    // @ApiOperation(value = "部门物品分发列表明细", notes = "部门物品分发列表明细")
    // @ApiImplicitParams({@ApiImplicitParam(name = "transferQryDto", value = "调拨单查询条件", required = true)})
    @GetMapping("/department/list")
    public ResponseValue departmentTransferList() {
        TransferQry param = CommonUtil.getObjFromReq(TransferQry.class);
@@ -539,8 +531,8 @@
        return ResponseValue.success(transferInfoDetailsVoGenericPager);
    }
    @ApiOperation(value = "使用人修改", notes = "使用人修改")
    @ApiImplicitParams({@ApiImplicitParam(name = "procureModelInfoDto", value = "使用信息", required = true)})
    // @ApiOperation(value = "使用人修改", notes = "使用人修改")
    // @ApiImplicitParams({@ApiImplicitParam(name = "procureModelInfoDto", value = "使用信息", required = true)})
    @PostMapping("/useInfo/update")
    public ResponseValue infoUpdate() {
@@ -611,8 +603,8 @@
     * @param transferOrderId
     * @return
     */
    @ApiOperation(value = "部门物品使用人记录", notes = "部门物品使用人记录")
    @ApiImplicitParams({@ApiImplicitParam(name = "transferOrderId", value = "调拨单id", required = true)})
    // @ApiOperation(value = "部门物品使用人记录", notes = "部门物品使用人记录")
    // @ApiImplicitParams({@ApiImplicitParam(name = "transferOrderId", value = "调拨单id", required = true)})
    @GetMapping("/use/record")
    public ResponseValue useRecord(Long transferOrderId) {
        S_user_core currentUser = this.getCurrentUser();
@@ -669,9 +661,9 @@
        return ResponseValue.success(goodsUseRecordList);
    }
    @ApiOperation(value = "查询部门下的分发单", notes = "查询部门下的分发单")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "agencyId", value = "机构id", required = true, dataType = "Long", paramType = "query")})
    // @ApiOperation(value = "查询部门下的分发单", notes = "查询部门下的分发单")
//    @ApiImplicitParams({
//            @ApiImplicitParam(name = "agencyId", value = "机构id", required = true, dataType = "Long", paramType = "query")})
    @GetMapping("/query/transfList")
    public ResponseValue queryDepartmentTransferOrderList() {
@@ -687,7 +679,7 @@
     * @param file
     * @return
     */
    @ApiOperation(value = "分发单导入", notes = "分发单导入")
    // @ApiOperation(value = "分发单导入", notes = "分发单导入")
    @PostMapping("/import2")
    public ResponseValue import2(MultipartFile file) {
        String originalFilename = file.getOriginalFilename();
@@ -845,7 +837,7 @@
                                        LWhProcureModelUserParam lWhProcureModelUserParam = new LWhProcureModelUserParam();
                                        lWhProcureModelUserParam.setGoodsNum(Integer.valueOf(num));
                                        lWhProcureModelUserParam.setNowUserName(user);
                                        lWhProcureModelUserParam.setNowUserPhone(new Long(userContactPhone));
                                        lWhProcureModelUserParam.setNowUserPhone(Convert.toLong(userContactPhone));
                                        Integer counts1 = optional3.get().getCounts();
                                        Integer num1 = optional3.get().getNum();
                                        optional3.get().setCounts(Integer.valueOf(num) + counts1);
@@ -879,11 +871,11 @@
                                    // 查询库存数量
                                    Integer nowNum = selectAllNumber(lWhFormTransferParam.getOutAgencyId(), selByModelNameAndGoodsTemplatesId.getId());
                                    if(nowNum<new Integer(num)){
                                    if(nowNum< Convert.toInt(num)){
                                        throw new ExcelAnalysisException("第" + index + "条数据" + "品名:[" + goodsName + "] 规格型号:[" + goodModelName + "] 库存数量不足:["+nowNum+"]");
                                    }
                                    lWhProcureModelUserParam.setNowUserName(user);
                                    lWhProcureModelUserParam.setNowUserPhone(new Long(userContactPhone));
                                    lWhProcureModelUserParam.setNowUserPhone(Convert.toLong(userContactPhone));
                                    addLWhProcureModelUserParam.add(lWhProcureModelUserParam);
                                    lWhTransferModelParam.setProcureModelUserList(addLWhProcureModelUserParam);
                                    optional.get().getModels().add(lWhTransferModelParam);
@@ -909,20 +901,20 @@
                                // 规格id
                                lWhTransferModelParam.setBaseGoodsModelsId(selByModelNameAndGoodsTemplatesId.getId());
                                // 新增的时候默认一条
                                lWhTransferModelParam.setCounts(new Integer(num));
                                lWhTransferModelParam.setNum(new Integer(num));
                                lWhTransferModelParam.setCounts(Convert.toInt(num));
                                lWhTransferModelParam.setNum(Convert.toInt(num));
                                List<LWhProcureModelUserParam> lWhFormProcureGoodsInfoParam = new ArrayList<>();
                                LWhProcureModelUserParam lWhProcureModelUserParam = new LWhProcureModelUserParam();
                                lWhProcureModelUserParam.setGoodsNum(Integer.valueOf(num));
                                // 查询库存数量
                                Integer nowNum = selectAllNumber(lWhFormTransferParam.getOutAgencyId(), selByModelNameAndGoodsTemplatesId.getId());
                                if(nowNum<new Integer(num)){
                                if(nowNum<Convert.toInt(num)){
                                    throw new ExcelAnalysisException("第" + index + "条数据 " + "品名:[" + goodsName + "] 规格型号:[" + goodModelName + "] 库存数量不足:["+nowNum+"]");
                                }
                                lWhProcureModelUserParam.setNowUserName(user);
                                lWhProcureModelUserParam.setNowUserPhone(new Long(userContactPhone));
                                lWhProcureModelUserParam.setNowUserPhone(Convert.toLong(userContactPhone));
                                lWhFormProcureGoodsInfoParam.add(lWhProcureModelUserParam);
                                lWhTransferModelParam.setProcureModelUserList(lWhFormProcureGoodsInfoParam);
                                lWhTransferModelParam.setBaseGoodsModelsName(goodModelName);
consum-base/src/main/java/com/consum/base/controller/LWhGoodsStatisticsController.java
@@ -1,7 +1,5 @@
package com.consum.base.controller;
import javax.annotation.Resource;
import jakarta.annotation.Resource;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@@ -14,7 +12,7 @@
import com.iplatform.model.po.S_user_core;
import com.walker.web.ResponseValue;
import io.swagger.annotations.Api;
/**
 * @ClassName LWhGoodsStatisticsController
@@ -23,7 +21,7 @@
 * @Description 物品统计
 * @Version 1.0
 **/
@Api(value = "物品统计", tags = "物品统计")
// @Api(value = "物品统计", tags = "物品统计")
@RestController
@RequestMapping("/pc/warehouse/goodsStatistics")
public class LWhGoodsStatisticsController extends BaseController {
consum-base/src/main/java/com/consum/base/controller/LWhHomeStatisticsController.java
@@ -3,8 +3,7 @@
import java.util.List;
import java.util.Map;
import javax.annotation.Resource;
import jakarta.annotation.Resource;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@@ -16,7 +15,7 @@
import com.walker.web.ResponseValue;
import cn.hutool.core.convert.Convert;
import io.swagger.annotations.Api;
/**
 * @ClassName LWhHomeStatisticsController
@@ -28,7 +27,7 @@
@RestController
@RequestMapping("/pc/warehouse/homeStatistics")
@Api(value = "首页统计", tags = "首页统计")
// @Api(value = "首页统计", tags = "首页统计")
public class LWhHomeStatisticsController extends BaseController {
    @Resource
    private LWhGoodsService lWhGoodsService;
consum-base/src/main/java/com/consum/base/controller/LWhProcureModelController.java
@@ -4,8 +4,7 @@
import java.util.Map;
import java.util.stream.Collectors;
import javax.annotation.Resource;
import jakarta.annotation.Resource;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@@ -17,9 +16,7 @@
import com.walker.infrastructure.utils.CollectionUtils;
import com.walker.web.ResponseValue;
import io.swagger.annotations.Api;
@Api(value = "物品型号", tags = "物品型号")
// @Api(value = "物品型号", tags = "物品型号")
@RestController
@RequestMapping("/pc/warehouse/procureModel")
public class LWhProcureModelController extends BaseController {
consum-base/src/main/java/com/consum/base/controller/LWhWarningConfigController.java
@@ -10,8 +10,7 @@
import java.util.Map;
import java.util.stream.Collectors;
import javax.annotation.Resource;
import jakarta.annotation.Resource;
import org.apache.commons.lang3.StringUtils;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.InputStreamResource;
@@ -50,7 +49,7 @@
import cn.afterturn.easypoi.excel.entity.ImportParams;
import cn.afterturn.easypoi.excel.entity.result.ExcelImportResult;
import cn.hutool.core.convert.Convert;
import io.swagger.annotations.Api;
/**
 * @ClassName LWhWarningConfigController
@@ -61,7 +60,7 @@
 **/
@RestController
@RequestMapping("/pc/warehouse/warningConfig")
@Api(value = "库存阀值", tags = "库存阀值")
// @Api(value = "库存阀值", tags = "库存阀值")
public class LWhWarningConfigController extends BaseController {
    @Resource
consum-base/src/main/java/com/consum/base/controller/LWhWarningController.java
@@ -2,8 +2,7 @@
import java.util.Map;
import javax.annotation.Resource;
import jakarta.annotation.Resource;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@@ -18,7 +17,7 @@
import com.walker.infrastructure.utils.DateUtils;
import com.walker.web.ResponseValue;
import io.swagger.annotations.Api;
/**
 * @ClassName LWhWarningConfigController
@@ -29,7 +28,7 @@
 **/
@RestController
@RequestMapping("/pc/warehouse/warning")
@Api(value = "预警查询", tags = "预警查询")
// @Api(value = "预警查询", tags = "预警查询")
public class LWhWarningController extends BaseController {
    @Resource
consum-base/src/main/java/com/consum/base/controller/UsingFormBackController.java
@@ -16,8 +16,8 @@
import com.consum.model.po.FinSysTenantUser;
import com.walker.web.ResponseValue;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
/**
 * @author asus
@@ -25,7 +25,7 @@
 * @description: 部门物品退回单
 * @date 2023/12/1 11:36
 */
@Api(value = "部门物品退回", tags = "部门物品退回")
// @Api(value = "部门物品退回", tags = "部门物品退回")
@RestController
@RequestMapping("/pc/l/wh/using/form/back")
public class UsingFormBackController extends BaseController {
@@ -33,7 +33,7 @@
    @Autowired
    private UsingFormBackService usingFormBackService;
    @ApiOperation(value = "部门物品退回单新增接口", notes = "部门物品退回单新增接口")
    // @ApiOperation(value = "部门物品退回单新增接口", notes = "部门物品退回单新增接口")
    @PostMapping("/add")
    public ResponseValue add() {
        UsingFormBackParam usingFormBackParam = CommonUtil.getObjFromReqBody(UsingFormBackParam.class);
@@ -49,7 +49,7 @@
        return ResponseValue.success();
    }
    @ApiOperation(value = "部门物品退回单查询接口", notes = "部门物品退回单查询接口")
    // @ApiOperation(value = "部门物品退回单查询接口", notes = "部门物品退回单查询接口")
    @GetMapping("/list")
    public ResponseValue query() {
        UsingFormBackQry usingFormBackParam = CommonUtil.getObjFromReq(UsingFormBackQry.class);
@@ -66,7 +66,7 @@
        return ResponseValue.success(page);
    }
    @ApiOperation(value = "部门物品退回单详情接口", notes = "部门物品退回单详情接口")
    // @ApiOperation(value = "部门物品退回单详情接口", notes = "部门物品退回单详情接口")
    @GetMapping("/detail")
    public ResponseValue detail(Long id) {
        FinSysTenantUser sysInfo = getSysInfo();
@@ -77,7 +77,7 @@
        return ResponseValue.success(detail);
    }
    @ApiOperation(value = "部门物品退回明细查询接口", notes = "部门物品退回明细查询接口")
    // @ApiOperation(value = "部门物品退回明细查询接口", notes = "部门物品退回明细查询接口")
    @GetMapping("/list/detail")
    public ResponseValue queryListDetail() {
        UsingFormBackQry usingFormBackParam = CommonUtil.getObjFromReq(UsingFormBackQry.class);
consum-base/src/main/java/com/consum/base/core/CodeGeneratorService.java
@@ -1,5 +1,6 @@
package com.consum.base.core;
import com.consum.base.core.utils.IdUtil;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
@@ -26,7 +27,7 @@
            get(sql, new SqlParameter().put("CODE_PREFIX", CODE_PREFIX), new BaseCodeIndexing());
        if (codeIndexing == null) {
            codeIndexing = new BaseCodeIndexing();
            codeIndexing.setId(NumberGenerator.getLongSequenceNumber());
            codeIndexing.setId(IdUtil.generateId());
            codeIndexing.setCodePrefix(CODE_PREFIX);
            codeIndexing.setCodeIndex(1);
            insert(codeIndexing);
consum-base/src/main/java/com/consum/base/core/utils/CommonUtil.java
@@ -4,13 +4,10 @@
import cn.hutool.core.util.ReflectUtil;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.BeanUtils;
import jakarta.servlet.ServletInputStream;import jakarta.servlet.http.HttpServletRequest;import jakarta.servlet.http.HttpServletResponse;import org.springframework.beans.BeanUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
consum-base/src/main/java/com/consum/base/pojo/DepFormScrappedGoodsParam.java
@@ -2,7 +2,6 @@
import java.util.List;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
@@ -15,14 +14,14 @@
@Data
public class DepFormScrappedGoodsParam {
    @ApiModelProperty("分类id")
    // @ApiModelProperty("分类id")
    private Long baseCategoryId;
    @ApiModelProperty("物品模版编号")
    // @ApiModelProperty("物品模版编号")
    private Long baseGoodsTemplateId;
    private String goodsTemplateName;
    @ApiModelProperty("调拨单Id")
    // @ApiModelProperty("调拨单Id")
    private Long transBusinessId;
    /**
consum-base/src/main/java/com/consum/base/pojo/DepFormScrappedModelParam.java
@@ -1,6 +1,4 @@
package com.consum.base.pojo;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
@@ -12,37 +10,37 @@
 **/
@Data
public class DepFormScrappedModelParam {
    @ApiModelProperty("物品型号id")
    // @ApiModelProperty("物品型号id")
    private Long baseGoodsModelsId;
    @ApiModelProperty("价值类型")
    // @ApiModelProperty("价值类型")
    private String classification;// "B类"
    @ApiModelProperty("报废数量")
    // @ApiModelProperty("报废数量")
    private Integer counts;
    @ApiModelProperty("分发数量")
    // @ApiModelProperty("分发数量")
    private Integer goodsNum;
    @ApiModelProperty("在用数量")
    // @ApiModelProperty("在用数量")
    private Integer userUseCount;
    @ApiModelProperty("物品id")
    // @ApiModelProperty("物品id")
    private Long goodsTemplatesId;
    @ApiModelProperty("物品型号")
    // @ApiModelProperty("物品型号")
    private String modelName;
    @ApiModelProperty("调拨使用人表中ID(A类物品填)")
    // @ApiModelProperty("调拨使用人表中ID(A类物品填)")
    private Long modelUserId;
    @ApiModelProperty("使用人员名称(可不填)")
    // @ApiModelProperty("使用人员名称(可不填)")
    private String nowUserName;
    @ApiModelProperty("报废原因CODE")
    // @ApiModelProperty("报废原因CODE")
    private Long scrappedCode;
    @ApiModelProperty("计量单位")
    // @ApiModelProperty("计量单位")
    private String unit;
}
consum-base/src/main/java/com/consum/base/pojo/FinSysTenantDepartmentParam.java
@@ -1,9 +1,8 @@
package com.consum.base.pojo;
import com.walker.web.param.ParamRequest;
import io.swagger.annotations.ApiModel;
@ApiModel(value = "FinSysTenantDepartmentParam")
// // @ApiModel(value = "FinSysTenantDepartmentParam")
public class FinSysTenantDepartmentParam extends ParamRequest {
    /**
consum-base/src/main/java/com/consum/base/pojo/LDeptFormScrappedParam.java
@@ -1,36 +1,34 @@
package com.consum.base.pojo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.util.List;
@Data
@ApiModel(value = "新增报废单信息")
// // @ApiModel(value = "新增报废单信息")
public class LDeptFormScrappedParam {
    @ApiModelProperty(value = "部门id")
    // // @ApiModelProperty(value = "部门id")
    private Long departmentId;
    /**
     * 报废时间
     */
    @ApiModelProperty(value = "报废时间")
    // // @ApiModelProperty(value = "报废时间")
    private Long dealTime;
    /**
     * 手续
     */
    @ApiModelProperty(value = "手续")
    // // @ApiModelProperty(value = "手续")
    private String procureDoc;
    /**
     * 操作人
     */
//    @ApiModelProperty(value = "操作人")
//    // @ApiModelProperty(value = "操作人")
//    private String operatorName;
    @ApiModelProperty(value = "报废商品信息")
    // // @ApiModelProperty(value = "报废商品信息")
    private List<DepFormScrappedGoodsParam> goods;
consum-base/src/main/java/com/consum/base/pojo/LWFormsOutputGoodsInfoParam.java
@@ -1,7 +1,7 @@
package com.consum.base.pojo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import java.util.List;
import lombok.Data;
@@ -10,7 +10,7 @@
 * @Author 卢庆阳
 * @Date 2023/11/2
 */
@ApiModel(value = "记录物品信息和规格型号")
// // @ApiModel(value = "记录物品信息和规格型号")
@Data
public class LWFormsOutputGoodsInfoParam {
@@ -18,18 +18,18 @@
    /**
     * 物品id
     */
    @ApiModelProperty(value = "物品id")
    // @ApiModelProperty(value = "物品id")
    private Long baseGoodsTemplateId;
    /**
     * 物品模版名称
     */
    @ApiModelProperty(value = "物品模版名称")
    // @ApiModelProperty(value = "物品模版名称")
    private String goodsTemplateName;
    /**
     * 分类id
     */
    @ApiModelProperty(value = "分类id")
    // @ApiModelProperty(value = "分类id")
    private Long baseCategoryId;
    /**
consum-base/src/main/java/com/consum/base/pojo/LWFormsOutputGoodsModelParam.java
@@ -1,7 +1,7 @@
package com.consum.base.pojo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
@@ -11,23 +11,23 @@
 * @date 2023/11/13 11:29
 */
@Data
@ApiModel(value = "出库物品型号")
// // @ApiModel(value = "出库物品型号")
public class LWFormsOutputGoodsModelParam {
    //规格型号编号
    @ApiModelProperty(value = "规格型号编号")
    // // @ApiModelProperty(value = "规格型号编号")
    private Long baseGoodsModelsId;
    // 计量单位
    @ApiModelProperty(value = "计量单位")
    // // @ApiModelProperty(value = "计量单位")
    private String baseUnit;
    //库存数量
    @ApiModelProperty(value = "库存数量")
    // @ApiModelProperty(value = "库存数量")
    private Long warehouseNum;
    //数量
    @ApiModelProperty(value = "操作数量")
    // @ApiModelProperty(value = "操作数量")
    private Integer counts;
    @ApiModelProperty(value = "采购类型")
    // @ApiModelProperty(value = "采购类型")
    private Integer procureModelBusinessType;
}
consum-base/src/main/java/com/consum/base/pojo/LWarehouseFlowParam.java
@@ -1,8 +1,6 @@
package com.consum.base.pojo;
import com.walker.web.param.ParamRequest;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
@@ -11,63 +9,63 @@
 * @Description
 * @Version 1.0
 **/
@ApiModel(value = "库存流水查询参数")
// @ApiModel(value = "库存流水查询参数")
@Data
public class LWarehouseFlowParam extends ParamRequest {
    //单据类型 1 采购2 调拨 3出库4部门分发 5报废
    @ApiModelProperty(value = "单据类型 1 采购2 调拨 3出库4部门分发 5报废")
    // @ApiModelProperty(value = "单据类型 1 采购2 调拨 3出库4部门分发 5报废")
    private Short businessType;
    // 业务单据编号
    @ApiModelProperty(value = "业务单据编号")
    // @ApiModelProperty(value = "业务单据编号")
    private String businessFormCode;
    //物品名称
    @ApiModelProperty(value = "物品名称")
    // @ApiModelProperty(value = "物品名称")
    private String goodsTemplateName;
    private Long goodsTemplateId;
    //规格型号
    @ApiModelProperty(value = "规格型号")
    // @ApiModelProperty(value = "规格型号")
    private String baseGoodsModelsName;
    // 机构
    @ApiModelProperty(value = "机构")
    // @ApiModelProperty(value = "机构")
    private Long agencyId;
    @ApiModelProperty("部门ID")
    // @ApiModelProperty("部门ID")
    private Long departmentId;
    @ApiModelProperty("仓库类型")
    // @ApiModelProperty("仓库类型")
    private Long warehouseType;
    @ApiModelProperty("仓库id")
    // @ApiModelProperty("仓库id")
    private Long baseWarehouseId;
    @ApiModelProperty("规格型号")
    // @ApiModelProperty("规格型号")
    private Long baseGoodsModelsId;
    @ApiModelProperty("价值类型")
    // @ApiModelProperty("价值类型")
    private Short costType;
    @ApiModelProperty("同码表,1采购入库 2退还入库 3调拨入库 4盘盈入库 5申领出库 6调拨出库 7盘亏出库 8报废出库 9其他出库")
    // @ApiModelProperty("同码表,1采购入库 2退还入库 3调拨入库 4盘盈入库 5申领出库 6调拨出库 7盘亏出库 8报废出库 9其他出库")
    private Short flowType;
    @ApiModelProperty("同码表,1 物品申领 2 物品分发 3 物品退还 4 物品报废")
    // @ApiModelProperty("同码表,1 物品申领 2 物品分发 3 物品退还 4 物品报废")
    private Short depFlowType;
    //创建人
    @ApiModelProperty(value = "创建人")
    // @ApiModelProperty(value = "创建人")
    private String createdName;
    // 操作时间
    @ApiModelProperty(value = "操作时间")
    // @ApiModelProperty(value = "操作时间")
    private Long dealTimeStart;
    @ApiModelProperty(value = "操作时间")
    // @ApiModelProperty(value = "操作时间")
    private Long dealTimeEnd;
    @ApiModelProperty(value = "每页显示条数")
    // @ApiModelProperty(value = "每页显示条数")
    private Integer pageSize = 10;
    @ApiModelProperty(value = "当前页数")
    // @ApiModelProperty(value = "当前页数")
    private Integer pageNum = 1;
}
consum-base/src/main/java/com/consum/base/pojo/LWhFormOutputParam.java
@@ -1,8 +1,8 @@
package com.consum.base.pojo;
import com.walker.web.param.ParamRequest;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import java.util.List;
import lombok.Data;
@@ -11,29 +11,29 @@
 * @Author 卢庆阳
 * @Date 2023/10/27
 */
@ApiModel
// @ApiModel
@Data
public class LWhFormOutputParam extends ParamRequest {
    /**
     * 仓库id
     */
    @ApiModelProperty(value = "仓库id")
    // @ApiModelProperty(value = "仓库id")
    private Long warehouseId;
    /**
     * 出库时间
     */
    @ApiModelProperty(value = "出库时间")
    // @ApiModelProperty(value = "出库时间")
    private Long dealTime;
    /**
     * 出库手续
     */
    @ApiModelProperty(value = "出库手续")
    // @ApiModelProperty(value = "出库手续")
    private String doc;
    /**
     * 记录物品信息和规格型号
     */
    @ApiModelProperty(value = "记录物品信息和规格型号")
    // @ApiModelProperty(value = "记录物品信息和规格型号")
    private List<LWFormsOutputGoodsInfoParam> goods;
consum-base/src/main/java/com/consum/base/pojo/LWhFormScrappedGoodsInfoParam.java
@@ -1,7 +1,7 @@
package com.consum.base.pojo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import java.util.List;
import lombok.Data;
@@ -12,7 +12,7 @@
 * @date 2023/11/21 16:06
 */
@Data
@ApiModel(value = "LWhFormScrappedGoodsInfoParam", description = "报废单物品信息")
// @ApiModel(value = "LWhFormScrappedGoodsInfoParam", description = "报废单物品信息")
public class LWhFormScrappedGoodsInfoParam {
    /**
@@ -23,7 +23,7 @@
     * 物品id
     */
    private Long baseGoodsTemplateId;
    @ApiModelProperty(value = "规格型号编号")
    // @ApiModelProperty(value = "规格型号编号")
    private Long baseGoodsModelsId;
    /**
     * 报废单物品
consum-base/src/main/java/com/consum/base/pojo/LWhFormScrappedGoodsModelParams.java
@@ -1,10 +1,10 @@
package com.consum.base.pojo;
import io.swagger.annotations.ApiModel;
import lombok.Data;
@Data
@ApiModel(value = "LWhFormScrappedGoodsParams", description = "报废商品参数")
// @ApiModel(value = "LWhFormScrappedGoodsParams", description = "报废商品参数")
public class LWhFormScrappedGoodsModelParams {
    //规格型号编号
consum-base/src/main/java/com/consum/base/pojo/LWhFormScrappedParam.java
@@ -1,46 +1,46 @@
package com.consum.base.pojo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.util.List;
@Data
@ApiModel(value = "新增报废单信息")
// @ApiModel(value = "新增报废单信息")
public class LWhFormScrappedParam {
    /**
     * 机构id
     */
    @ApiModelProperty(value = "机构id")
    // @ApiModelProperty(value = "机构id")
    private Long agencyId;
    @ApiModelProperty(value = "部门id")
    // @ApiModelProperty(value = "部门id")
    private Long departmentId;
    /**
     * 仓库编号
     */
    @ApiModelProperty(value = "仓库编号")
    // @ApiModelProperty(value = "仓库编号")
    private Long warehouseId;
    /**
     * 操作人
     */
    @ApiModelProperty(value = "操作人")
    // @ApiModelProperty(value = "操作人")
    private Long operatorId;
    /**
     * 报废时间
     */
    @ApiModelProperty(value = "报废时间")
    // @ApiModelProperty(value = "报废时间")
    private Long dealTime;
    /**
     * 附件
     */
    @ApiModelProperty(value = "附件")
    // @ApiModelProperty(value = "附件")
    private String uploadFiles;
    @ApiModelProperty(value = "报废商品信息")
    // @ApiModelProperty(value = "报废商品信息")
    private List<LWhFormScrappedGoodsInfoParam> scrappedGoodsInfo;
consum-base/src/main/java/com/consum/base/pojo/LWhFormTransferGoodsInfoParam.java
@@ -1,7 +1,7 @@
package com.consum.base.pojo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import java.util.List;
import lombok.Data;
@@ -12,7 +12,7 @@
 * @Version 1.0
 **/
@Data
@ApiModel(value = "调拨单型号")
// @ApiModel(value = "调拨单型号")
public class LWhFormTransferGoodsInfoParam {
    // 主键
@@ -28,10 +28,10 @@
    //供应商
    private String supplier;
    @ApiModelProperty(value = "单据类型 1 采购2 调拨 3出库4部门分发")
    // @ApiModelProperty(value = "单据类型 1 采购2 调拨 3出库4部门分发")
    private Integer procureModelBusinessType;
    @ApiModelProperty(value = "规格型号编号")
    // @ApiModelProperty(value = "规格型号编号")
    private Long baseGoodsModelsId;
    private List<LWhTransferModelParam> models;
consum-base/src/main/java/com/consum/base/pojo/LWhProcureModelParam.java
@@ -1,7 +1,7 @@
package com.consum.base.pojo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import java.util.List;
import lombok.Data;
@@ -11,27 +11,27 @@
 * @Description
 * @Version 1.0
 **/
@ApiModel(value = "调拨单规格型号")
// @ApiModel(value = "调拨单规格型号")
@Data
public class LWhProcureModelParam {
    //规格型号编号
    @ApiModelProperty(value = "规格型号编号")
    // @ApiModelProperty(value = "规格型号编号")
    private Long baseGoodsModelsId;
    //价格
    @ApiModelProperty(value = "价格")
    // @ApiModelProperty(value = "价格")
    private Long price;
    //数量
    @ApiModelProperty(value = "数量")
    // @ApiModelProperty(value = "数量")
    private Integer counts;
    // 单据类型 1 采购2 调拨 3出库4部门分发
    @ApiModelProperty(value = "单据类型 1 采购2 调拨 3出库4部门分发")
    // @ApiModelProperty(value = "单据类型 1 采购2 调拨 3出库4部门分发")
    private Integer procureModelBusinessType;
    // 计量单位
    @ApiModelProperty(value = "计量单位")
    // @ApiModelProperty(value = "计量单位")
    private String baseUnit;
    @ApiModelProperty(value = "用户规格型号")
    // @ApiModelProperty(value = "用户规格型号")
    private List<LWhProcureModelUserParam> procureModelUserList;
}
consum-base/src/main/java/com/consum/base/pojo/LWhProcureModelUserParam.java
@@ -1,6 +1,6 @@
package com.consum.base.pojo;
import io.swagger.annotations.ApiModel;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@@ -14,7 +14,7 @@
@Data
@AllArgsConstructor
@NoArgsConstructor
@ApiModel(value = "物品分发使用人信息")
// // @ApiModel(value = "物品分发使用人信息")
public class LWhProcureModelUserParam {
    /**
consum-base/src/main/java/com/consum/base/pojo/LWhTransferModelParam.java
@@ -1,7 +1,7 @@
package com.consum.base.pojo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import java.util.List;
import lombok.Data;
@@ -12,31 +12,31 @@
 * @description: 调拨单规格型号
 * @date 2023/11/6 9:28
 */
@ApiModel(value = "调拨单规格型号")
// @ApiModel(value = "调拨单规格型号")
@Data
public class LWhTransferModelParam {
    // //规格型号编号
    @ApiModelProperty(value = "规格型号编号")
    // @ApiModelProperty(value = "规格型号编号")
    private Long baseGoodsModelsId;
    @ApiModelProperty(value = "规格型号名字")
    // @ApiModelProperty(value = "规格型号名字")
    private String baseGoodsModelsName;
    //价格
    @ApiModelProperty(value = "价格")
    // @ApiModelProperty(value = "价格")
    private Long price;
    //数量
    @ApiModelProperty(value = "数量")
    // @ApiModelProperty(value = "数量")
    private Integer counts;
    // 单据类型 1 采购2 调拨 3出库4部门分发
    @ApiModelProperty(value = "单据类型 1 采购2 调拨 3出库4部门分发")
    // @ApiModelProperty(value = "单据类型 1 采购2 调拨 3出库4部门分发")
    private Integer procureModelBusinessType;
    // 计量单位
    @ApiModelProperty(value = "计量单位")
    // @ApiModelProperty(value = "计量单位")
    private String baseUnit;
    @ApiModelProperty(value = "剩余数量")
    // @ApiModelProperty(value = "剩余数量")
    private Integer num;
    @ApiModelProperty(value = "用户规格型号")
    // @ApiModelProperty(value = "用户规格型号")
    private List<LWhProcureModelUserParam> procureModelUserList;
}
consum-base/src/main/java/com/consum/base/pojo/WarehouseManagerInfo.java
@@ -1,7 +1,5 @@
package com.consum.base.pojo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
@@ -11,12 +9,12 @@
 * @date 2023/11/17 13:07
 */
@Data
@ApiModel(value = "库管员信息")
// @ApiModel(value = "库管员信息")
public class WarehouseManagerInfo {
    @ApiModelProperty(value = "库管员id")
    // @ApiModelProperty(value = "库管员id")
    private Long managerId;
    @ApiModelProperty(value = "库管员姓名")
    // @ApiModelProperty(value = "库管员姓名")
    private String managerName;
}
consum-base/src/main/java/com/consum/base/pojo/WarnConfImEntity.java
@@ -3,7 +3,7 @@
import cn.afterturn.easypoi.excel.annotation.Excel;
import cn.afterturn.easypoi.handler.inter.IExcelDataModel;
import cn.afterturn.easypoi.handler.inter.IExcelModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
@@ -16,33 +16,33 @@
@Data
public class WarnConfImEntity implements IExcelDataModel, IExcelModel {
    @ApiModelProperty("仓库名称")
    // @ApiModelProperty("仓库名称")
    @Excel(name = "仓库名称")
    private String warehouseName;
    @Excel(name = "物品名称")
    @ApiModelProperty(value = "物品名称")
    // @ApiModelProperty(value = "物品名称")
    private String goodsTemplateName;
    @Excel(name = "型号名称")
    @ApiModelProperty("型号名称")
    // @ApiModelProperty("型号名称")
    private String goodsModelName;
    @Excel(name = "保底库存")
    @ApiModelProperty("保底库存")
    // @ApiModelProperty("保底库存")
    private Integer lowerLimit;
    @Excel(name = "封顶库存")
    @ApiModelProperty("封顶库存")
    // @ApiModelProperty("封顶库存")
    private Integer upperLimit;
    @ApiModelProperty("物品id")
    // @ApiModelProperty("物品id")
    private Long goodsTemplateId;
    @ApiModelProperty("仓库id")
    // @ApiModelProperty("仓库id")
    private Long baseWarehouseId;
    @ApiModelProperty("物品型号id")
    // @ApiModelProperty("物品型号id")
    private Long baseGoodsModelsId;
consum-base/src/main/java/com/consum/base/pojo/WhWarningConfigParam.java
@@ -1,6 +1,6 @@
package com.consum.base.pojo;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
@@ -12,10 +12,10 @@
 **/
@Data
public class WhWarningConfigParam {
    @ApiModelProperty("仓库类型")
    // @ApiModelProperty("仓库类型")
    private Long warehouseType;
    @ApiModelProperty("仓库id")
    // @ApiModelProperty("仓库id")
    private Long baseWarehouseId;
    private Long baseGoodsTemplateId;
consum-base/src/main/java/com/consum/base/pojo/query/FormProcureQry.java
@@ -1,8 +1,8 @@
package com.consum.base.pojo.query;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
@@ -11,64 +11,64 @@
 * @description: 采购查询条件
 * @date 2023/11/10 16:51
 */
@ApiModel(value = "采购查询条件")
// @ApiModel(value = "采购查询条件")
@Data
public class FormProcureQry {
    @ApiModelProperty(value = "主键")
    // @ApiModelProperty(value = "主键")
    private Long id;
    /**
     * 仓库编号
     */
    @ApiModelProperty(value = "仓库编号")
    // @ApiModelProperty(value = "仓库编号")
    private Long warehouseId;
    /**
     * 入库单号
     */
    @ApiModelProperty(value = "入库单号")
    // @ApiModelProperty(value = "入库单号")
    private String businessFormCode;
    /**
     * 物品模版名称
     */
    @ApiModelProperty(value = "物品模版名称")
    // @ApiModelProperty(value = "物品模版名称")
    private String goodsTemplateName;
    /**
     * 机构
     */
    @ApiModelProperty(value = "机构")
    // @ApiModelProperty(value = "机构")
    private Long agencyId;
    /**
     * 创建人
     */
    @ApiModelProperty(value = "创建人")
    // @ApiModelProperty(value = "创建人")
    private String createName;
    /**
     * 1=待入库;2=已入库
     */
    @ApiModelProperty(value = "1=待入库;2=已入库")
    // @ApiModelProperty(value = "1=待入库;2=已入库")
    private Integer states;
    /**
     * 入库时间 开始
     */
    @ApiModelProperty(value = "入库时间 开始")
    // @ApiModelProperty(value = "入库时间 开始")
    @JsonFormat(pattern = "yyyyMMdd", timezone = "GMT+8")
    private Long startTime;
    /**
     * 入库时间 结束
     */
    @ApiModelProperty(value = "入库时间 结束")
    // @ApiModelProperty(value = "入库时间 结束")
    @JsonFormat(pattern = "yyyyMMdd", timezone = "GMT+8")
    private Long endTime;
    /**
     * 规格型号id
     */
    @ApiModelProperty(value = "规格型号id")
    // @ApiModelProperty(value = "规格型号id")
    private Long baseGoodsModelsId;
    @ApiModelProperty(value = "每页显示条数")
    // @ApiModelProperty(value = "每页显示条数")
    private Integer pageSize = 10;
    @ApiModelProperty(value = "当前页数")
    // @ApiModelProperty(value = "当前页数")
    private Integer pageNum = 1;
}
consum-base/src/main/java/com/consum/base/pojo/query/LDeptFormScrappedQry.java
@@ -1,6 +1,5 @@
package com.consum.base.pojo.query;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
@@ -42,10 +41,10 @@
     */
    private String agencyId;
    @ApiModelProperty("规格型号")
    // @ApiModelProperty("规格型号")
    private Long baseGoodsModelsId;
    @ApiModelProperty("部门ID")
    // @ApiModelProperty("部门ID")
    private Long departmentId;
    private Integer pageSize = 10;
consum-base/src/main/java/com/consum/base/pojo/query/LWhFormInventoryQry.java
@@ -1,10 +1,9 @@
package com.consum.base.pojo.query;
import io.swagger.annotations.ApiModel;
import lombok.Data;
@Data
@ApiModel(value = "盘点单条件参数")
// @ApiModel(value = "盘点单条件参数")
public class LWhFormInventoryQry {
    private Long id;
consum-base/src/main/java/com/consum/base/pojo/query/LWhFormOutputQry.java
@@ -1,7 +1,5 @@
package com.consum.base.pojo.query;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
@@ -10,51 +8,51 @@
 * @Date 2023/10/27
 */
@Data
@ApiModel(value = "出库单参数")
// @ApiModel(value = "出库单参数")
public class LWhFormOutputQry {
    @ApiModelProperty(value = "主键")
    // @ApiModelProperty(value = "主键")
    private Long id;
    /**
     * 仓库编号
     */
    @ApiModelProperty(value = "仓库编号")
    // @ApiModelProperty(value = "仓库编号")
    private Long warehouseId;
    /**
     * 入库单号
     */
    @ApiModelProperty(value = "入库单号")
    // @ApiModelProperty(value = "入库单号")
    private String businessFormCode;
    /**
     * 物品模版名称
     */
    @ApiModelProperty(value = "物品模版名称")
    // @ApiModelProperty(value = "物品模版名称")
    private String goodsTempName;
    /**
     * 物品模版名称
     */
    @ApiModelProperty(value = "物品模版id")
    // @ApiModelProperty(value = "物品模版id")
    private String goodsTemplateId;
    /**
     * 物品模版名称
     */
    @ApiModelProperty(value = "物品模版名称")
    // @ApiModelProperty(value = "物品模版名称")
    private String goodsName;
    /**
     * 物品模版名称
     */
    @ApiModelProperty(value = "物品模版id")
    // @ApiModelProperty(value = "物品模版id")
    private String baseGoodsId;
    /**
     * 机构
     */
    @ApiModelProperty(value = "机构")
    // @ApiModelProperty(value = "机构")
    private Long agencyId;
    /**
     * 创建人
     */
    @ApiModelProperty(value = "创建人")
    // @ApiModelProperty(value = "创建人")
    private String createName;
    /**
     * 采购入库 map.put("1" + "1", "1")
@@ -65,31 +63,31 @@
     * 退还入库 map.put("4" + "1", "6")
     * 申领出库 map.put("4" + "2", "7")
     */
    @ApiModelProperty(value = "1采购入库 2退还入库 3调拨入库 4盘盈入库 5申领出库 6调拨出库 7盘亏出库 8报废出库 9其他出库")
    // @ApiModelProperty(value = "1采购入库 2退还入库 3调拨入库 4盘盈入库 5申领出库 6调拨出库 7盘亏出库 8报废出库 9其他出库")
    private Integer flowType;
    @ApiModelProperty(value = "状态")
    // @ApiModelProperty(value = "状态")
    private Integer states;
    /**
     * 入库时间 开始
     */
    @ApiModelProperty(value = "开始时间")
    // @ApiModelProperty(value = "开始时间")
    private Long startTime;
    /**
     * 入库时间 结束
     */
    @ApiModelProperty(value = "结束时间")
    // @ApiModelProperty(value = "结束时间")
    private Long endTime;
    /**
     * 规格型号id
     */
    @ApiModelProperty(value = "规格型号id")
    // @ApiModelProperty(value = "规格型号id")
    private Long baseGoodsModelsId;
    @ApiModelProperty(value = "每页显示条数")
    // @ApiModelProperty(value = "每页显示条数")
    private Integer pageSize = 10;
    @ApiModelProperty(value = "当前页数")
    // @ApiModelProperty(value = "当前页数")
    private Integer pageNum = 1;
}
consum-base/src/main/java/com/consum/base/pojo/query/LWhFormScrappedQry.java
@@ -1,6 +1,6 @@
package com.consum.base.pojo.query;
import io.swagger.annotations.ApiModel;
import lombok.Data;
/**
@@ -10,7 +10,7 @@
 * @date 2023/11/21 15:00
 */
@Data
@ApiModel(value = "LWhFormScrappedQry", description = "报废单查询条件")
// @ApiModel(value = "LWhFormScrappedQry", description = "报废单查询条件")
public class LWhFormScrappedQry {
    /**
consum-base/src/main/java/com/consum/base/pojo/query/LWhGoodsStatisQry.java
@@ -1,7 +1,7 @@
package com.consum.base.pojo.query;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
@@ -11,35 +11,35 @@
 * @Description
 * @Version 1.0
 **/
@ApiModel(value = "物品统计查询参数")
// @ApiModel(value = "物品统计查询参数")
@Data
public class LWhGoodsStatisQry {
    @ApiModelProperty(value = "机构")
    // @ApiModelProperty(value = "机构")
    private Long agencyId;
    @ApiModelProperty(value = "部门id")
    // @ApiModelProperty(value = "部门id")
    private Long departmentId;
    @ApiModelProperty("仓库id")
    // @ApiModelProperty("仓库id")
    private Long baseWarehouseId;
    @ApiModelProperty(value = "物品名称")
    // @ApiModelProperty(value = "物品名称")
    private String goodsTemplateName;
    private Long goodsTemplateId;
    @ApiModelProperty(value = "规格型号")
    // @ApiModelProperty(value = "规格型号")
    private String baseGoodsModelsName;
    @ApiModelProperty("规格型号")
    // @ApiModelProperty("规格型号")
    private Long baseGoodsModelsId;
    @ApiModelProperty("价值类型")
    // @ApiModelProperty("价值类型")
    private Short costType;
    @ApiModelProperty(value = "操作时间")
    // @ApiModelProperty(value = "操作时间")
    private Long dealTimeStart;
    @ApiModelProperty(value = "操作时间")
    // @ApiModelProperty(value = "操作时间")
    private Long dealTimeEnd;
}
consum-base/src/main/java/com/consum/base/pojo/query/LWhLedgerQry.java
@@ -1,8 +1,6 @@
package com.consum.base.pojo.query;
import com.walker.web.param.ParamRequest;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.util.List;
@@ -15,38 +13,38 @@
 * @Version 1.0
 **/
@Data
@ApiModel(value = "机构台账参数")
// @ApiModel(value = "机构台账参数")
public class LWhLedgerQry extends ParamRequest {
    @ApiModelProperty("机构")
    // @ApiModelProperty("机构")
    private Long agencyId;
    @ApiModelProperty("部门ID")
    // @ApiModelProperty("部门ID")
    private Long departmentId;
    @ApiModelProperty("仓库类型")
    // @ApiModelProperty("仓库类型")
    private Short warehouseType;
    @ApiModelProperty("仓库id")
    // @ApiModelProperty("仓库id")
    private Long warehouseId;
    @ApiModelProperty("物品名称")
    // @ApiModelProperty("物品名称")
    private String goodsTemplateName;
    private Long goodsTemplateId;
    @ApiModelProperty("分类")
    // @ApiModelProperty("分类")
    private Long categoryId;
    @ApiModelProperty("价值类型")
    // @ApiModelProperty("价值类型")
    private Short costType;
    @ApiModelProperty("状态(0=在途调拨;1=入库未分发;2=已下发;3=报废 4 零星出库)")
    // @ApiModelProperty("状态(0=在途调拨;1=入库未分发;2=已下发;3=报废 4 零星出库)")
    private Short states;
    private List<Short> statesList;
    @ApiModelProperty(value = "每页显示条数")
    // @ApiModelProperty(value = "每页显示条数")
    private Integer pageSize = 10;
    @ApiModelProperty(value = "当前页数")
    // @ApiModelProperty(value = "当前页数")
    private Integer pageNum = 1;
}
consum-base/src/main/java/com/consum/base/pojo/query/LWhProcureModelQry.java
@@ -1,8 +1,8 @@
package com.consum.base.pojo.query;
import com.walker.web.param.ParamRequest;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
@@ -13,16 +13,16 @@
 * @Version 1.0
 **/
@Data
@ApiModel(value = "参数")
// @ApiModel(value = "参数")
public class LWhProcureModelQry extends ParamRequest {
    @ApiModelProperty("物品名称")
    // @ApiModelProperty("物品名称")
    private String goodsTemplateName;
    private Long baseGoodsTemplateId;
    @ApiModelProperty("部门ID")
    // @ApiModelProperty("部门ID")
    private Long departmentId;
    @ApiModelProperty("分发单ID")
    // @ApiModelProperty("分发单ID")
    private Long transBusinessId;
}
consum-base/src/main/java/com/consum/base/pojo/query/TransferQry.java
@@ -1,7 +1,7 @@
package com.consum.base.pojo.query;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
@@ -10,88 +10,88 @@
 * @description: 调拨单查询条件
 * @date 2023/11/6 11:47
 */
@ApiModel(value = "调拨单查询条件")
// @ApiModel(value = "调拨单查询条件")
@Data
public class TransferQry {
    /**
     * 调拨单号/分发单号
     */
    @ApiModelProperty(value = "调拨单号/分发单号")
    // @ApiModelProperty(value = "调拨单号/分发单号")
    private String businessFormCode;
    /**
     * 物品模版名称
     */
    @ApiModelProperty(value = "物品模版名称")
    // @ApiModelProperty(value = "物品模版名称")
    private String goodsTemplateName;
    /**
     * 物品模版id
     */
    @ApiModelProperty(value = "物品模版id")
    // @ApiModelProperty(value = "物品模版id")
    private Long goodsTemplateId;
    /**
     * 出库仓库机构编号(调拨机构)
     */
    @ApiModelProperty(value = "出库仓库机构编号(调拨机构)")
    // @ApiModelProperty(value = "出库仓库机构编号(调拨机构)")
    private Long outAgencyId;
    /**
     * 接收机构
     */
    @ApiModelProperty(value = "接收机构")
    // @ApiModelProperty(value = "接收机构")
    private Long inAgencyId;
    /**
     * 状态 0=待出库;1=待接收;2=已入库库:4=已撤销
     */
    @ApiModelProperty(value = "状态 0=待出库;1=待接收;2=已入库库:4=已撤销")
    // @ApiModelProperty(value = "状态 0=待出库;1=待接收;2=已入库库:4=已撤销")
    private Short states;
    /**
     * 创建人/分发人
     */
    @ApiModelProperty(value = "创建人/分发人")
    // @ApiModelProperty(value = "创建人/分发人")
    private String operatorName;
    /**
     * 申请时间 开始
     */
    @ApiModelProperty(value = "申请时间 开始")
    // @ApiModelProperty(value = "申请时间 开始")
    private Long createTimeStart;
    /**
     * 申请时间 结束
     */
    @ApiModelProperty(value = "申请时间 结束")
    // @ApiModelProperty(value = "申请时间 结束")
    private Long createTimeEnd;
    /**
     * 接收时间 开始
     */
    @ApiModelProperty(value = "接收时间 开始")
    // @ApiModelProperty(value = "接收时间 开始")
    private Long startTime;
    /**
     * 接收时间 结束
     */
    @ApiModelProperty(value = "接收时间 结束")
    // @ApiModelProperty(value = "接收时间 结束")
    private Long endTime;
    /**
     * 规格型号id
     */
    @ApiModelProperty(value = "规格型号id")
    // @ApiModelProperty(value = "规格型号id")
    private Long baseGoodsModelsId;
    /**
     * 页码
     */
    @ApiModelProperty(value = "页码")
    // @ApiModelProperty(value = "页码")
    private Integer pageNum;
    /**
     * 页大小
     */
    @ApiModelProperty(value = "页大小")
    // @ApiModelProperty(value = "页大小")
    private Integer pageSize;
    @ApiModelProperty(value = "查询类型")
    // @ApiModelProperty(value = "查询类型")
    private Integer qryType;
    @ApiModelProperty(value = "分发部门,部门分发时仓库id就是分发部门")
    // @ApiModelProperty(value = "分发部门,部门分发时仓库id就是分发部门")
    private Long departmentId;
}
consum-base/src/main/java/com/consum/base/pojo/query/UsingFormBackQry.java
@@ -2,8 +2,8 @@
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
@@ -13,67 +13,67 @@
 * @date 2023/12/1 15:32
 */
@Data
@ApiModel
// @ApiModel
@JsonIgnoreProperties(ignoreUnknown = true)
public class UsingFormBackQry {
    @ApiModelProperty(value = "主键")
    // @ApiModelProperty(value = "主键")
    private Long id;
    /**
     * 退回单号
     */
    @ApiModelProperty(value = "退回单号")
    // @ApiModelProperty(value = "退回单号")
    private String businessFormCode;
    /**
     * 分发部门id
     */
    @ApiModelProperty(value = "分发部门id")
    // @ApiModelProperty(value = "分发部门id")
    private Long departmentId;
    /**
     * 物品模版名称
     */
    @ApiModelProperty(value = "物品模版名称")
    // @ApiModelProperty(value = "物品模版名称")
    private String goodsTemplateName;
    /**
     * 物品模版id
     */
    @ApiModelProperty(value = "物品模版id")
    // @ApiModelProperty(value = "物品模版id")
    private String goodsTemplateId;
    /**
     * 开始时间
     */
    @ApiModelProperty(value = "开始时间")
    // @ApiModelProperty(value = "开始时间")
    private Long startTime;
    /**
     * 结束时间
     */
    @ApiModelProperty(value = "结束时间")
    // @ApiModelProperty(value = "结束时间")
    private Long endTime;
    /**
     * 规格型号id
     */
    @ApiModelProperty(value = "规格型号id")
    // @ApiModelProperty(value = "规格型号id")
    private Long baseGoodsModelsId;
    /**
     * 机构
     */
    @ApiModelProperty(value = "机构")
    // @ApiModelProperty(value = "机构")
    private Long agencyId;
    /**
     * 创建人
     */
    @ApiModelProperty(value = "创建人")
    // @ApiModelProperty(value = "创建人")
    private String createName;
    /**
     * 页码
     */
    @ApiModelProperty(value = "页码")
    // @ApiModelProperty(value = "页码")
    private Integer pageNum;
    /**
     * 页大小
     */
    @ApiModelProperty(value = "页大小")
    // @ApiModelProperty(value = "页大小")
    private Integer pageSize;
}
consum-base/src/main/java/com/consum/base/pojo/query/WarehouseQry.java
@@ -1,7 +1,7 @@
package com.consum.base.pojo.query;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import javax.validation.constraints.NotNull;
import lombok.Data;
@@ -12,24 +12,24 @@
 * @date 2023/11/16 10:18
 */
@Data
@ApiModel(value = "库存查询条件")
// @ApiModel(value = "库存查询条件")
public class WarehouseQry {
    @ApiModelProperty(value = "仓库id")
    // @ApiModelProperty(value = "仓库id")
    private Long warehouseId;
    @ApiModelProperty(value = "商品型号id")
    // @ApiModelProperty(value = "商品型号id")
    @NotNull(message = "商品型号id不能为空")
    private Long baseGoodsModelsId;
    //仓库类型 0机构1部门
    @ApiModelProperty(value = "仓库类型 0机构1部门")
    // @ApiModelProperty(value = "仓库类型 0机构1部门")
    private Integer warehouseType;
    //(0=在途调拨;1=入库未分发;2=已下发;3=报废)
    @ApiModelProperty(value = "(0=在途调拨;1=入库未分发;2=已下发;3=报废)")
    // @ApiModelProperty(value = "(0=在途调拨;1=入库未分发;2=已下发;3=报废)")
    private Integer states;
    //采购方式(1:集采;2=自采)
    @ApiModelProperty(value = "采购方式(1:集采;2=自采)")
    // @ApiModelProperty(value = "采购方式(1:集采;2=自采)")
    private Integer buyType;
    @ApiModelProperty(value = "机构id")
    // @ApiModelProperty(value = "机构id")
    private Long agencyId;
}
consum-base/src/main/java/com/consum/base/pojo/query/WhWarningConfigQry.java
@@ -1,8 +1,8 @@
package com.consum.base.pojo.query;
import com.walker.web.param.ParamRequest;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
@@ -12,7 +12,7 @@
 * @Description
 * @Version 1.0
 **/
@ApiModel(value = "预警设置查询参数")
// @ApiModel(value = "预警设置查询参数")
@Data
public class WhWarningConfigQry extends ParamRequest {
    // 主键
@@ -20,26 +20,26 @@
    private Long baseGoodsTemplateId;
    private Long baseGoodsModelsId;
    private Integer goodsType;
    @ApiModelProperty("机构")
    // @ApiModelProperty("机构")
    private Long agencyId;
    @ApiModelProperty("仓库类型")
    // @ApiModelProperty("仓库类型")
    private Long warehouseType;
    @ApiModelProperty("仓库id")
    // @ApiModelProperty("仓库id")
    private Long baseWarehouseId;
    @ApiModelProperty("物品名称")
    // @ApiModelProperty("物品名称")
    private String goodsTemplateName;
    @ApiModelProperty("分类")
    // @ApiModelProperty("分类")
    private Long categoryId;
    @ApiModelProperty("价值类型")
    // @ApiModelProperty("价值类型")
    private Short costType;
    @ApiModelProperty(value = "每页显示条数")
    // @ApiModelProperty(value = "每页显示条数")
    private Integer pageSize = 10;
    @ApiModelProperty(value = "当前页数")
    // @ApiModelProperty(value = "当前页数")
    private Integer pageNum = 1;
}
consum-base/src/main/java/com/consum/base/pojo/query/WhWarningQry.java
@@ -1,8 +1,8 @@
package com.consum.base.pojo.query;
import com.walker.web.param.ParamRequest;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
@@ -12,40 +12,40 @@
 * @Description
 * @Version 1.0
 **/
@ApiModel(value = "预警设置查询参数")
// @ApiModel(value = "预警设置查询参数")
@Data
public class WhWarningQry extends ParamRequest {
    @ApiModelProperty("物品名称")
    // @ApiModelProperty("物品名称")
    private String goodsTemplateName;
    private String goodsTemplateId;
    @ApiModelProperty("规格型号")
    // @ApiModelProperty("规格型号")
    private Long baseGoodsModelsId;
    @ApiModelProperty("分类")
    // @ApiModelProperty("分类")
    private Long categoryId;
    @ApiModelProperty("价值类型")
    // @ApiModelProperty("价值类型")
    private Short costType;
    @ApiModelProperty("机构")
    // @ApiModelProperty("机构")
    private Long agencyId;
    @ApiModelProperty("仓库类型")
    // @ApiModelProperty("仓库类型")
    private Short warehouseType;
    @ApiModelProperty("仓库id")
    // @ApiModelProperty("仓库id")
    private Long baseWarehouseId;
    @ApiModelProperty("状态(1=未查看;2=已查看)")
    // @ApiModelProperty("状态(1=未查看;2=已查看)")
    private Integer states;
    @ApiModelProperty("预警类型(1=超上限;2=超下限)")
    // @ApiModelProperty("预警类型(1=超上限;2=超下限)")
    private Short warningType;
    @ApiModelProperty(value = "每页显示条数")
    // @ApiModelProperty(value = "每页显示条数")
    private Integer pageSize = 10;
    @ApiModelProperty(value = "当前页数")
    // @ApiModelProperty(value = "当前页数")
    private Integer pageNum = 1;
}
consum-base/src/main/java/com/consum/base/pojo/request/FormInventoryParam.java
@@ -1,7 +1,5 @@
package com.consum.base.pojo.request;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
@@ -12,34 +10,34 @@
 * @date 2023/11/20 13:49
 */
@Data
@ApiModel(value = "新增盘点单信息")
// @ApiModel(value = "新增盘点单信息")
public class FormInventoryParam {
    private Long id;
    /**
     * 盘点单名
     */
    @ApiModelProperty(value = "盘点单名")
    // @ApiModelProperty(value = "盘点单名")
    private String businessFormName;
    /**
     * 仓库编号
     */
    @ApiModelProperty(value = "仓库编号")
    // @ApiModelProperty(value = "仓库编号")
    private Long warehouseId;
    /**
     * 操作人
     */
    @ApiModelProperty(value = "操作人")
    // @ApiModelProperty(value = "操作人")
    private Long operatorUserId;
    /**
     * 监盘人
     */
    @ApiModelProperty(value = "监盘人")
    // @ApiModelProperty(value = "监盘人")
    private Long monitorUserId;
    /**
     * 备注
     */
    @ApiModelProperty(value = "备注")
    // @ApiModelProperty(value = "备注")
    private String remark;
consum-base/src/main/java/com/consum/base/pojo/request/LWhFormInventoryParam.java
@@ -1,12 +1,11 @@
package com.consum.base.pojo.request;
import com.consum.base.pojo.response.FormInventoryGoodsVO;
import io.swagger.annotations.ApiModel;
import java.util.List;
import lombok.Data;
@Data
@ApiModel(value = "盘点单")
// @ApiModel(value = "盘点单")
public class LWhFormInventoryParam {
    // 主键
consum-base/src/main/java/com/consum/base/pojo/request/LWhFormTransferParam.java
@@ -1,8 +1,8 @@
package com.consum.base.pojo.request;
import com.consum.base.pojo.LWhFormTransferGoodsInfoParam;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import java.util.List;
import lombok.AllArgsConstructor;
import lombok.Data;
@@ -13,55 +13,55 @@
 * @Author 卢庆阳
 * @Date 2023/10/30
 */
@ApiModel(value = "调拨单据新增信息")
// @ApiModel(value = "调拨单据新增信息")
@Data
@AllArgsConstructor
@NoArgsConstructor
public class LWhFormTransferParam {
    @ApiModelProperty(value = "主键")
    // @ApiModelProperty(value = "主键")
    private Long id;
    /**
     * 入库仓库编号
     */
    @ApiModelProperty(value = "入库仓库编号")
    // @ApiModelProperty(value = "入库仓库编号")
    private Long inWarehouseId;
    /**
     * 出库仓库机构编号(调拨机构)
     */
    @ApiModelProperty(value = "出库仓库机构编号(调拨机构)")
    // @ApiModelProperty(value = "出库仓库机构编号(调拨机构)")
    private Long outAgencyId;
    /**
     * 调拨时间
     */
    @ApiModelProperty(value = "调拨时间")
    // @ApiModelProperty(value = "调拨时间")
    private Long createTime;
    /**
     * 调拨手续
     */
    @ApiModelProperty(value = "调拨手续")
    // @ApiModelProperty(value = "调拨手续")
    private String procureDoc;
    /**
     * 单据类型。0仓库调拨;1部门分发;2部门物品回退
     */
    @ApiModelProperty(value = "单据类型。0仓库调拨;1部门分发;2部门物品回退")
    // @ApiModelProperty(value = "单据类型。0仓库调拨;1部门分发;2部门物品回退")
    private Integer transferBusinessType;
    @ApiModelProperty(value = "部门id")
    // @ApiModelProperty(value = "部门id")
    private Long departmentId;
    @ApiModelProperty(value = "部门名字")
    // @ApiModelProperty(value = "部门名字")
    private String departmentName;
    @ApiModelProperty(value = "领取人名字")
    // @ApiModelProperty(value = "领取人名字")
    private String operatorName;
    @ApiModelProperty(value = "联系电话")
    // @ApiModelProperty(value = "联系电话")
    private Long tel;
    /**
     * 调拨单型号
     */
    @ApiModelProperty(value = "调拨单型号")
    // @ApiModelProperty(value = "调拨单型号")
    private List<LWhFormTransferGoodsInfoParam> transferGoods;
consum-base/src/main/java/com/consum/base/pojo/request/RecordInfoParam.java
@@ -1,6 +1,6 @@
package com.consum.base.pojo.request;
import io.swagger.annotations.ApiModelProperty;
import java.util.List;
import lombok.Data;
@@ -13,7 +13,7 @@
@Data
public class RecordInfoParam {
    @ApiModelProperty("使用人修改记录")
    // @ApiModelProperty("使用人修改记录")
    List<ProcureModelInfoParam> recordInfoList;
}
consum-base/src/main/java/com/consum/base/pojo/request/UsingFormBackGoodsInfo.java
@@ -2,7 +2,7 @@
import java.util.List;
import io.swagger.annotations.ApiModel;
import lombok.Data;
/**
@@ -12,7 +12,7 @@
 * @date 2023/12/1 13:16
 */
@Data
@ApiModel
// @ApiModel
public class UsingFormBackGoodsInfo {
    // 分类编号
consum-base/src/main/java/com/consum/base/pojo/request/UsingFormBackParam.java
@@ -4,8 +4,8 @@
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.NoArgsConstructor;
@@ -17,28 +17,28 @@
 */
@NoArgsConstructor
@Data
@ApiModel
// @ApiModel
@JsonIgnoreProperties(ignoreUnknown = true)
public class UsingFormBackParam {
    @ApiModelProperty(value = "物品退回部门id")
    // @ApiModelProperty(value = "物品退回部门id")
    private Long departmentId;
    @ApiModelProperty(value = "物品退回部门名字")
    // @ApiModelProperty(value = "物品退回部门名字")
    private String departmentName;
    @ApiModelProperty(value = "退回人id")
    // @ApiModelProperty(value = "退回人id")
    private Long operatorId;
    @ApiModelProperty(value = "退回人名字")
    // @ApiModelProperty(value = "退回人名字")
    private String operatorName;
    @ApiModelProperty(value = "物品退回时间")
    // @ApiModelProperty(value = "物品退回时间")
    private Long dealTime;
    @ApiModelProperty(value = "手续")
    // @ApiModelProperty(value = "手续")
    private String procureDoc;
    @ApiModelProperty(value = "分发物品信息")
    // @ApiModelProperty(value = "分发物品信息")
    private List<UsingFormBackGoodsInfo> goods;
}
consum-base/src/main/java/com/consum/base/pojo/request/WarehouseManagerParam.java
@@ -1,8 +1,8 @@
package com.consum.base.pojo.request;
import com.consum.base.pojo.WarehouseManagerInfo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
//
//
import java.util.List;
import lombok.Data;
@@ -13,13 +13,13 @@
 * @date 2023/11/17 13:05
 */
@Data
@ApiModel(value = "库管员信息")
// @ApiModel(value = "库管员信息")
public class WarehouseManagerParam {
    @ApiModelProperty(value = "仓库id")
    // // @ApiModelProperty(value = "仓库id")
    private Long warehouseId;
    @ApiModelProperty(value = "库管员信息")
    // @ApiModelProperty(value = "库管员信息")
    List<WarehouseManagerInfo> warehouseManagerInfoList;
}
consum-base/src/main/java/com/consum/base/pojo/request/baseGoodModel.java
@@ -1,7 +1,7 @@
package com.consum.base.pojo.request;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
@@ -11,14 +11,14 @@
 * @date 2023/12/1 13:21
 */
@Data
@ApiModel
// @ApiModel
public class baseGoodModel {
    @ApiModelProperty(value = "商品模型id")
    // @ApiModelProperty(value = "商品模型id")
    private Long baseGoodsModelsId;
    @ApiModelProperty(value = "单位")
    // @ApiModelProperty(value = "单位")
    private String unit;
    @ApiModelProperty(value = "物品类型 A,B,C")
    // @ApiModelProperty(value = "物品类型 A,B,C")
    private String classification;
    private Long goodsTemplatesId;
consum-base/src/main/java/com/consum/base/pojo/response/DepartGoodsUseInfo.java
@@ -1,6 +1,6 @@
package com.consum.base.pojo.response;
import io.swagger.annotations.ApiModel;
import lombok.Data;
/**
@@ -10,7 +10,7 @@
 * @date 2023/11/23 15:30
 */
@Data
@ApiModel
// @ApiModel
public class DepartGoodsUseInfo {
    private Long id;
consum-base/src/main/java/com/consum/base/pojo/response/FinSysTenantUserVO.java
@@ -1,7 +1,5 @@
package com.consum.base.pojo.response;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
@@ -11,32 +9,32 @@
 * @date 2023/11/16 15:46
 */
@Data
@ApiModel(value = "FinSysTenantUserVO", description = "机构用户信息")
// // @ApiModel(value = "FinSysTenantUserVO", description = "机构用户信息")
public class FinSysTenantUserVO {
    // 主键
    @ApiModelProperty(value = "主键")
    // // @ApiModelProperty(value = "主键")
    private Long id;
    @ApiModelProperty(value = "用户编码")
    // // @ApiModelProperty(value = "用户编码")
    private String userCode;
    @ApiModelProperty(value = "用户姓名")
    // // @ApiModelProperty(value = "用户姓名")
    private String userName;
    @ApiModelProperty(value = "用户登录名")
    // // @ApiModelProperty(value = "用户登录名")
    private String userPhone;
    @ApiModelProperty(value = "机构id")
    // // @ApiModelProperty(value = "机构id")
    private String tenantId;
    @ApiModelProperty(value = "机构名称")
    // // @ApiModelProperty(value = "机构名称")
    private String tenantName;
    @ApiModelProperty(value = "部门名称")
    // // @ApiModelProperty(value = "部门名称")
    private String sysDeptName;
    @ApiModelProperty(value = "状态 (0禁用1启用)")
    // // @ApiModelProperty(value = "状态 (0禁用1启用)")
    private Integer status;
}
consum-base/src/main/java/com/consum/base/pojo/response/FormInventoryDetailVO.java
@@ -1,6 +1,5 @@
package com.consum.base.pojo.response;
import io.swagger.annotations.ApiModel;
import lombok.Data;
/**
@@ -10,7 +9,7 @@
 * @date 2023/11/21 13:20
 */
@Data
@ApiModel(value = "盘点异常明细信息")
// @ApiModel(value = "盘点异常明细信息")
public class FormInventoryDetailVO {
    private Long id;
consum-base/src/main/java/com/consum/base/pojo/response/FormInventoryGoodsVO.java
@@ -1,7 +1,5 @@
package com.consum.base.pojo.response;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
@@ -11,11 +9,11 @@
 * @date 2023/11/20 14:45
 */
@Data
@ApiModel(value = "盘点物品信息")
// @ApiModel(value = "盘点物品信息")
public class FormInventoryGoodsVO {
    // 主键
    @ApiModelProperty(value = "id")
    // @ApiModelProperty(value = "id")
    private Long id;
    private Long whFormInventoryId;
@@ -26,31 +24,31 @@
    private Long baseGoodsTemplateId;
    @ApiModelProperty(value = "商品模板名称")
    // @ApiModelProperty(value = "商品模板名称")
    private String goodsTemplateName;
    @ApiModelProperty(value = "商品型号名称")
    // @ApiModelProperty(value = "商品型号名称")
    private String baseGoodsModelsName;
    @ApiModelProperty(value = "商品类别")
    // @ApiModelProperty(value = "商品类别")
    private String type;
    @ApiModelProperty(value = "单位")
    // @ApiModelProperty(value = "单位")
    private String unit;
    @ApiModelProperty(value = "库存数量")
    // @ApiModelProperty(value = "库存数量")
    private Integer inventoryCount;
    @ApiModelProperty(value = "单价")
    // @ApiModelProperty(value = "单价")
    private Integer price;
    @ApiModelProperty(value = "总价")
    // @ApiModelProperty(value = "总价")
    private Long totalAmount;
    @ApiModelProperty(value = "实盘数量")
    // @ApiModelProperty(value = "实盘数量")
    private Integer realNum;
    @ApiModelProperty(value = "状态,")
    // @ApiModelProperty(value = "状态,")
    private Integer status;
consum-base/src/main/java/com/consum/base/pojo/response/FormInventoryVO.java
@@ -1,7 +1,5 @@
package com.consum.base.pojo.response;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import java.util.List;
import lombok.Data;
@@ -12,34 +10,34 @@
 * @date 2023/11/20 14:43
 */
@Data
@ApiModel(value = "盘点物品单详细信息")
// @ApiModel(value = "盘点物品单详细信息")
public class FormInventoryVO {
    /**
     * 盘点单号
     */
    @ApiModelProperty(value = "盘点单号")
    // @ApiModelProperty(value = "盘点单号")
    private String businessFormCode;
    /**
     * 盘点单名
     */
    @ApiModelProperty(value = "盘点单名")
    // @ApiModelProperty(value = "盘点单名")
    private String businessFormName;
    /**
     * 仓库编号
     */
    @ApiModelProperty(value = "仓库编号")
    // @ApiModelProperty(value = "仓库编号")
    private Long warehouseId;
    /**
     * 仓库名字
     */
    @ApiModelProperty(value = "仓库名字")
    // @ApiModelProperty(value = "仓库名字")
    private String warehouseName;
    /**
     * 盘点物品信息
     */
    @ApiModelProperty(value = "盘点物品信息")
    // @ApiModelProperty(value = "盘点物品信息")
    private List<FormInventoryGoodsVO> formInventoryGoodsList;
}
consum-base/src/main/java/com/consum/base/pojo/response/FormOutputVO.java
@@ -1,6 +1,6 @@
package com.consum.base.pojo.response;
import io.swagger.annotations.ApiModel;
import java.util.List;
import lombok.Data;
@@ -10,7 +10,7 @@
 * @description: 出库列表信息
 * @date 2023/11/10 11:37
 */
@ApiModel(value = "出库列表信息")
// @ApiModel(value = "出库列表信息")
@Data
public class FormOutputVO {
consum-base/src/main/java/com/consum/base/pojo/response/FormProcureVO.java
@@ -2,7 +2,7 @@
import java.util.List;
import io.swagger.annotations.ApiModel;
import lombok.Data;
/**
@@ -11,7 +11,7 @@
 * @description: 采购列表明细信息
 * @date 2023/11/10 11:37
 */
@ApiModel(value = "采购列表明细信息")
// @ApiModel(value = "采购列表明细信息")
@Data
public class FormProcureVO {
consum-base/src/main/java/com/consum/base/pojo/response/FormScrappedGoodsDetailVO.java
@@ -1,7 +1,7 @@
package com.consum.base.pojo.response;
import com.fasterxml.jackson.annotation.JsonProperty;
import io.swagger.annotations.ApiModel;
import lombok.Data;
import lombok.NoArgsConstructor;
@@ -13,7 +13,7 @@
 */
@NoArgsConstructor
@Data
@ApiModel(value = "FormScrappedGoodsDetailVO")
// @ApiModel(value = "FormScrappedGoodsDetailVO")
public class FormScrappedGoodsDetailVO {
    @JsonProperty("id")
consum-base/src/main/java/com/consum/base/pojo/response/FormTransferVO.java
@@ -2,7 +2,7 @@
import java.util.List;
import io.swagger.annotations.ApiModel;
import lombok.Data;
/**
@@ -12,7 +12,7 @@
 * @date 2023/11/15 17:27
 */
@Data
@ApiModel(value = "调拨单列表信息")
// @ApiModel(value = "调拨单列表信息")
public class FormTransferVO {
    // 主键
consum-base/src/main/java/com/consum/base/pojo/response/GoodsModelVO.java
@@ -2,7 +2,7 @@
import java.util.List;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
@@ -14,29 +14,29 @@
@Data
public class GoodsModelVO {
    @ApiModelProperty(value = "id")
    // @ApiModelProperty(value = "id")
    private Long id;
    @ApiModelProperty(value = "型号名称")
    // @ApiModelProperty(value = "型号名称")
    private String baseGoodsModelsName;
    @ApiModelProperty(value = "单位信息")
    // @ApiModelProperty(value = "单位信息")
    private String unit;
    @ApiModelProperty(value = "数量")
    // @ApiModelProperty(value = "数量")
    private Integer counts;
    @ApiModelProperty(value = "总金额")
    // @ApiModelProperty(value = "总金额")
    private Double totalAmount;
    /*采购信息*/
    @ApiModelProperty(value = "库存数量")
    // @ApiModelProperty(value = "库存数量")
    private Integer worehouseCount;
    @ApiModelProperty(value = "单价")
    // @ApiModelProperty(value = "单价")
    private Long price;
    private Long baseGoodsModelsId;
    /*报废信息*/
    private String scrappedName;
    @ApiModelProperty(value = "部门分发使用人信息")
    // @ApiModelProperty(value = "部门分发使用人信息")
    private List<DepartGoodsUseInfo> useInfo;
}
consum-base/src/main/java/com/consum/base/pojo/response/GoodsStatisticsInfoVO.java
@@ -1,7 +1,7 @@
package com.consum.base.pojo.response;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.NoArgsConstructor;
@@ -13,27 +13,27 @@
 */
@NoArgsConstructor
@Data
@ApiModel
// @ApiModel
public class GoodsStatisticsInfoVO {
    @ApiModelProperty(value = "机构")
    // @ApiModelProperty(value = "机构")
    private String orgName;
    @ApiModelProperty(value = "调拨数量")
    // @ApiModelProperty(value = "调拨数量")
    private Integer diaoBoNum;
    @ApiModelProperty(value = "总数量")
    // @ApiModelProperty(value = "总数量")
    private Integer totalNum;
    @ApiModelProperty(value = "在库数量")
    // @ApiModelProperty(value = "在库数量")
    private Integer zaiKuNum;
    @ApiModelProperty(value = "物品型号类别(A类,B类..)")
    // @ApiModelProperty(value = "物品型号类别(A类,B类..)")
    private String costType;
    @ApiModelProperty(value = "报废数量")
    // @ApiModelProperty(value = "报废数量")
    private Integer baoFeiNum;
    @ApiModelProperty(value = "物品型号名称")
    // @ApiModelProperty(value = "物品型号名称")
    private String baseGoodsModelsName;
    @ApiModelProperty(value = "物品编码")
    // @ApiModelProperty(value = "物品编码")
    private String goodsCode;
    @ApiModelProperty(value = "仓库/部门名称")
    // @ApiModelProperty(value = "仓库/部门名称")
    private String warehouseName;
    @ApiModelProperty(value = "物品模板名称")
    // @ApiModelProperty(value = "物品模板名称")
    private String goodsTemplateName;
}
consum-base/src/main/java/com/consum/base/pojo/response/GoodsTemplateCountVO.java
@@ -1,7 +1,7 @@
package com.consum.base.pojo.response;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
@@ -10,23 +10,23 @@
 * @description: 物品统计信息
 * @date 2023/11/10 11:37
 */
@ApiModel
// @ApiModel
@Data
public class GoodsTemplateCountVO {
    @ApiModelProperty(value = "id")
    // @ApiModelProperty(value = "id")
    private Long id;
    @ApiModelProperty(value = "物品模板名称")
    // @ApiModelProperty(value = "物品模板名称")
    private String goodsName;
    @ApiModelProperty(value = "物品模板名称")
    // @ApiModelProperty(value = "物品模板名称")
    private String goodsTemplateName;
    @ApiModelProperty(value = "物品模板名称")
    // @ApiModelProperty(value = "物品模板名称")
    private String baseGoodsTemplateName;
    @ApiModelProperty(value = "商品模板id")
    // @ApiModelProperty(value = "商品模板id")
    private Long baseGoodsTemplateId;
    @ApiModelProperty(value = "物品内全部型号统计数量")
    // @ApiModelProperty(value = "物品内全部型号统计数量")
    private Integer count;
    @ApiModelProperty(value = "单位")
    // @ApiModelProperty(value = "单位")
    private String unit;
}
consum-base/src/main/java/com/consum/base/pojo/response/GoodsTemplateInfoVO.java
@@ -2,7 +2,7 @@
import java.util.List;
import io.swagger.annotations.ApiModel;
import lombok.Data;
/**
@@ -12,7 +12,7 @@
 * @date 2023/11/14 13:13
 */
@Data
@ApiModel(description = "输出物品信息")
// @ApiModel(description = "输出物品信息")
public class GoodsTemplateInfoVO {
    // 主键
consum-base/src/main/java/com/consum/base/pojo/response/GoodsTemplateVO.java
@@ -1,6 +1,6 @@
package com.consum.base.pojo.response;
import io.swagger.annotations.ApiModel;
import lombok.Data;
/**
@@ -10,7 +10,7 @@
 * @date 2023/11/15 13:38
 */
@Data
@ApiModel(value = "物品模板信息")
// @ApiModel(value = "物品模板信息")
public class GoodsTemplateVO {
    private Long id;
consum-base/src/main/java/com/consum/base/pojo/response/LWHFromTransferExtendVO.java
@@ -2,8 +2,8 @@
import java.util.List;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
@@ -13,97 +13,97 @@
 * @date 2023/11/15 18:02
 */
@Data
@ApiModel(value = "调拨单扩展详细信息")
// @ApiModel(value = "调拨单扩展详细信息")
public class LWHFromTransferExtendVO {
    // 主键
    private Long id;
    @ApiModelProperty(value = "出入库id")
    // @ApiModelProperty(value = "出入库id")
    private Long inWarehouseFormId;
    @ApiModelProperty(value = "入库流水id")
    // @ApiModelProperty(value = "入库流水id")
    private Long inWarehouseFlowId;
    @ApiModelProperty(value = "出库流水id")
    // @ApiModelProperty(value = "出库流水id")
    private Long outWarehouseFlowId;
    @ApiModelProperty(value = "出库单id")
    // @ApiModelProperty(value = "出库单id")
    private Long outWarehouseFormId;
    @ApiModelProperty(value = "业务类型0仓库调拨;1部门分发;2部门物品回退")
    // @ApiModelProperty(value = "业务类型0仓库调拨;1部门分发;2部门物品回退")
    private Integer businessType;
    @ApiModelProperty(value = "业务单号")
    // @ApiModelProperty(value = "业务单号")
    private String businessFormCode;
    @ApiModelProperty(value = "入库单类型0机构1部门")
    // @ApiModelProperty(value = "入库单类型0机构1部门")
    private Integer inWarehouseType;
    @ApiModelProperty(value = "入库仓库id")
    // @ApiModelProperty(value = "入库仓库id")
    private Long inWarehouseId;
    @ApiModelProperty(value = "入库仓库名称")
    // @ApiModelProperty(value = "入库仓库名称")
    private String inWarehouseName;
    @ApiModelProperty(value = "出库单类型0机构1部门")
    // @ApiModelProperty(value = "出库单类型0机构1部门")
    private Integer outWarehouseType;
    @ApiModelProperty(value = "出库仓库id")
    // @ApiModelProperty(value = "出库仓库id")
    private Long outWarehouseId;
    @ApiModelProperty(value = "出库仓库名称")
    // @ApiModelProperty(value = "出库仓库名称")
    private String outWarehouseName;
    @ApiModelProperty(value = "入库机构id")
    // @ApiModelProperty(value = "入库机构id")
    private Long inAgencyId;
    @ApiModelProperty(value = "入库机构名称")
    // @ApiModelProperty(value = "入库机构名称")
    private String inAgencyName;
    @ApiModelProperty(value = "出库机构id")
    // @ApiModelProperty(value = "出库机构id")
    private Long outAgencyId;
    @ApiModelProperty(value = "出库机构名称")
    // @ApiModelProperty(value = "出库机构名称")
    private String outAgencyName;
    @ApiModelProperty(value = "操作人id")
    // @ApiModelProperty(value = "操作人id")
    private Long operatorId;
    @ApiModelProperty(value = "操作人名称")
    // @ApiModelProperty(value = "操作人名称")
    private String operatorName;
    @ApiModelProperty(value = "创建时间")
    // @ApiModelProperty(value = "创建时间")
    private Long createTime;
    @ApiModelProperty(value = "0=待出库;1=待接收;2=已入库库:4=已撤销")
    // @ApiModelProperty(value = "0=待出库;1=待接收;2=已入库库:4=已撤销")
    private Integer states;
    @ApiModelProperty(value = "入库操作人id")
    // @ApiModelProperty(value = "入库操作人id")
    private Long inOperatorId;
    @ApiModelProperty(value = "入库操作人名称")
    // @ApiModelProperty(value = "入库操作人名称")
    private String inOperatorName;
    @ApiModelProperty(value = "入库时间")
    // @ApiModelProperty(value = "入库时间")
    private Long inTime;
    @ApiModelProperty(value = "出库操作人id")
    // @ApiModelProperty(value = "出库操作人id")
    private Long outOperatorId;
    @ApiModelProperty(value = "出库操作人名称")
    // @ApiModelProperty(value = "出库操作人名称")
    private String outOperatorName;
    @ApiModelProperty(value = "出库时间")
    // @ApiModelProperty(value = "出库时间")
    private Long outputTime;
    @ApiModelProperty(value = "手续")
    // @ApiModelProperty(value = "手续")
    private String procureDoc;
    @ApiModelProperty(value = "部门名称")
    // @ApiModelProperty(value = "部门名称")
    private String departmentName;
    @ApiModelProperty(value = "电话")
    // @ApiModelProperty(value = "电话")
    private Long tel;
    private List<GoodsTemplateInfoVO> formTransferGoods;
consum-base/src/main/java/com/consum/base/pojo/response/LWhFormOutputExtendVO.java
@@ -1,6 +1,6 @@
package com.consum.base.pojo.response;
import io.swagger.annotations.ApiModel;
import java.util.List;
import lombok.Data;
@@ -11,7 +11,7 @@
 * @date 2023/11/13 16:45
 */
@Data
@ApiModel(value = "出库单信息扩展")
// @ApiModel(value = "出库单信息扩展")
public class LWhFormOutputExtendVO {
consum-base/src/main/java/com/consum/base/pojo/response/LWhFormScrappedExtendVO.java
@@ -1,6 +1,6 @@
package com.consum.base.pojo.response;
import io.swagger.annotations.ApiModel;
import java.util.List;
import lombok.Data;
@@ -10,7 +10,7 @@
 * @Date 2023/11/2
 */
@Data
@ApiModel
// @ApiModel
public class LWhFormScrappedExtendVO {
consum-base/src/main/java/com/consum/base/pojo/response/LWhFormScrappedVO.java
@@ -2,7 +2,7 @@
import java.util.List;
import io.swagger.annotations.ApiModel;
import lombok.Data;
/**
@@ -12,7 +12,7 @@
 * @date 2023/11/22 16:29
 */
@Data
@ApiModel(value = "LWhFormScrappedVO", description = "LWhFormScrappedVO")
// @ApiModel(value = "LWhFormScrappedVO", description = "LWhFormScrappedVO")
public class LWhFormScrappedVO {
    // 主键
consum-base/src/main/java/com/consum/base/pojo/response/TransferInfoVO.java
@@ -1,7 +1,7 @@
package com.consum.base.pojo.response;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
@@ -10,20 +10,20 @@
 * @description: 机构分发单信息
 * @date 2023/11/10 15:35
 */
@ApiModel(value = "机构分发单信息")
// @ApiModel(value = "机构分发单信息")
@Data
public class TransferInfoVO {
    @ApiModelProperty(value = "id")
    // @ApiModelProperty(value = "id")
    private String id;
    @ApiModelProperty(value = "单号")
    // @ApiModelProperty(value = "单号")
    private String businessFormCode;
    @ApiModelProperty(value = "物品名称")
    // @ApiModelProperty(value = "物品名称")
    private String goodsTemplatesName;
    @ApiModelProperty(value = "分发数量")
    // @ApiModelProperty(value = "分发数量")
    private String num;
    @ApiModelProperty(value = "可退数量")
    // @ApiModelProperty(value = "可退数量")
    private String refundNum;
    @ApiModelProperty(value = "分发时间")
    // @ApiModelProperty(value = "分发时间")
    private String createDate;
}
consum-base/src/main/java/com/consum/base/pojo/response/UseInfo.java
@@ -1,6 +1,6 @@
package com.consum.base.pojo.response;
import io.swagger.annotations.ApiModel;
import lombok.Data;
/**
@@ -10,7 +10,7 @@
 * @date 2023/12/2 9:34
 */
@Data
@ApiModel
// @ApiModel
public class UseInfo {
    private String useName;
consum-base/src/main/java/com/consum/base/pojo/response/UsingFormBackDetailListVO.java
@@ -1,6 +1,6 @@
package com.consum.base.pojo.response;
import io.swagger.annotations.ApiModel;
import lombok.Data;
import lombok.NoArgsConstructor;
@@ -12,7 +12,7 @@
 */
@NoArgsConstructor
@Data
@ApiModel
// @ApiModel
public class UsingFormBackDetailListVO {
    private Long id;
consum-base/src/main/java/com/consum/base/pojo/response/UsingFormBackGoodsTemplateInfo.java
@@ -1,6 +1,6 @@
package com.consum.base.pojo.response;
import io.swagger.annotations.ApiModel;
import java.util.List;
import lombok.Data;
@@ -11,7 +11,7 @@
 * @date 2023/12/2 9:18
 */
@Data
@ApiModel
// @ApiModel
public class UsingFormBackGoodsTemplateInfo {
    private Long id;
consum-base/src/main/java/com/consum/base/pojo/response/UsingFormBackModelInfo.java
@@ -1,6 +1,6 @@
package com.consum.base.pojo.response;
import io.swagger.annotations.ApiModel;
import java.util.List;
import lombok.Data;
@@ -11,7 +11,7 @@
 * @date 2023/12/2 9:24
 */
@Data
@ApiModel
// @ApiModel
public class UsingFormBackModelInfo {
    private Long id;
consum-base/src/main/java/com/consum/base/pojo/response/WarehouseFlowVO.java
@@ -1,7 +1,7 @@
package com.consum.base.pojo.response;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.NoArgsConstructor;
@@ -11,30 +11,30 @@
 * @description: 仓库流水明细信息
 * @date 2023/11/11 11:26
 */
@ApiModel(value = "仓库流水明细信息")
// @ApiModel(value = "仓库流水明细信息")
@NoArgsConstructor
@Data
public class WarehouseFlowVO {
    @ApiModelProperty(value = "商品模板名称")
    // @ApiModelProperty(value = "商品模板名称")
    private String goodsTemplateName;
    @ApiModelProperty(value = "规格名称")
    // @ApiModelProperty(value = "规格名称")
    private String baseGoodsModelsName;
    @ApiModelProperty(value = "交易时间")
    // @ApiModelProperty(value = "交易时间")
    private Long dealTime;
    @ApiModelProperty(value = "交易类型")
    // @ApiModelProperty(value = "交易类型")
    private Integer businessType;
    @ApiModelProperty(value = "创建人")
    // @ApiModelProperty(value = "创建人")
    private String createdName;
    @ApiModelProperty(value = "数量")
    // @ApiModelProperty(value = "数量")
    private Integer thisCount;
    @ApiModelProperty(value = "交易单号")
    // @ApiModelProperty(value = "交易单号")
    private String businessFormCode;
    @ApiModelProperty(value = "经营单位")
    // @ApiModelProperty(value = "经营单位")
    private String agencyName;
    @ApiModelProperty(value = "交易单ID")
    // @ApiModelProperty(value = "交易单ID")
    private Long businessFormId;
    @ApiModelProperty(value = "交易类型")
    // @ApiModelProperty(value = "交易类型")
    private Integer thisType;
}
consum-base/src/main/java/com/consum/base/service/LGoodsUserRecordCoreService.java
@@ -4,8 +4,7 @@
import java.util.HashMap;
import java.util.List;
import javax.annotation.Resource;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport;
consum-base/src/main/java/com/consum/base/service/core/DepFormScrappedCoreService.java
@@ -8,8 +8,7 @@
import java.util.Objects;
import java.util.stream.Collectors;
import javax.annotation.Resource;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
consum-base/src/main/java/com/consum/base/service/core/DepUsingFormBackCoreService.java
@@ -7,8 +7,7 @@
import java.util.Map;
import java.util.stream.Collectors;
import javax.annotation.Resource;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
consum-base/src/main/java/com/consum/base/service/core/LWarehouseFlowCoreService.java
@@ -6,8 +6,7 @@
import java.util.Map;
import java.util.stream.Collectors;
import javax.annotation.PostConstruct;
import jakarta.annotation.PostConstruct;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.jdbc.core.JdbcTemplate;
consum-base/src/main/java/com/consum/base/service/core/LWhFormInventoryCoreService.java
@@ -206,7 +206,7 @@
        if (CollectionUtils.isEmpty(inventoryGoodsList)) {
            return null;
        }
        Long whFormProcureId = NumberGenerator.getLongSequenceNumber();
        Long whFormProcureId = IdUtil.generateId();
        BaseWarehouse baseWarehouse = baseWarehouseService.getById(warehouseId);
        LWhFormProcure lWhFormProcure = new LWhFormProcure();
        lWhFormProcure.setId(whFormProcureId);
@@ -238,7 +238,7 @@
            Long baseCategoryId = Long.valueOf(tempGoodsInfo.get("categoryid") + "");
            LWhFormProcureGoods procureGood = new LWhFormProcureGoods();
            long fromProcureGoodsId = NumberGenerator.getLongSequenceNumber();
            long fromProcureGoodsId = IdUtil.generateId();
            procureGood.setId(fromProcureGoodsId);
            procureGood.setWhFormProcureId(whFormProcureId);
            procureGood.setBaseCategoryId(baseCategoryId);
consum-base/src/main/java/com/consum/base/service/core/LWhFormOutputCoreService.java
@@ -99,7 +99,7 @@
            return outWarehouseFormId;
        }
        // 出库单ID
        outWarehouseFormId = NumberGenerator.getLongSequenceNumberNano();
        outWarehouseFormId = IdUtil.generateId();
        // 申请调拨的物品
        List<LWhProcureModel> goodsModelNumList = lWhProcureModelService.getModelByForm(businessEnum, whFormTransferId);
@@ -191,7 +191,7 @@
        String nickName = currentUser.getNick_name();
        // 流水记录总表ID
        long lWarehouseFlowId = NumberGenerator.getLongSequenceNumberNano();
        long lWarehouseFlowId = IdUtil.generateId();
        long lWarehouseFlowBusinessId = outWarehouseFormId;
        Integer queryModelStatus = 1;
        Integer buyType = null;
@@ -234,7 +234,7 @@
            String modelName = (String)tempGoodsInfo.get("modelname");
            // 插入 各规格物品的进出库记录 L_WH_GOODS_RECORD
            long whGoodsRecordId = NumberGenerator.getLongSequenceNumberNano();
            long whGoodsRecordId = IdUtil.generateId();
            LWhGoodsRecord whGoodsRecord = new LWhGoodsRecord();
            whGoodsRecord.setId(whGoodsRecordId);
            whGoodsRecord.setWarehouseId(outWarehouseId);
consum-base/src/main/java/com/consum/base/service/core/LWhFormProcureCoreService.java
@@ -5,8 +5,8 @@
import java.util.List;
import java.util.stream.Collectors;
import javax.annotation.Resource;
import com.iplatform.base.IdUtil;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
@@ -66,7 +66,7 @@
            lWhProcureModelService.getModelByForm1(WhBusinessEnum.CAIGOU, whFormProcureId);
        // 流水记录总表ID
        Long warehouseId = lWhFormProcure.getWarehouseId();
        long lWarehouseFlowId = NumberGenerator.getLongSequenceNumberNano();
        long lWarehouseFlowId = IdUtil.generateId();
        LWarehouseFlow warehouseFlow = new LWarehouseFlow();
        warehouseFlow.setId(lWarehouseFlowId);
        warehouseFlow.setWarehouseType(0);
consum-base/src/main/java/com/consum/base/service/core/LWhFormTransferCoreService.java
@@ -7,8 +7,7 @@
import java.util.Map;
import java.util.stream.Collectors;
import javax.annotation.Resource;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
@@ -173,7 +172,7 @@
        String inWarehouseName = lWhFormTransfer.getInWarehouseName();
        // 流水记录总表ID
        long lWarehouseFlowId = NumberGenerator.getLongSequenceNumberNano();
        long lWarehouseFlowId = IdUtil.generateId();
        // 流水记录总表中 业务ID 。调拨单时 保存调拨单id 出库单时 保存出库单id
        long lWarehouseFlowBusinessId = whFormTransferId;
@@ -204,7 +203,7 @@
            String modelName = (String)tempGoodsInfo.get("modelname");
            // 插入 各规格物品的进出库记录 L_WH_GOODS_RECORD
            long whGoodsRecordId = NumberGenerator.getLongSequenceNumberNano();
            long whGoodsRecordId = IdUtil.generateId();
            LWhGoodsRecord whGoodsRecord = new LWhGoodsRecord();
            whGoodsRecord.setId(whGoodsRecordId);
            whGoodsRecord.setWarehouseId(outWarehouseId);
@@ -325,7 +324,7 @@
        long dealTime = DateUtils.getDateTimeNumber(System.currentTimeMillis());
        // 流水记录总表ID
        long inWarehouseFlowId = NumberGenerator.getLongSequenceNumber();
        long inWarehouseFlowId = IdUtil.generateId();
        // 单据类型。0仓库调拨;1部门分发;2部门物品回退
        Integer businessType = lWhFormTransfer.getBusinessType();
        // 状态(0=在途调拨;1=入库未分发;2=已下发;3=报废 4 零星出库)
consum-base/src/main/java/com/consum/base/service/core/LWhWarningCoreService.java
@@ -5,8 +5,7 @@
import java.util.Map;
import java.util.stream.Collectors;
import javax.annotation.Resource;
import jakarta.annotation.Resource;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
consum-base/src/main/java/com/consum/base/service/impl/DepFormScrappedServiceImpl.java
@@ -6,8 +6,7 @@
import java.util.Map;
import java.util.stream.Collectors;
import javax.annotation.Resource;
import jakarta.annotation.Resource;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
consum-base/src/main/java/com/consum/base/service/impl/LWhFormInventoryServiceImpl.java
@@ -10,6 +10,7 @@
import cn.hutool.core.collection.CollectionUtil;
import com.consum.base.pojo.query.LWhLedgerQry;
import com.consum.base.service.*;
import jakarta.annotation.Resource;
import org.apache.commons.compress.utils.Lists;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
@@ -42,10 +43,7 @@
import com.walker.infrastructure.utils.DateUtils;
import com.walker.infrastructure.utils.StringUtils;
import com.walker.jdbc.service.BaseServiceImpl;
import lombok.extern.slf4j.Slf4j;
import javax.annotation.Resource;
/**
 * @Description 盘点
consum-base/src/main/java/com/consum/base/service/impl/LWhFormProcureServiceImpl.java
@@ -8,8 +8,7 @@
import java.util.Map;
import java.util.Set;
import javax.annotation.Resource;
import jakarta.annotation.Resource;
import org.apache.commons.compress.utils.Lists;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
consum-base/src/main/java/com/consum/base/service/impl/LWhGoodsRecordDetailsService.java
@@ -3,8 +3,7 @@
import java.util.List;
import java.util.Map;
import javax.annotation.PostConstruct;
import jakarta.annotation.PostConstruct;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
consum-base/src/main/java/com/consum/base/service/impl/UsingFormBackServiceImpl.java
@@ -21,13 +21,12 @@
import com.walker.infrastructure.utils.DateUtils;
import com.walker.infrastructure.utils.StringUtils;
import com.walker.jdbc.service.BaseServiceImpl;
import jakarta.annotation.Resource;
import org.apache.commons.compress.utils.Lists;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
consum-model-pojo/pom.xml
@@ -45,7 +45,9 @@
        <dependency>
            <groupId>com.walkersoft</groupId>
            <artifactId>walker-jdbc-common</artifactId>
            <scope>provided</scope>
            <version>${walker-jdbc-common.version}</version>
            <systemPath>${project.basedir}/src/main/resources/lib/walker-jdbc-common-3.2.0.jar</systemPath>
            <scope>system</scope>
        </dependency>
        <!-- json注解:属性绑定依赖 -->
        <dependency>
@@ -53,6 +55,10 @@
            <artifactId>jackson-databind</artifactId>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
        </dependency>
    </dependencies>
</project>
deploy-jar-single/pom.xml
@@ -32,11 +32,18 @@
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.31</version> <!-- 使用适合您项目的版本号 -->
        </dependency>
        <!-- 通过界面下载po生成文件,2023/03/16 -->
        <dependency>
            <groupId>com.iplatform</groupId>
            <artifactId>iplatform-jdbc-generator</artifactId>
            <version>3.2.0</version>
            <systemPath>${project.basedir}/src/main/resources/lib/iplatform-jdbc-generator-2.3.0-SNAPSHOT.jar</systemPath>
            <scope>system</scope>
        </dependency>
        <!-- 阿里druid数据库连接池,如果使用直接引入到业务发布模块中,2023-03-15 -->
@@ -45,31 +52,24 @@
            <artifactId>druid</artifactId>
        </dependency>
        <!-- 如果使用Redis缓存,则引入支持模块 -->
        <!-- redis支持 -->
        <dependency>
            <groupId>com.walkersoft</groupId>
            <artifactId>walker-support-redis</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>com.walkersoft</groupId>
                    <artifactId>walker-cache</artifactId>
                </exclusion>
            </exclusions>
            <version>${walker-support-redis.version}</version>
            <systemPath>${project.basedir}/src/main/resources/lib/walker-support-redis-3.2.0.jar</systemPath>
            <scope>system</scope>
        </dependency>
        <dependency>
            <groupId>com.walkersoft</groupId>
            <artifactId>walker-jdbc</artifactId>
            <version>3.2.0</version>
            <systemPath>${project.basedir}/src/main/resources/lib/walker-jdbc-3.2.0.jar</systemPath>
            <scope>system</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-autoconfigure</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <version>2.1.6.RELEASE</version>
            <exclusions>
                <exclusion>
                    <artifactId>snakeyaml</artifactId>
                    <groupId>org.yaml</groupId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
@@ -92,64 +92,23 @@
        <dependency>
            <groupId>org.apache.tomcat.embed</groupId>
            <artifactId>tomcat-embed-core</artifactId>
            <version>9.0.89</version>
            <version>10.1.31</version>
        </dependency>
        <dependency>
            <groupId>org.apache.tomcat.embed</groupId>
            <artifactId>tomcat-embed-el</artifactId>
            <version>9.0.89</version>
            <version>10.1.31</version>
        </dependency>
        <dependency>
            <groupId>org.apache.tomcat.embed</groupId>
            <artifactId>tomcat-embed-websocket</artifactId>
            <version>9.0.89</version>
            <version>10.1.31</version>
        </dependency>
        <dependency>
            <groupId>org.apache.tomcat</groupId>
            <artifactId>tomcat-annotations-api</artifactId>
            <version>9.0.89</version>
            <version>10.1.31</version>
        </dependency>
        <!--        &lt;!&ndash; 2023-06-25 使用Oracle数据库需要配置,不需要直接注释即可。 &ndash;&gt;-->
<!--        <dependency>-->
<!--            <groupId>com.walkersoft</groupId>-->
<!--            <artifactId>walker-jdbc-support-oracle</artifactId>-->
<!--        </dependency>-->
        <!-- elastic search 需要单独引入模块, 2023/07/31 -->
<!--        <dependency>-->
<!--            <groupId>org.springframework.boot</groupId>-->
<!--            <artifactId>spring-boot-starter-web</artifactId>-->
<!--            <exclusions>-->
<!--                <exclusion>-->
<!--                    <groupId>org.elasticsearch.client</groupId>-->
<!--                    <artifactId>elasticsearch-rest-high-level-client</artifactId>-->
<!--                </exclusion>-->
<!--                <exclusion>-->
<!--                    <groupId>org.elasticsearch</groupId>-->
<!--                    <artifactId>elasticsearch</artifactId>-->
<!--                </exclusion>-->
<!--            </exclusions>-->
<!--        </dependency>-->
        <!-- 2023-08-07 为现场调试,暂时关闭检索模块 -->
<!--        <dependency>-->
<!--            <groupId>org.elasticsearch.client</groupId>-->
<!--            <artifactId>elasticsearch-rest-high-level-client</artifactId>-->
<!--            <version>${es.version}</version>-->
<!--        </dependency>-->
<!--        <dependency>-->
<!--            <groupId>org.elasticsearch</groupId>-->
<!--            <artifactId>elasticsearch</artifactId>-->
<!--            <version>${es.version}</version>-->
<!--        </dependency>-->
<!--        &lt;!&ndash; 积木报表功能,需要使用打开注释即可,2023-08-24 &ndash;&gt;-->
<!--        <dependency>-->
<!--            <groupId>com.iplatform</groupId>-->
<!--            <artifactId>iplatform-report</artifactId>-->
<!--        </dependency>-->
        <!-- 配置文件加密 -->
        <dependency>
            <groupId>com.github.ulisesbocchio</groupId>
@@ -183,7 +142,6 @@
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-core</artifactId>
            <version>5.7.12</version>
        </dependency>
    </dependencies>
deploy-jar-single/src/main/resources/application-dev.yml
@@ -8,8 +8,8 @@
    dataBaseName: low_consum_manage_test24041101
    username: root
#    Bjjmy_2020
    password: ENC(V7lFKlYcHfEzTbXsbBQhSUswgxLsbS5z)
    url: jdbc:mysql://116.198.40.76:3306/${spring.datasource.dataBaseName}?useUnicode=true&characterEncoding=UTF-8&useSSL=false
    password: Bjjmy_2020
    url: jdbc:mysql://116.198.40.76:3306/low_consum_manage_test24041101?useUnicode=true&characterEncoding=UTF-8&useSSL=false
# Druid连接池
    type: com.walker.jdbc.ds.MyDruidDataSource
    druid:
@@ -32,12 +32,6 @@
      max-open-prepared-statements: 100
      use-global-data-source-stat: true
# Redis
  redis:
    host: 116.198.40.76
    port: 6379
    database: 11
    password: ENC(C40h1dp9Q1oLqdU+JUGSBOWUfOmj/ZO4)   #Jmy2019.
  mvc:
    pathmatch:
      # 加该配置是因为 swagger3 启动报错,2023-02-23
@@ -50,6 +44,12 @@
    multipart:
      max-file-size: 100MB
      max-request-size: 300MB
  data:
    redis:
      host: 116.198.40.76
      port: 6379
      database: 11
      password: Jmy2019.
server:
  port: 8083
@@ -152,7 +152,7 @@
#      - /api/**
    # 超级管理员密码,加密后的秘文
    supervisor-password: ENC(cDkgI4dm/vDXFDsDm5KJW23i5zAAUxIUW448ReA27tmgQ83ZLgO3k/YMK5+wIiuGDKPDIXUH9/irgKTLMKl7n7mP6XRgBnOf)
    supervisor-password: $2a$10$9lSwwUFMULR6/KhPsUbTj.0PTZfTnq0fB3OtS6PWoKAibpa8hL1cy
    #    supervisor-password: $2a$10$9lSwwUFMULR6/KhPsUbTj.0PTZfTnq0fB3OtS6PWoKAibpa8hL1cy
    # 是否允许配置跨域响应头, true 启用, false 不启用。2022-12-28
    # 在Gateway模式中,需要关闭跨域配置,因为网关也会配置。
@@ -219,7 +219,7 @@
      ip: 116.198.40.76
      port: 22
      user-name: mysftp
      password: ENC(kEQ43JzZv6yOOmbmKNLWmsU/wQKTwA9x)
      password: 123456
      private-key:
      # ftp服务上面的存储根路径,只能是linux路径
      file-root: /progress/
@@ -249,4 +249,4 @@
    # 邮件通知发送信息配置,2023-04-26
    mail-server: smtp.126.com
    mail-from: hnzzzhsl@126.com
    mail-password: ENC(hjRDRcQmmhpUICkAaJnXUQ==)
    mail-password: 123456
deploy-jar-single/src/main/resources/application-prod.yml
@@ -31,12 +31,6 @@
      max-open-prepared-statements: 100
      use-global-data-source-stat: true
  # Redis
  redis:
    host: 192.200.0.49
    port: 6379
    database: 11
    password: ENC(1IxlpqZ3ykMIWVknDjVydrCpK/6nccv/)   #Jmy2019.
  mvc:
    pathmatch:
      # 加该配置是因为 swagger3 启动报错,2023-02-23
@@ -49,6 +43,12 @@
    multipart:
      max-file-size: 100MB
      max-request-size: 300MB
  data:
    redis:
      host: 192.200.0.49
      port: 6379
      database: 11
      password: ENC(1IxlpqZ3ykMIWVknDjVydrCpK/6nccv/)
server:
  port: 9301
deploy-jar-single/src/main/resources/application-test.yml
@@ -32,12 +32,6 @@
      max-open-prepared-statements: 100
      use-global-data-source-stat: true
  # Redis
  redis:
    host: 116.198.40.76
    port: 6379
    database: 11
    password: ENC(C40h1dp9Q1oLqdU+JUGSBOWUfOmj/ZO4)   #Jmy2019.
  mvc:
    pathmatch:
      # 加该配置是因为 swagger3 启动报错,2023-02-23
@@ -50,6 +44,12 @@
    multipart:
      max-file-size: 100MB
      max-request-size: 300MB
  data:
    redis:
      host: 116.198.40.76
      port: 6379
      database: 11
      password: ENC(C40h1dp9Q1oLqdU+JUGSBOWUfOmj/ZO4)
server:
  port: 8083
deploy-jar-single/src/main/resources/application.yml
@@ -1,4 +1,4 @@
spring:
  profiles:
    active: prod
    active: dev
iplatform-base-admin/pom.xml
New file
@@ -0,0 +1,32 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>low-consum-manage</artifactId>
        <groupId>com.consum</groupId>
        <version>1.0.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.iplatform</groupId>
    <artifactId>iplatform-base-admin</artifactId>
    <name>iplatform-base-admin</name>
    <packaging>jar</packaging>
    <version>3.2.0</version>
    <properties>
    </properties>
    <dependencies>
        <!-- 基础模块界面管理,只依赖基础模块,运行环境必须提供。2022/11/16 -->
        <dependency>
            <groupId>com.iplatform</groupId>
            <artifactId>iplatform-base</artifactId>
            <scope>provided</scope>
        </dependency>
    </dependencies>
</project>
iplatform-base-admin/src/main/java/com/iplatform/base/IdUtil.java
New file
@@ -0,0 +1,78 @@
package com.iplatform.base;
public class IdUtil {
    private static IdUtil instance = new IdUtil(0);
    public static IdUtil getInstance() {
        return instance;
    }
    public static long generateId() {
        return instance.nextId();
    }
    private final static long MACHINE_BIT = 5; // max 31
    private final static long SEQUENCE_BIT = 8; // 256/10ms
    /**
     * mask/max value
     */
    private final static long MAX_MACHINE_NUM = -1L ^ (-1L << MACHINE_BIT);
    private final static long MAX_SEQUENCE = -1L ^ (-1L << SEQUENCE_BIT);
    private final static long MACHINE_LEFT = SEQUENCE_BIT;
    private final static long TIMESTMP_LEFT = MACHINE_BIT + SEQUENCE_BIT;
    private long machineId;
    private long sequence = 0L;
    private long lastStmp = -1L;
    private IdUtil(long machineId) {
        if (machineId > MAX_MACHINE_NUM || machineId < 0) {
            throw new IllegalArgumentException(
                    "machineId can't be greater than " + MAX_MACHINE_NUM + " or less than 0");
        }
        this.machineId = machineId;
    }
    /**
     * generate new ID
     *
     * @return
     */
    public synchronized long nextId() {
        long currStmp = getTimestamp();
        if (currStmp < lastStmp) {
            throw new RuntimeException("Clock moved backwards.  Refusing to generate id");
        }
        if (currStmp == lastStmp) {
            sequence = (sequence + 1) & MAX_SEQUENCE;
            if (sequence == 0L) {
                currStmp = getNextTimestamp();
            }
        } else {
            sequence = 0L;
        }
        lastStmp = currStmp;
        return currStmp << TIMESTMP_LEFT //
                | machineId << MACHINE_LEFT //
                | sequence;
    }
    private long getNextTimestamp() {
        long mill = getTimestamp();
        while (mill <= lastStmp) {
            mill = getTimestamp();
        }
        return mill;
    }
    private long getTimestamp() {
        // per 10ms
        return System.currentTimeMillis() / 10;// 10ms
    }
}
iplatform-base-admin/src/main/java/com/iplatform/base/Test1.java
New file
@@ -0,0 +1,7 @@
package com.iplatform.base;
import com.walker.web.UserType;
public class Test1 {
    private UserType userType = UserType.User;
}
iplatform-base-admin/src/main/java/com/iplatform/base/config/SwaggerConfig.java
New file
@@ -0,0 +1,70 @@
//package com.iplatform.base.config;
//
//import com.walker.infrastructure.utils.StringUtils;
//
//import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
//import org.springframework.context.annotation.Bean;
//import org.springframework.context.annotation.Configuration;
//import org.springframework.context.annotation.Profile;
//import springfox.documentation.builders.ApiInfoBuilder;
//import springfox.documentation.builders.PathSelectors;
//import springfox.documentation.builders.RequestHandlerSelectors;
//import springfox.documentation.oas.annotations.EnableOpenApi;
//import springfox.documentation.service.ApiInfo;
//import springfox.documentation.service.Contact;
//import springfox.documentation.spi.DocumentationType;
//import springfox.documentation.spring.web.plugins.ApiSelectorBuilder;
//import springfox.documentation.spring.web.plugins.Docket;
//
//@Configuration
//@EnableOpenApi
//@Profile({"dev"})
//@ConditionalOnProperty(name = "iplatform.security.swagger.enable", havingValue = "true")
////@EnableSwaggerBootstrapUI//访问美化,方便查看调试
//public class SwaggerConfig {
//
//    @Bean
//    public Docket createApi(SwaggerProperties properties) {
//        ApiSelectorBuilder builder = new Docket(DocumentationType.OAS_30).apiInfo(apiInfo(properties)).select();
//        if(StringUtils.isNotEmpty(properties.getBasePackage())){
//            // 如果存在包路径,优先使用
//            builder.apis(RequestHandlerSelectors.basePackage(properties.getBasePackage()));
//        } else {
//            // 没有配置包路径,则查找注解API的文档
//            builder.apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class));
//        }
//
//        Docket docket = builder.paths(PathSelectors.any()).build();
////        docket.globalRequestParameters(getGlobalRequestParameters());
////        docket.globalResponses(HttpMethod.POST, getGlobalResonseMessage());
//        return docket;
//
///*        return new Docket(DocumentationType.OAS_30)
//                .apiInfo(apiInfo(properties))
//                .select()
//                // 指定扫描的包,不指定会扫描出 spring 框架的接口,指定错误会导致接口扫描不出来
////                .apis(RequestHandlerSelectors.basePackage(properties.getBasePackage()))
//                .apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class))
//                .paths(PathSelectors.any())
//                .build()
//                //加入通用入参
////                .globalRequestParameters(getGlobalRequestParameters())
//                //post方法加入通用响应头
////                .globalResponses(HttpMethod.POST, getGlobalResonseMessage())
//                ;*/
//    }
//
//    @Bean
//    public SwaggerProperties swaggerProperties(){
//        return new SwaggerProperties();
//    }
//
//    private ApiInfo apiInfo(SwaggerProperties swaggerProperties) {
//        return new ApiInfoBuilder()
//                .title(swaggerProperties.getTitle())
//                .description(swaggerProperties().getDescription())
//                .contact(new Contact("时克英", "https://www.baidu.com", "28914843@qq.com"))
//                .version("2.0")
//                .build();
//    }
//}
iplatform-base-admin/src/main/java/com/iplatform/base/config/SwaggerProperties.java
New file
@@ -0,0 +1,35 @@
//package com.iplatform.base.config;
//
//import org.springframework.boot.context.properties.ConfigurationProperties;
//
//@ConfigurationProperties(prefix = "iplatform.swagger")
//public class SwaggerProperties {
//
//    public String getBasePackage() {
//        return basePackage;
//    }
//
//    public void setBasePackage(String basePackage) {
//        this.basePackage = basePackage;
//    }
//
//    public String getTitle() {
//        return title;
//    }
//
//    public void setTitle(String title) {
//        this.title = title;
//    }
//
//    public String getDescription() {
//        return description;
//    }
//
//    public void setDescription(String description) {
//        this.description = description;
//    }
//
//    private String basePackage;
//    private String title;
//    private String description;
//}
iplatform-base-admin/src/main/java/com/iplatform/base/controller/ApiTimeController.java
New file
@@ -0,0 +1,49 @@
package com.iplatform.base.controller;
import com.iplatform.base.SystemController;
import com.iplatform.base.service.ApiTimeServiceImpl;
import com.iplatform.model.vo.ApiTime;
import com.walker.infrastructure.utils.StringUtils;
import com.walker.web.ResponseValue;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@RestController
@RequestMapping("/test/api")
public class ApiTimeController extends SystemController {
    private ApiTimeServiceImpl apiTimeService;
    @Autowired
    public ApiTimeController(ApiTimeServiceImpl apiTimeService){
        this.apiTimeService = apiTimeService;
    }
    /**
     * 查询接口调用时间记录
     * @param currentDate 当前日期,如:20240229(可选)
     * @param maxSize 限制返回记录最大条数,如:200(可选)
     * @param cost 条件:最小花费时间,毫秒,超过该时间的记录,如:200(可选)
     * @return
     */
    @RequestMapping("/time")
    public ResponseValue getCostMostList(String currentDate, String cost, String maxSize){
        Integer dateValue = null;
        Integer maxSizeValue = null;
        Long costValue = null;
        if(StringUtils.isNotEmpty(currentDate)){
            dateValue = Integer.parseInt(currentDate);
        }
        if(StringUtils.isNotEmpty(maxSize)){
            maxSizeValue = Integer.parseInt(maxSize);
        }
        if(StringUtils.isNotEmpty(cost)){
            costValue = Long.parseLong(cost);
        }
        List<ApiTime> data = this.apiTimeService.queryCostList(dateValue, maxSizeValue, costValue);
        return ResponseValue.success(data);
    }
}
iplatform-base-admin/src/main/java/com/iplatform/base/controller/CacheController.java
New file
@@ -0,0 +1,154 @@
package com.iplatform.base.controller;
import com.iplatform.base.SystemController;
import com.iplatform.base.VariableConstants;
import com.iplatform.base.config.CacheProperties;
import com.iplatform.base.util.cache.CacheInfo;
import com.walker.cache.CacheProvider;
import com.walker.cache.SimpleCacheManager;
import com.walker.infrastructure.utils.StringUtils;
import com.walker.web.ResponseValue;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
@RestController
@RequestMapping("/monitor/cache")
@ConditionalOnProperty(prefix = "iplatform.cache", name = "redis-enabled", havingValue = "true", matchIfMissing = false)
public class CacheController extends SystemController {
    private RedisTemplate<String, Object> redisTemplate;
    private CacheProperties cacheProperties;
    @Autowired(required = false)
    public CacheController(RedisTemplate<String, Object> redisTemplate, CacheProperties cacheProperties){
        this.redisTemplate = redisTemplate;
        this.cacheProperties = cacheProperties;
    }
    @RequestMapping("/clearCacheName")
    public ResponseValue clearCacheName(String name){
        if(StringUtils.isEmpty(name)){
            return ResponseValue.error("必须指定缓存名称");
        }
        CacheProvider<?> cacheProvider = SimpleCacheManager.getCacheProvider(name);
        if(cacheProvider == null){
            return ResponseValue.error("缓存对象不存在");
        }
        try {
            cacheProvider.reload();
            return ResponseValue.success("缓存重新加载成功");
        } catch (Exception e) {
            logger.error("重新加载缓存失败:" + e.getMessage(), e);
            return ResponseValue.error("重新加载缓存失败:" + name);
        }
    }
    /**
     * 清空 系统缓存,该功能主要对 Redis 缓存方式,通过手动触发重构缓存,在开发阶段使用。
     * @return
     * @date 2023-08-26
     */
    @RequestMapping("/clearCacheAll")
    public ResponseValue reloadAllCacheProvider(){
        if(!this.cacheProperties.isRedisRebuild()){
            return ResponseValue.error("未开启'重构缓存'选项");
        }
        // 2023-08-26 开启了重建redis缓存选项
        Collection<CacheProvider<?>> cacheList = SimpleCacheManager.getCacheProviders();
        if(cacheList != null && cacheList.size() > 0){
            String name = null;
            try{
                for(CacheProvider<?> cacheProvider : cacheList){
                    name = cacheProvider.getProviderName();
                    cacheProvider.reload();
                }
            } catch (Exception ex){
                logger.error("重新加载所有缓存,出现异常:" + ex.getMessage() + ", name=" + name, ex);
                return ResponseValue.error("重新加载缓存,出现错误,name = " + name);
            }
        }
        return ResponseValue.success();
    }
    @RequestMapping("/getInfo")
    public ResponseValue getInfo(){
        if(this.redisTemplate == null){
            return ResponseValue.error("系统未开启Redis方式缓存");
        }
        Properties info = (Properties) redisTemplate.execute((RedisCallback<Object>) connection -> connection.info());
//        Properties commandStats = (Properties) redisTemplate.execute((RedisCallback<Object>) connection -> connection.info("commandstats"));
        Object dbSize = redisTemplate.execute((RedisCallback<Object>) connection -> connection.dbSize());
        Map<String, Object> result = new HashMap<>(3);
        result.put("info", info);
        result.put("dbSize", dbSize);
        result.put("commandStats", new ArrayList<>());
        return ResponseValue.success(result);
    }
    /**
     * 返回缓存对象名称列表,即:系统所有缓存定义名称集合
     * @return
     * @date 2023-01-04
     */
    @GetMapping("/select/getNames")
    public ResponseValue getCacheNames(){
        List<CacheInfo> cacheNames = new ArrayList<>();
        Collection<CacheProvider<?>> cacheList = SimpleCacheManager.getCacheProviders();
        if(cacheList != null && cacheList.size() > 0){
            for(CacheProvider<?> cacheProvider : cacheList){
                cacheNames.add(new CacheInfo(cacheProvider.getProviderName(), cacheProvider.getProviderType().getName()));
            }
        }
        return ResponseValue.success(cacheNames);
    }
    @GetMapping("/select/getKeys/{cacheName}")
    public ResponseValue getCacheKeys(@PathVariable String cacheName){
        if(StringUtils.isEmpty(cacheName)){
            return ResponseValue.error("参数错误");
        }
        List<String> keyList = new ArrayList<>((int) VariableConstants.MAX_CACHE_SHOW);
        CacheProvider<?> cacheProvider = SimpleCacheManager.getCacheProvider(cacheName);
        if(cacheProvider != null){
            if(cacheProvider.getCache().getPersistentSize() <= VariableConstants.MAX_CACHE_SHOW){
//                Cachable cachable = null;
//                for(Iterator<Cachable> it = cacheProvider.getCache().getIterator(); it.hasNext();){
//                    cachable = it.next();
//                    keyList.add(cachable.getKey());
//                }
                return ResponseValue.success(cacheProvider.getCache().getKeys());
            } else {
                return ResponseValue.error("缓存数据量超过 " + VariableConstants.MAX_CACHE_SHOW + ", 暂无法显示");
            }
        }
        return ResponseValue.success(keyList);
    }
    @GetMapping("/select/getValue/{cacheName}/{cacheKey}")
    public ResponseValue getOneCacheValue(@PathVariable String cacheName, @PathVariable String cacheKey){
        CacheProvider<?> cacheProvider = SimpleCacheManager.getCacheProvider(cacheName);
        if(cacheProvider != null){
            Object value = cacheProvider.getCacheData(cacheKey);
            CacheInfo cacheInfo = new CacheInfo(cacheName, cacheKey, value.toString());
            return ResponseValue.success(cacheInfo);
        }
        return ResponseValue.success();
    }
}
iplatform-base-admin/src/main/java/com/iplatform/base/controller/CategoryController.java
New file
@@ -0,0 +1,155 @@
package com.iplatform.base.controller;
import com.iplatform.base.CategoryCacheProvider;
import com.iplatform.base.Constants;
import com.iplatform.base.PlatformAdapterController;
import com.iplatform.base.cache.FormCacheProvider;
import com.iplatform.base.service.CategoryServiceImpl;
import com.iplatform.model.po.S_category;
import com.iplatform.model.vo.CategoryTreeVo;
import com.walker.infrastructure.utils.DateUtils;
import com.walker.infrastructure.utils.StringUtils;
import com.walker.web.ResponseValue;
import com.walker.web.WebRuntimeException;
//
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
/**
 * 平台端基础分类服务
 * @date 2023-05-15
 */
@RestController
@RequestMapping("/system/category")
public class CategoryController extends PlatformAdapterController {
    private CategoryServiceImpl categoryService;
    private CategoryCacheProvider categoryCacheProvider;
    private FormCacheProvider formCacheProvider;
    @Autowired
    public CategoryController(CategoryServiceImpl categoryService
            , CategoryCacheProvider categoryCacheProvider, FormCacheProvider formCacheProvider){
        this.categoryService = categoryService;
        this.categoryCacheProvider = categoryCacheProvider;
        this.formCacheProvider = formCacheProvider;
    }
    @RequestMapping("/list/tree")
    public ResponseValue getListTree(@RequestParam(name = "type") Integer type,
                                     @RequestParam(name = "status") Integer status,
                                     @RequestParam(name = "name", required = false) String name) {
        int owner = (int)this.getOwner();
//        return ResponseValue.success(this.categoryService.getListTree(type, status, name, owner));
        // 2023-05-18 表加上关联表单名称。
        List<CategoryTreeVo> data = this.categoryCacheProvider.getListTree(type, status, name, owner);
        if(!StringUtils.isEmptyList(data)){
            data.stream().forEach(e -> {
                if(e.getType().intValue() == Constants.CATEGORY_TYPE_CONFIG && StringUtils.isNotEmpty(e.getExtra())){
                    // 平台配置分类,extra存储的是formId
                    e.setFormName(this.formCacheProvider.getCacheData(e.getExtra()).getName());
                    if(!StringUtils.isEmptyList(e.getChild())){
                        for(CategoryTreeVo child : e.getChild()){
                            if(StringUtils.isNotEmpty(child.getExtra())){
                                child.setFormName(this.formCacheProvider.getCacheData(child.getExtra()).getName());
                            }
                        }
                    }
                }
            });
        }
        return ResponseValue.success(data);
    }
    @RequestMapping(value = "/info/{id}", method = RequestMethod.GET)
    public ResponseValue getInfo(@PathVariable(value = "id") Integer id){
        S_category category = this.categoryCacheProvider.get(id);
        if(category == null){
            throw new WebRuntimeException("分类不存在");
        }
        return ResponseValue.success(category);
    }
//    // @ApiOperation(value = "新增基础分类")
    @RequestMapping(value = "/save", method = RequestMethod.POST)
    public ResponseValue save(@RequestBody S_category category){
        if(category == null){
            return ResponseValue.error("提交内容错误");
        }
        logger.debug(category.toString());
        if(this.categoryService.queryNameUnique(category.getName(), category.getType(), (int)this.getOwner(), 0) > 0){
            return ResponseValue.error("相同分类已经存在");
        }
        long dateTime = DateUtils.getDateTimeNumber();
        int id = this.categoryService.queryNextId();
        category.setId(id);
        category.setPath(this.getPathByPId(category.getPid()));
        category.setOwner((int)this.getOwner());
        category.setExtra(this.clearCdnPrefix(category.getExtra()));    // 这里额外保存的可能是文件路径信息,清除了前缀。
        category.setCreate_time(dateTime);
        category.setUpdate_time(dateTime);
        this.categoryService.insert(category);
        this.categoryCacheProvider.save(category);
        return ResponseValue.success();
    }
//    // @ApiOperation(value = "修改基础分类")
    @RequestMapping(value = "/update", method = RequestMethod.POST)
    public ResponseValue update(@RequestBody S_category category){
        if(category == null || category.getId() <= 0){
            throw new WebRuntimeException("分类参数错误");
        }
        S_category exist = this.categoryCacheProvider.get(category.getId());
        if(exist == null){
            throw new WebRuntimeException("分类数据不存在,id=" + category.getId());
        }
        if(exist.getId().intValue() == category.getPid()){
            throw new WebRuntimeException("分类父级不能是自己");
        }
        if(!exist.getName().equals(category.getName())){
            if(this.categoryService.queryNameUnique(category.getName(), category.getType(), (int)this.getOwner(), exist.getId()) > 0){
                return ResponseValue.error("分类名称重复");
            }
        }
        if(StringUtils.isNotEmpty(category.getExtra())){
            category.setExtra(this.clearCdnPrefix(category.getExtra()));
        }
        category.setPath(this.getPathByPId(category.getPid()));
        category.setOwner((int)this.getOwner());
        category.setUpdate_time(DateUtils.getDateTimeNumber());
        this.categoryService.execUpdateCategory(category);
        this.categoryCacheProvider.update(category);
        return ResponseValue.success();
    }
    @RequestMapping("/delete")
    public ResponseValue delete(Integer id){
//        S_category category = this.categoryCacheProvider.get(id);
        if(this.categoryService.queryChildCategorySize(id) > 0){
            return ResponseValue.error("当前分类下有子类,请先删除子类!");
        }
        this.categoryService.delete(new S_category(id));
        this.categoryCacheProvider.remove(id);
        return ResponseValue.success();
    }
    private String getPathByPId(Integer pid) {
        S_category category = this.categoryCacheProvider.get(pid);
        if (category != null) {
            return category.getPath() + pid + StringUtils.FOLDER_SEPARATOR;
        }
        return "/0/";
    }
}
iplatform-base-admin/src/main/java/com/iplatform/base/controller/CodeController.java
New file
@@ -0,0 +1,237 @@
package com.iplatform.base.controller;
import com.iplatform.base.SystemController;
import com.iplatform.base.pojo.dict.DictParam;
import com.iplatform.base.service.CodeServiceImpl;
import com.iplatform.model.po.S_dict_data;
import com.iplatform.model.po.S_dict_type;
import com.iplatform.model.po.S_user_core;
import com.walker.db.page.GenericPager;
import com.walker.infrastructure.utils.DateUtils;
import com.walker.infrastructure.utils.StringUtils;
import com.walker.web.DataStatus;
import com.walker.web.ResponseValue;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.ArrayList;
import java.util.List;
@RestController
@RequestMapping("/system/dict")
public class CodeController extends SystemController {
    private CodeServiceImpl codeService;
    @Autowired
    public CodeController(CodeServiceImpl codeService){
        this.codeService = codeService;
    }
    /** 类型列表 */
    @RequestMapping("/type/list")
    public ResponseValue listType(DictParam dictParam){
        logger.debug(dictParam.toString());
        //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        // 需要添加拦截器,统一销毁线程变量对象,马上要补充,2022-11-19
        //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//        this.preparePageSearch();
        long beginTime = -1;
        long endTime = -1;
        if(dictParam.getParams().get("beginTime") != null){
            beginTime = this.getParamsDateTime(dictParam.getParams().get("beginTime").toString(), false);
        }
        if(dictParam.getParams().get("endTime") != null){
            endTime = this.getParamsDateTime(dictParam.getParams().get("endTime").toString(), false);
        }
        logger.debug("beginTime = " + beginTime + ", endTime = " + endTime);
        GenericPager<S_dict_type> pager = this.codeService.queryPageDictType(dictParam.getDictName()
                , dictParam.getDictType(), dictParam.getStatus(), beginTime, endTime);
//        return this.acquireTablePage(pager.getDatas(), pager.getTotalRows());
        return ResponseValue.success(pager);
    }
    /** 新增类型 */
    @PostMapping("/type/add")
    public ResponseValue<?> addType(@RequestBody DictParam dictParam){
        S_user_core currentUser = getCurrentUser();
        S_dict_type byDictType = codeService.getByDictType(dictParam.getDictType());
        if (byDictType != null) {
            return ResponseValue.error("字典类型[" + byDictType.getDict_type() + "] 已存在!");
        }
        S_dict_type dictType = new S_dict_type();
        dictType.setDict_type(dictParam.getDictType());
        dictType.setDict_name(dictParam.getDictName());
        if (dictParam.getStatus() == null) {
            dictType.setStatus(DataStatus.CONST_NORMAL);
        } else {
            dictType.setStatus(dictParam.getStatus());
        }
        if (currentUser != null) {
            dictType.setCreate_by(currentUser.getUser_name());
            dictType.setCreate_time(DateUtils.getDateTimeNumber());
        }
        dictType.setRemark(dictParam.getRemark());
        this.codeService.save(dictType);
        return ResponseValue.success(null);
    }
    /** 类型修改 */
    @PostMapping("/type/upd")
    public ResponseValue<?> updType(@RequestBody DictParam dictParam){
        S_dict_type dictType = new S_dict_type();
        dictType.setDict_id(dictParam.getDict_id());
        dictType.setDict_name(dictParam.getDictName());
        dictType.setStatus(dictParam.getStatus());
        dictType.setRemark(dictParam.getRemark());
        this.codeService.update(dictType);
        return ResponseValue.success(null);
    }
    /** 类型删除 */
    @DeleteMapping("/type/del")
    public ResponseValue<?> delType(@RequestBody DictParam dictParam) {
        this.codeService.execDelType(dictParam.getDict_id());
        return ResponseValue.success(null);
    }
    /** 类型详情 */
    @RequestMapping("/type/{dictId}")
    public ResponseValue detailDictType(@PathVariable Long dictId){
        if(dictId == null || dictId.longValue() <= 0){
            return ResponseValue.error("字典id错误");
        }
        return ResponseValue.success(this.codeService.queryOneDictType(dictId));
    }
    /**
     * 在数据字典项管理界面,查询条件展示所有'字典类型'列表。<p></p>
     * 因为权限配置的是: /system/dict/data/** 开放,因此该权限也配置到data,否则还需要再加上一个权限点。
     * @return
     * @date 2022-11-19
     */
//    @GetMapping("/type/optionselect")
    @GetMapping("/data/optionselect")
    public ResponseValue showDictTypeListAll(){
        List<S_dict_type> list = this.codeService.selectAll(new S_dict_type());
        return ResponseValue.success(list);
    }
    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~`
    /**
     * 根据代码表名字,查询包含的代码项集合。
     * @param dictType
     * @return
     */
    @RequestMapping("/data/type/{dictType}")
    public ResponseValue<List<S_dict_data>> dictTypeList(@PathVariable String dictType){
        logger.debug("dictType = " + dictType);
        List<S_dict_data> list = this.codeService.queryDictDataByType(dictType);
        if(StringUtils.isEmptyList(list)){
            list = new ArrayList<S_dict_data>(2);
        }
        return ResponseValue.success(list);
    }
    /** 字典数据列表 */
    @RequestMapping("/data/list")
    public ResponseValue listData(DictParam dictParam){
//        this.preparePageSearch();
        if(StringUtils.isEmpty(dictParam.getDictType())){
            return ResponseValue.error("必须提供字典类型参数");
        }
        GenericPager<S_dict_data> pager = this.codeService.queryPageDictData(dictParam.getDictType(), dictParam.getDictLabel());
//        return this.acquireTablePage(pager.getDatas(), pager.getTotalRows());
        return ResponseValue.success(pager);
    }
    /** 字典数据新增 */
    @PostMapping("/add")
    public ResponseValue<?> addDictData(@RequestBody S_dict_data s_dict_data){
        logger.debug(s_dict_data.toString());
//        if(s_dict_data.getDict_code() == null){
//            return ResponseValue.error("字典id必须输入!");
//        }
        if(s_dict_data.getParent_id() == null){
            return ResponseValue.error("父id必须输入!");
        }
        S_dict_data dict_data = this.getDictCacheProvider().getCacheData(String.valueOf(s_dict_data.getDict_code()));
        if(dict_data != null){
            return ResponseValue.error("字典id已存在!");
        }
        String error = this.validateDictData(s_dict_data, true);
        if(error != null){
            return ResponseValue.error(error);
        }
        s_dict_data.setIs_default("N");
        if(StringUtils.isEmpty(s_dict_data.getDict_value())){
            // 字典值如果不填,必须和id一致
            s_dict_data.setDict_value(String.valueOf(s_dict_data.getDict_code()));
        }
//        s_dict_data.setDict_code(NumberGenerator.getSequenceNumber());
        s_dict_data.setCreate_time(DateUtils.getDateTimeNumber(System.currentTimeMillis()));
        this.codeService.save(s_dict_data);
        this.getDictCacheProvider().putCacheData(String.valueOf(s_dict_data.getDict_code()), s_dict_data);
        return ResponseValue.success();
    }
    /** 字典数据详情 */
    @RequestMapping("/data/{dictCode}")
    public ResponseValue getDictDataInfo(@PathVariable Long dictCode){
        S_dict_data e = this.codeService.queryOneDictData(dictCode);
        return ResponseValue.success(e);
    }
    /** 字典数据修改 */
    @RequestMapping("/edit")
    public ResponseValue updateDictData(@RequestBody S_dict_data s_dict_data){
        String error = this.validateDictData(s_dict_data, false);
        if(error != null){
            return ResponseValue.error(error);
        }
        this.codeService.save(s_dict_data);
        this.getDictCacheProvider().updateCacheData(String.valueOf(s_dict_data.getDict_code()), s_dict_data);
        return ResponseValue.success();
    }
    /** 字典数据删除 */
    @RequestMapping("/data/remove/{dictCodes}")
    public ResponseValue removeDictData(@PathVariable Long[] dictCodes){
        this.codeService.execDeleteDictData(dictCodes);
        return ResponseValue.success();
    }
    /**
     * 校验
     * @param s_dict_data 数据
     * @param checkExist 是否检查存在
     * @return 错误信息
     */
    private String validateDictData(S_dict_data s_dict_data, boolean checkExist){
        if(s_dict_data == null){
            return "提交字典内容为空";
        }
        if(StringUtils.isEmpty(s_dict_data.getDict_type())){
            return "请选择字典类型";
        }
        if(StringUtils.isEmpty(s_dict_data.getDict_label())){
            return "请输入字典标签";
        }
//        if(StringUtils.isEmpty(s_dict_data.getDict_value())){
//            return "请输入字典值";
//        }
        if(checkExist){
            S_dict_data exist = this.codeService.queryOneDictData(s_dict_data.getDict_type(), s_dict_data.getDict_value());
            if(exist != null){
                return "已经存在该字典值,请重新输入";
            }
        }
        return null;
    }
}
iplatform-base-admin/src/main/java/com/iplatform/base/controller/ConfigController.java
New file
@@ -0,0 +1,175 @@
package com.iplatform.base.controller;
import com.iplatform.base.SystemController;
import com.iplatform.base.cache.FormCacheProvider;
import com.iplatform.base.pojo.ConfigParam;
import com.iplatform.base.pojo.form.FormData;
import com.iplatform.base.pojo.form.FormDataItem;
import com.iplatform.base.service.ConfigArgumentServiceImpl;
import com.iplatform.model.po.S_config;
import com.walker.db.page.GenericPager;
import com.walker.infrastructure.arguments.ArgumentsManager;
import com.walker.infrastructure.arguments.Variable;
import com.walker.infrastructure.utils.DateUtils;
import com.walker.infrastructure.utils.NumberGenerator;
import com.walker.infrastructure.utils.StringUtils;
import com.walker.web.ResponseValue;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
@RestController
@RequestMapping("/system/config")
public class ConfigController extends SystemController {
    private ConfigArgumentServiceImpl configArgumentService;
    private ArgumentsManager argumentsManager = null;
    private FormCacheProvider formCacheProvider;
    @Autowired
    public ConfigController(ConfigArgumentServiceImpl configArgumentService
            , ArgumentsManager argumentsManager, FormCacheProvider formCacheProvider){
        this.configArgumentService = configArgumentService;
        this.argumentsManager = argumentsManager;
        this.formCacheProvider = formCacheProvider;
    }
    @RequestMapping("/list")
    public ResponseValue list(ConfigParam configParam){
//        this.preparePageSearch();
        GenericPager<S_config> pager = this.configArgumentService.queryPageConfigList(configParam.getConfigName()
                , configParam.getConfigKey(), configParam.getConfigType());
//        return this.acquireTablePage(pager.getDatas(), pager.getTotalRows());
        // 缓存前端封装的table组件,可以直接返回分页对象。2023-04-11
        return ResponseValue.success(pager);
    }
    @RequestMapping("/view/{configId}")
    public ResponseValue view(@PathVariable Long configId){
        if(configId == null || configId.longValue() <= 0){
            return ResponseValue.error("config id required!");
        }
        S_config s_config = this.configArgumentService.get(new S_config(configId));
        return ResponseValue.success(s_config);
    }
    @RequestMapping("/edit")
    public ResponseValue saveEdit(@RequestBody S_config s_config){
        S_config exist = this.configArgumentService.get(new S_config(s_config.getConfig_id()));
        if(exist == null){
            return ResponseValue.error("配置项不存在: " + s_config.getConfig_id());
        }
        this.configArgumentService.save(s_config);
        this.argumentsManager.persist(s_config.getConfig_key(), s_config.getConfig_value());
        return ResponseValue.success();
    }
    /**
     * 添加保存新配置项。
     * @param s_config
     * @return
     */
    @RequestMapping("/add")
    public ResponseValue saveAdd(@RequestBody S_config s_config){
        if(s_config == null || StringUtils.isEmpty(s_config.getConfig_key())){
            return ResponseValue.error("提交配置数据错误");
        }
        S_config exist = this.configArgumentService.queryConfigByKey(s_config.getConfig_key());
        if(exist != null){
            return ResponseValue.error("配置项已经存在: " + s_config.getConfig_key());
        }
        s_config.setConfig_id(NumberGenerator.getSequenceNumber());
        s_config.setCreate_time(DateUtils.getDateTimeNumber(System.currentTimeMillis()));
        this.configArgumentService.insert(s_config);
        this.argumentsManager.persist(s_config.getConfig_key(), s_config.getConfig_value());
        return ResponseValue.success();
    }
    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    //~ 2023-05-16 新界面使用,电商系统。
    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    /**
     * 获得一个唯一配置项的值。
     * @param key
     * @return
     * @date 2023-05-15
     */
    @RequestMapping(value = "/getuniq", method = RequestMethod.GET)
    public ResponseValue getConfigValue(@RequestParam String key){
        String configValue = this.getArgumentVariable(key).getStringValue();
        return ResponseValue.success("success", configValue);
    }
    /**
     * 根据formId(分组)获得包含的所有配置项集合,包括:key,value
     * @param formId
     * @return
     * @date 2023-05-16
     */
    @RequestMapping(value = "/info", method = RequestMethod.GET)
    public ResponseValue info(@RequestParam(value = "formId") Integer formId){
        if(formId == null){
            return ResponseValue.error("参数错误");
        }
        List<Variable> list = this.argumentsManager.getVariableList(String.valueOf(formId));
        if(StringUtils.isEmptyList(list)){
            return ResponseValue.success(new HashMap<>(1));
        }
        HashMap<String, String> map = new HashMap<>(8);
        for (Variable systemConfig : list) {
            map.put(systemConfig.getId(), systemConfig.getStringValue());
        }
        map.put("id", formId.toString());
        if(this.logger.isDebugEnabled()){
            this.logger.debug(map.toString());
        }
        return ResponseValue.success(map);
    }
    @RequestMapping(value = "/save/form", method = RequestMethod.POST)
    public ResponseValue saveFormConfig(@RequestBody FormData formData){
        if(formData == null || formData.getId() == null || StringUtils.isEmptyList(formData.getFields())){
            return ResponseValue.error("参数错误");
        }
        try{
            this.formCacheProvider.validateForm(formData);
        } catch (Exception ex){
            return ResponseValue.error("表单配置数据格式错误:" + ex.getMessage());
        }
        List<Object[]> savedConfigList = new ArrayList<>(8);
        Object[] config = null;
        String value = null;
        for(FormDataItem formDataItem : formData.getFields()){
            value = this.clearCdnPrefix(formDataItem.getValue());
            if(StringUtils.isEmpty(value)){
                // 去掉图片域名之后没有数据则说明当前数据就是图片域名
                logger.debug("配置的值是:图片域名," + formDataItem.getValue());
                value = formDataItem.getValue();
            }
            config = new Object[2];
            config[0] = value;
            config[1] = formDataItem.getName();
            savedConfigList.add(config);
        }
        // 多个配置项同时更新
        this.configArgumentService.execUpdateFormConfig(savedConfigList);
        for(Object[] one : savedConfigList){
            this.argumentsManager.persist(one[1].toString(), one[0]);
        }
        return ResponseValue.success();
    }
}
iplatform-base-admin/src/main/java/com/iplatform/base/controller/ConfigFormController.java
New file
@@ -0,0 +1,127 @@
package com.iplatform.base.controller;
import com.iplatform.base.PlatformAdapterController;
import com.iplatform.base.PlatformRuntimeException;
import com.iplatform.base.cache.FormCacheProvider;
import com.iplatform.base.pojo.KeywordsParam;
import com.iplatform.base.pojo.form.RequestForm;
import com.iplatform.base.service.ConfigFormServiceImpl;
import com.iplatform.model.po.S_config_form;
import com.iplatform.model.vo.ConfigFormVo;
import com.walker.db.page.GenericPager;
import com.walker.infrastructure.utils.DateUtils;
import com.walker.infrastructure.utils.JsonUtils;
import com.walker.infrastructure.utils.StringUtils;
import com.walker.infrastructure.utils.UrlUtils;
import com.walker.web.ResponseValue;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
/**
 * 配置表单管理功能。
 * @author 时克英
 * @date 2023-05-16
 */
@RestController
@RequestMapping("/system/form")
public class ConfigFormController extends PlatformAdapterController {
    private ConfigFormServiceImpl configFormService;
    private FormCacheProvider formCacheProvider;
    @Autowired
    public ConfigFormController(ConfigFormServiceImpl configFormService, FormCacheProvider formCacheProvider){
        this.configFormService = configFormService;
        this.formCacheProvider = formCacheProvider;
    }
    @RequestMapping(value = "/update", method = RequestMethod.POST)
    public ResponseValue edit(@RequestParam Integer id, @RequestBody RequestForm requestForm){
        if(requestForm == null || StringUtils.isEmpty(requestForm.getContent()) || id == null){
            return ResponseValue.error("参数错误");
        }
        try {
            JsonUtils.jsonStringToObject(requestForm.getContent(), ConfigFormVo.class);
            S_config_form form = this.acquireConfigForm(requestForm, id);
            this.configFormService.save(form);
            this.formCacheProvider.updateCacheData(String.valueOf(form.getId()), form);
            logger.info("编辑表单模板成功:" + requestForm.getName());
        } catch (Exception e) {
            logger.error(requestForm.getContent());
            throw new PlatformRuntimeException("更新表单模板格式不正确:" + e.getMessage(), e);
        }
        return ResponseValue.success();
    }
    /**
     * 保存新创建表单模板。
     * @param requestForm
     * @return
     * @date 2023-05-23
     */
    @RequestMapping(value = "/save", method = RequestMethod.POST)
    public ResponseValue add(@RequestBody RequestForm requestForm){
        if(requestForm == null || StringUtils.isEmpty(requestForm.getContent())){
            return ResponseValue.error("参数错误");
        }
        try {
            JsonUtils.jsonStringToObject(requestForm.getContent(), ConfigFormVo.class);
//            S_config_form form = new S_config_form();
//            form.setName(requestForm.getName());
//            form.setInfo(requestForm.getInfo());
//            form.setContent(requestForm.getContent());
//            form.setCreate_time(DateUtils.getDateTimeNumber());
//            form.setUpdate_time(form.getCreate_time());
//            form.setId(this.configFormService.queryNextId());
            S_config_form form = this.acquireConfigForm(requestForm, this.configFormService.queryNextId());
            this.configFormService.insert(form);
            this.formCacheProvider.putCacheData(String.valueOf(form.getId()), form);
            logger.info("创建表单模板成功:" + requestForm.getName());
        } catch (Exception e) {
            logger.error(requestForm.getContent());
            throw new PlatformRuntimeException("创建的表单模板格式不正确:" + e.getMessage(), e);
        }
        return ResponseValue.success();
    }
    private S_config_form acquireConfigForm(RequestForm requestForm, int id){
        S_config_form form = new S_config_form();
        form.setName(requestForm.getName());
        form.setInfo(requestForm.getInfo());
        form.setContent(requestForm.getContent());
        form.setCreate_time(DateUtils.getDateTimeNumber());
        form.setUpdate_time(form.getCreate_time());
        form.setId(id);
        return form;
    }
    @RequestMapping(value = "/info", method = RequestMethod.GET)
    public ResponseValue info(@RequestParam(value = "id") Integer id){
//        S_config_form form = this.configFormService.get(new S_config_form(id));
        if(id == null || id.intValue() <= 0){
            return ResponseValue.error();
        }
        S_config_form form = this.formCacheProvider.getCacheData(id.toString());
        if(form == null){
            return ResponseValue.error("表单不存在, id=" + id);
        }
        return ResponseValue.success(form);
    }
    @RequestMapping(value = "/list", method = RequestMethod.GET)
    public ResponseValue list(KeywordsParam keywordsParam){
        String keywords = null;
        if(keywordsParam != null && StringUtils.isNotEmpty(keywordsParam.getKeywords())){
            keywords = UrlUtils.decode(keywordsParam.getKeywords());
        }
        GenericPager<S_config_form> pager = this.configFormService.queryPageFormList(keywords);
        return ResponseValue.success(pager);
    }
}
iplatform-base-admin/src/main/java/com/iplatform/base/controller/DeptController.java
New file
@@ -0,0 +1,283 @@
package com.iplatform.base.controller;
import com.iplatform.base.SystemController;
import com.iplatform.base.pojo.DeptParam;
import com.iplatform.base.service.DeptServiceImpl;
import com.iplatform.base.util.DeptUtils;
import com.iplatform.model.po.S_dept;
import com.iplatform.model.po.S_user_core;
import com.walker.infrastructure.tree.TreeNode;
import com.walker.infrastructure.utils.DateUtils;
import com.walker.infrastructure.utils.NumberGenerator;
import com.walker.infrastructure.utils.StringUtils;
import com.walker.web.DataStatus;
import com.walker.web.OrgType;
import com.walker.web.ResponseValue;
import com.walker.web.UserType;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
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 java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@RestController
@RequestMapping("/system/dept")
public class DeptController extends SystemController {
    private DeptServiceImpl deptService;
    @Autowired
    public DeptController(DeptServiceImpl deptService){
        this.deptService = deptService;
    }
    /**
     * 前端展示所有顶级单位列表,超级管理员可以选择所有机构,普通用户只能显示自己单位的。
     * @return
     * @date 2022-12-15
     */
    @GetMapping("/select/list_root_org")
    public ResponseValue listOrgRoot(){
        List<S_dept> rootList = this.getOrgListScope();
        return ResponseValue.success(rootList);
    }
    /**
     * 前端展示部门机构树(单个组织机构),在添加用户选部门使用。
     * @param deptId 提供一个部门ID(界面选择),该参数在超级管理员时使用。
     * @return
     * @date 2022-12-12
     */
    @GetMapping("/select/tree_dept/{deptId}")
    public ResponseValue listSelectDeptTree(@PathVariable Long deptId){
        long orgId = 0;
        if(this.isSupervisor()){
            if(deptId == null || deptId.longValue() <= 0){
                return ResponseValue.error("查询一个机构树,必须传入机构ID");
            }
            orgId = this.getRootOrgIdByDept(deptId);
        } else {
            orgId = this.getCurrentUser().getOrg_id();
        }
        List<S_dept> orgList = this.deptService.queryOrgListForTree(orgId);
        List<TreeNode> treeNodeList = DeptUtils.getOrgDeptTree(orgList);
        if(StringUtils.isEmptyList(treeNodeList)){
            // 2023-05-31 树组件不能数据为空
            treeNodeList = new ArrayList<>(1);
        }
        return ResponseValue.success(treeNodeList);
    }
    /**
     * 选择整个组织机构树,区分是否管理员。<p></p>
     * <pre>
     *     1)该功能是公共权限,system:dept:select:**
     *     2)展示所有机构树节点,如果是超级管理员则会显示所有根单位(集团)。
     *     3)普通用户只能看到本单位机构树。
     * </pre>
     * @return
     * @date 2022-12-08
     */
    @GetMapping("/select/tree_org")
    public ResponseValue listOrgRootTree(){
        List<S_dept> orgList = null;
        if(this.isSupervisor()){
            orgList = this.deptService.queryOrgListForTree(-1);
        } else {
           long orgId = this.getCurrentUser().getOrg_id();
           orgList = this.deptService.queryOrgListForTree(orgId);
        }
//        DeptTreeGenerator deptTreeGenerator = new DeptTreeGenerator(null);
//        deptTreeGenerator.setEntityList(orgList);
//        List<TreeNode> treeNodeList = deptTreeGenerator.getTreeRootList();
        List<TreeNode> treeNodeList = DeptUtils.getOrgDeptTree(orgList);
        if(StringUtils.isEmptyList(treeNodeList)){
            logger.error("未找到任何顶级机构树列表: treeNodeList = null");
        } else {
//            logger.debug(treeNodeList.toString());
        }
        return ResponseValue.success(treeNodeList);
    }
    @GetMapping("/list")
    public ResponseValue listOrgList(DeptParam deptParam){
        String deptDataScope = this.getCurrentDataScope("103");
        logger.info("部门管理(数据权限) = " + deptDataScope);
        Map<String, Object> data = new HashMap<>(4);
        if(deptParam.getLoadSelect() == 1){
            // 来自下拉机构选择框请求
            List<S_dept> selectParentList = this.deptService.queryRootOrgChildrenList(deptParam.getOrgId(), null);
            data.put("deptList", DeptUtils.toSystemDeptList(selectParentList));
            return ResponseValue.success(data);
        }
        List<S_dept> rootList = this.getOrgListScope();
        S_user_core currentUser = this.getCurrentUser();
        List<S_dept> deptSrcList = null;
        long selectedOrgRoot = 0;
        if(currentUser.getUser_type().intValue() == UserType.TYPE_SUPER){
            // 超级管理员,可以看到所有根单位列表
            if(deptParam != null && deptParam.getOrgId() > 0){
                // 管理员选择了一个根机构
                selectedOrgRoot = deptParam.getOrgId();
            } else if(!StringUtils.isEmptyList(rootList)){
                // 没有选择,则默认查询第一个根机构下面的
                selectedOrgRoot = rootList.get(0).getId();
            }
            deptSrcList = this.deptService.queryRootOrgChildrenList(selectedOrgRoot, deptParam.getDeptName());
            data.put("deptList", DeptUtils.toSystemDeptList(deptSrcList));
        } else {
            // 其他用户只能看到自己所在根机构下的所有机构
            deptSrcList = this.deptService.queryRootOrgChildrenList(currentUser.getOrg_id(), deptParam.getDeptName());
            data.put("deptList", DeptUtils.toSystemDeptList(deptSrcList));
        }
        // 记录超级管理员选择的顶级机构,返回前端。2022-12-03
        if(deptParam.getOrgId() > 0){
            data.put("selectedRootOrgId", deptParam.getOrgId());
        } else {
            data.put("selectedRootOrgId", rootList.get(0).getId());
        }
        data.put("rootOrgList", rootList);
        return ResponseValue.success(data);
    }
    @PostMapping("/add")
    public ResponseValue saveAddDept(@RequestBody S_dept s_dept){
        if(s_dept == null || StringUtils.isEmpty(s_dept.getDept_name())){
            return ResponseValue.error("提交保存机构为空");
        }
        logger.info(s_dept.toString());
        s_dept.setId(NumberGenerator.getSequenceNumber());
        s_dept.setCreate_time(DateUtils.getDateTimeNumber(System.currentTimeMillis()));
        s_dept.setCreate_by(this.getCurrentUser().getUser_name());
        Long parentId = s_dept.getParent_id();
        if(parentId == null || parentId.longValue() == 0){
            // 说明是根机构,根机构: id = org_id
            s_dept.setOrg_id(s_dept.getId());
            s_dept.setParent_id(0L);
            s_dept.setAncestors("0");
            if(s_dept.getOrg_type().intValue() != OrgType.TYPE_ORG){
                return ResponseValue.error("顶级机构只能选择第一项");
            }
        } else {
            // 普通机构
            S_dept parentDept = this.deptService.queryOneDept(parentId);
            if(parentDept == null){
                return ResponseValue.error("上级单位不存在");
            }
            if(parentDept.getStatus().intValue() != DataStatus.CONST_NORMAL){
                return ResponseValue.error("该机构已停用,无法创建子机构");
            }
            s_dept.setOrg_id(parentDept.getOrg_id());
            s_dept.setAncestors(parentDept.getAncestors() + "," + s_dept.getParent_id());
            String error = this.checkOrgType(parentDept.getOrg_type(), s_dept.getOrg_type());
            if(error != null){
                return ResponseValue.error(error);
            }
        }
        this.deptService.insert(s_dept);
//        this.deptCacheProvider.putDept(s_dept);
        this.getDeptCacheProvider().putDept(s_dept);
        return ResponseValue.success();
    }
    @RequestMapping("/remove/{deptId}")
    public ResponseValue removeDept(@PathVariable Long deptId){
        if(deptId == null || deptId <= 0){
            return ResponseValue.error("机构参数错误");
        }
        int subDeptSize = this.deptService.querySubDeptSizeInCurrent(deptId);
        if(subDeptSize > 0){
            return ResponseValue.error("该机构下存在子机构,无法删除");
        }
        long userSize = this.deptService.queryUserSizeInCurrent(deptId);
        if(userSize > 0){
            return ResponseValue.error("该机构下存在用户,无法删除");
        }
        this.deptService.delete(new S_dept(deptId));
//        this.deptCacheProvider.removeDept(deptId);
        this.getDeptCacheProvider().removeDept(deptId);
        return ResponseValue.success();
    }
    @PostMapping("/edit")
    public ResponseValue saveEdit(@RequestBody S_dept s_dept){
        if(s_dept == null){
            return ResponseValue.error("编辑的机构不存在");
        }
        Long deptId = s_dept.getId();
        if(deptId == null || deptId.longValue() <= 0){
            return ResponseValue.error("编辑的机构ID不存在");
        }
        Long parentId = s_dept.getParent_id();
        if(parentId == null || parentId.longValue() == 0){
            // 说明编辑的是顶级机构,根机构: id = org_id
            if(s_dept.getOrg_type().intValue() != OrgType.TYPE_ORG){
                return ResponseValue.error("顶级机构只能选择一级机构");
            }
        } else {
            // 普通机构
            S_dept parentDept = this.deptService.queryOneDept(parentId);
            if(parentDept == null){
                return ResponseValue.error("上级单位不存在");
            }
            String error = this.checkOrgType(parentDept.getOrg_type(), s_dept.getOrg_type());
            if(error != null){
                return ResponseValue.error(error);
            }
        }
        s_dept.setCreate_time(DateUtils.getDateTimeNumber(System.currentTimeMillis()));
        this.deptService.save(s_dept);
//        this.deptCacheProvider.updateDept(s_dept);
        this.getDeptCacheProvider().updateDept(s_dept);
        return ResponseValue.success();
    }
    @GetMapping("/view/{deptId}")
    public ResponseValue viewDept(@PathVariable Long deptId){
        if(deptId == null || deptId.longValue() <= 0){
            return ResponseValue.error("参数错误");
        }
        S_dept s_dept = this.deptService.queryOneDept(deptId);
        if(s_dept == null){
            return ResponseValue.error("机构不存在");
        }
        return ResponseValue.success(s_dept);
    }
    private String checkOrgType(int parentOrgTypeValue, int currentOrgTypeValue){
        OrgType parentOrgType = OrgType.getType(parentOrgTypeValue);
        OrgType currentOrtType = OrgType.getType(currentOrgTypeValue);
        if(currentOrtType == OrgType.OrgSub){
            if(parentOrgType != OrgType.Org){
                return "二级单位只能在顶级单位下面";
            }
        } else if (currentOrtType == OrgType.OrgFactory) {
            if(parentOrgType != OrgType.OrgSub){
                return "三级单位只能在二级单位下面";
            }
        } else if (currentOrtType == OrgType.OrgSubFactory) {
            if(parentOrgType != OrgType.OrgFactory){
                return "四级单位只能在三级级单位下面";
            }
        }
        return null;
    }
}
iplatform-base-admin/src/main/java/com/iplatform/base/controller/GroupController.java
New file
@@ -0,0 +1,164 @@
package com.iplatform.base.controller;
import com.iplatform.base.PlatformAdapterController;
import com.iplatform.base.PlatformRuntimeException;
import com.iplatform.base.cache.FormCacheProvider;
import com.iplatform.base.pojo.GroupDataParam;
import com.iplatform.base.pojo.KeywordsParam;
import com.iplatform.base.pojo.form.FormData;
import com.iplatform.base.pojo.group.GroupData;
import com.iplatform.base.service.GroupServiceImpl;
import com.iplatform.model.po.S_group;
import com.iplatform.model.po.S_group_data;
import com.walker.db.page.GenericPager;
import com.walker.infrastructure.utils.DateUtils;
import com.walker.infrastructure.utils.JsonUtils;
import com.walker.infrastructure.utils.StringUtils;
import com.walker.infrastructure.utils.UrlUtils;
import com.walker.web.ResponseValue;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
 * 平台分组数据管理。
 * <p>实现配置功能块能包含哪些内容项,如:手机首页导航栏可配置等。</p>
 * @author 时克英
 * @date 2023-05-20
 */
@RestController
@RequestMapping("/system/group")
public class GroupController extends PlatformAdapterController {
    @Autowired
    public GroupController(GroupServiceImpl groupService, FormCacheProvider formCacheProvider){
        this.groupService = groupService;
        this.formCacheProvider = formCacheProvider;
    }
    @RequestMapping(value = "/update", method = RequestMethod.POST)
    public ResponseValue updateGroup(
//            @RequestParam Integer id,
            @RequestBody S_group group){
        if(group == null || StringUtils.isEmpty(group.getName()) || group.getId() == null){
            return ResponseValue.error("参数错误");
        }
//        group.setId(id);
        group.setUpdate_time(DateUtils.getDateTimeNumber());
        this.groupService.save(group);
        return ResponseValue.success();
    }
    @RequestMapping(value = "/delete", method = RequestMethod.GET)
    public ResponseValue deleteGroup(Integer id){
        if(id == null){throw new IllegalArgumentException("缺少参数");}
        S_group_data groupData = new S_group_data();
        groupData.setGid(id);
        List<S_group_data> groupDataList = this.groupService.select(groupData);
        if(!StringUtils.isEmptyList(groupDataList)){
            return ResponseValue.error("分组下存在数据项,无法删除!");
        }
        this.groupService.delete(new S_group(id));
        return ResponseValue.success();
    }
    @RequestMapping(value = "/save", method = RequestMethod.POST)
    public ResponseValue saveGroup(@RequestBody S_group group){
        if(group == null){
            return ResponseValue.error("参数错误");
        }
        group.setCreate_time(DateUtils.getDateTimeNumber());
        group.setUpdate_time(group.getCreate_time());
        group.setId(this.groupService.queryGroupNextId());
        this.groupService.insert(group);
        return ResponseValue.success();
    }
    @RequestMapping(value = "/data/delete", method = RequestMethod.GET)
    public ResponseValue deleteData(@RequestParam(value = "id") Integer id){
        this.groupService.delete(new S_group_data(id));
        return ResponseValue.success();
    }
    @RequestMapping(value = "/data/update", method = RequestMethod.POST)
    public ResponseValue updateData(@RequestParam Integer id, @RequestBody GroupData groupData){
        if(groupData == null || id == null){
            return ResponseValue.error("参数错误");
        }
        String value = this.acquireFormValue(groupData.getForm());
        S_group_data group_data = new S_group_data();
        group_data.setGid(groupData.getGid());
        group_data.setValue(this.clearCdnPrefix(value));
        group_data.setSort(groupData.getForm().getSort());
        group_data.setStatus(groupData.getForm().getStatus());
        group_data.setUpdate_time(group_data.getCreate_time());
        group_data.setId(id);
        this.groupService.save(group_data);
        return ResponseValue.success();
    }
//    // @ApiOperation(value = "新增:分组列表项")
    @RequestMapping(value = "/data/save", method = RequestMethod.POST)
    public ResponseValue saveData(@RequestBody GroupData groupData){
        if(groupData == null){
            return ResponseValue.error("参数错误");
        }
        String value = this.acquireFormValue(groupData.getForm());
        int nextId = this.groupService.queryGroupDataNextId();
        S_group_data group_data = new S_group_data();
        group_data.setGid(groupData.getGid());
        group_data.setValue(this.clearCdnPrefix(value));
        group_data.setSort(groupData.getForm().getSort());
        group_data.setStatus(groupData.getForm().getStatus());
        group_data.setCreate_time(DateUtils.getDateTimeNumber());
        group_data.setUpdate_time(group_data.getCreate_time());
        group_data.setId(nextId);
        this.groupService.insert(group_data);
        return ResponseValue.success();
    }
    private String acquireFormValue(FormData formData){
        this.formCacheProvider.validateForm(formData);
        try {
            return JsonUtils.objectToJsonString(formData);
        } catch (Exception e) {
            throw new PlatformRuntimeException("转换Json字符串失败:" + formData.getId(), e);
        }
    }
    @RequestMapping(value = "/data/list", method = RequestMethod.GET)
    public ResponseValue listData(GroupDataParam groupDataParam){
        if(groupDataParam == null || groupDataParam.getGid() == null){
            throw new IllegalArgumentException("参数错误:groupDataParam");
        }
        GenericPager<S_group_data> pager = this.groupService.queryPageGroupDataList(groupDataParam.getGid(), groupDataParam.getStatus());
        pager.getDatas().stream().forEach(data -> {
            try {
                // 把value转成Json,重新格式化一遍,返回前端。
                FormData formData = JsonUtils.jsonStringToObject(data.getValue(), FormData.class);
                data.setValue(JsonUtils.objectToJsonString(formData));
                logger.info(data.getValue());
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        });
        return ResponseValue.success(pager);
    }
    @RequestMapping(value = "/list", method = RequestMethod.GET)
    public ResponseValue list(KeywordsParam keywordsParam){
        String keywords = null;
        if(keywordsParam != null && StringUtils.isNotEmpty(keywordsParam.getKeywords())){
            keywords = UrlUtils.decode(keywordsParam.getKeywords());
        }
        GenericPager<S_group> pager = this.groupService.queryPageGroupList(keywords);
        return ResponseValue.success(pager);
    }
    private GroupServiceImpl groupService;
    private FormCacheProvider formCacheProvider;
}
iplatform-base-admin/src/main/java/com/iplatform/base/controller/LoginInfoController.java
New file
@@ -0,0 +1,47 @@
package com.iplatform.base.controller;
import com.iplatform.base.SystemController;
import com.iplatform.base.pojo.log.LoginLogParam;
import com.iplatform.base.service.LogServiceImpl;
import com.iplatform.model.po.S_login_info;
import com.walker.db.page.GenericPager;
import com.walker.web.ResponseValue;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
 * 登录日志查询。
 * @date 2023-01-04
 */
@RestController
@RequestMapping("/monitor/login_info")
public class LoginInfoController extends SystemController {
    private LogServiceImpl logService;
    @Autowired
    public LoginInfoController(LogServiceImpl logService){
        this.logService = logService;
    }
    @RequestMapping("/select/clean")
    public ResponseValue clear(){
        this.logService.execClearLoginLog();
        return ResponseValue.success();
    }
    @RequestMapping("/list")
    public ResponseValue list(LoginLogParam loginLogParam){
//        this.preparePageSearch();
        GenericPager<S_login_info> pager = null;
        if(loginLogParam == null){
            pager = this.logService.queryPageLoginLogList(null, null, null);
        } else {
            pager = this.logService.queryPageLoginLogList(loginLogParam.getIpaddr()
                    , loginLogParam.getUserName(), loginLogParam.getStatus());
        }
//        return this.acquireTablePage(pager.getDatas(), pager.getTotalRows());
        return ResponseValue.success(pager);
    }
}
iplatform-base-admin/src/main/java/com/iplatform/base/controller/MenuController.java
New file
@@ -0,0 +1,154 @@
package com.iplatform.base.controller;
import com.iplatform.base.SystemController;
import com.iplatform.base.cache.MenuCacheProvider;
import com.iplatform.base.service.MenuServiceImpl;
import com.iplatform.base.service.RoleServiceImpl;
import com.iplatform.base.util.MenuUtils;
import com.iplatform.base.util.menu.SystemMenu;
import com.iplatform.model.po.S_menu;
import com.iplatform.model.po.S_user_core;
import com.walker.infrastructure.utils.DateUtils;
import com.walker.infrastructure.utils.StringUtils;
import com.walker.web.DataStatus;
import com.walker.web.ResponseValue;
import com.walker.web.UserPrincipal;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@RestController
@RequestMapping("/system/menu")
public class MenuController extends SystemController {
    private MenuCacheProvider menuCacheProvider;
    private RoleServiceImpl roleService;
    private MenuServiceImpl menuService;
    @Autowired
    public MenuController(MenuCacheProvider menuCacheProvider
            , RoleServiceImpl roleService, MenuServiceImpl menuService){
        this.menuCacheProvider = menuCacheProvider;
        this.roleService = roleService;
        this.menuService = menuService;
    }
    @RequestMapping("/remove/{menuId}")
    public ResponseValue remove(@PathVariable String menuId){
        boolean existChildren = this.menuCacheProvider.isHasChildren(menuId);
        if(existChildren){
            return ResponseValue.error("该菜单存在子菜单,无法删除");
        }
        int roleMenuSize = this.menuService.queryRoleMenuSize(menuId);
        if(roleMenuSize > 0){
            return ResponseValue.error("菜单已分配,无法删除");
        }
        this.menuService.delete(new S_menu(menuId));
        this.menuCacheProvider.removeCacheData(menuId);
        return ResponseValue.success();
    }
    @RequestMapping("/view/{menuId}")
    public ResponseValue view(@PathVariable String menuId){
        S_menu s_menu = this.menuService.get(new S_menu(menuId));
        if(s_menu == null){
            return ResponseValue.error("菜单不存在:" + menuId);
        }
        return ResponseValue.success(new SystemMenu(s_menu));
    }
    @RequestMapping("/edit")
    public ResponseValue saveEdit(@RequestBody S_menu s_menu){
        if(s_menu == null || StringUtils.isEmpty(s_menu.getParent_id()) || StringUtils.isEmpty(s_menu.getMenu_name())){
            return ResponseValue.error("参数错误");
        }
        S_menu exist = this.menuService.queryExistMenuInParent(s_menu.getParent_id(), s_menu.getMenu_name());
        if(exist != null && !exist.getMenu_id().equals(s_menu.getMenu_id())){
            return ResponseValue.error("父菜单下已有相同名称菜单存在:" + s_menu.getMenu_name());
        }
        if(s_menu.getIs_frame().intValue() == MenuUtils.MENU_FRAME_YES && !StringUtils.isHttpLink(s_menu.getPath())){
            return ResponseValue.error("地址必须以http(s)://开头");
        }
        if(s_menu.getMenu_type().equals(MenuUtils.MENU_TYPE_ITEM) && StringUtils.isEmpty(s_menu.getComponent())){
            return ResponseValue.error("菜单项必须填写:组件路径(component)");
        }
        this.menuService.save(s_menu);
        this.menuCacheProvider.updateCacheData(s_menu.getMenu_id(), s_menu);
        return ResponseValue.success();
    }
    @RequestMapping("/add")
    public ResponseValue saveAddMenu(@RequestBody S_menu s_menu){
        if(s_menu == null || StringUtils.isEmpty(s_menu.getParent_id()) || StringUtils.isEmpty(s_menu.getMenu_name())){
            return ResponseValue.error("参数错误");
        }
        S_menu exist = this.menuService.queryExistMenuInParent(s_menu.getParent_id(), s_menu.getMenu_name());
        if(exist != null){
            return ResponseValue.error("父菜单下已有相同名称菜单存在:" + s_menu.getMenu_name());
        }
        if(s_menu.getIs_frame().intValue() == MenuUtils.MENU_FRAME_YES && !StringUtils.isHttpLink(s_menu.getPath())){
            return ResponseValue.error("地址必须以http(s)://开头");
        }
        if(s_menu.getMenu_type().equals(MenuUtils.MENU_TYPE_ITEM) && StringUtils.isEmpty(s_menu.getComponent())){
            return ResponseValue.error("菜单项必须填写:组件路径(component)");
        }
        s_menu.setMenu_id(DateUtils.getDateTimeSecondForShow());
        this.menuService.insert(s_menu);
        S_menu saved = this.menuService.get(new S_menu(s_menu.getMenu_id()));
        this.menuCacheProvider.putCacheData(s_menu.getMenu_id(), saved);
        return ResponseValue.success();
    }
    /**
     * 角色编辑时,显示已经设置的功能树结构目录。
     * @param roleId
     * @return
     * @date 2022-12-19
     */
    @RequestMapping("/select/roleMenuTree/{roleId}")
    public ResponseValue selectRoleMenuTree(@PathVariable Long roleId){
        if(roleId == null || roleId.longValue() <= 0){
            throw new IllegalArgumentException("角色参数不存在");
        }
        Map<String, Object> data = new HashMap<>(4);
        List<String> roleMenuIds = this.roleService.queryRoleMenuIdList(roleId);
        int menuScope = this.getCurrentOrgMenuScope();
        List<SystemMenu> menuTreeList = this.menuCacheProvider.getMenuTreeAll(null, true, menuScope);
        data.put("checkedKeys", roleMenuIds);
        data.put("menus", menuTreeList);
        return ResponseValue.success(data);
    }
    /**
     * 返回整个系统菜单树结构数据,目前前端角色设置中使用选择关联功能权限。
     * @return
     * @date 2022-12-18
     */
    @RequestMapping("/select/tree")
    public ResponseValue selectMenuTree(){
        int menuScope = this.getCurrentOrgMenuScope();
        List<SystemMenu> menuTreeList = this.menuCacheProvider.getMenuTreeAll(null, true, menuScope);
        return ResponseValue.success(menuTreeList);
    }
    @GetMapping("/list")
    public ResponseValue listMenuWithoutTree(){
        List<SystemMenu> menuList = null;
        UserPrincipal<S_user_core> userPrincipal = this.getCurrentUserPrincipal();
        if(this.isSupervisor()){
            menuList = this.menuCacheProvider.getMenuList(null, MenuUtils.MENU_SCOPE_ALL);
        } else {
            int menuScope = this.getCurrentOrgMenuScope();
            menuList = this.menuCacheProvider.getMenuList(userPrincipal.getRoleIdList(), menuScope);
        }
        return ResponseValue.success(menuList);
    }
}
iplatform-base-admin/src/main/java/com/iplatform/base/controller/NotificationController.java
New file
@@ -0,0 +1,133 @@
package com.iplatform.base.controller;
import com.iplatform.base.Constants;
import com.iplatform.base.NotifyConstants;
import com.iplatform.base.SystemController;
import com.iplatform.base.pojo.notify.InfoParam;
import com.iplatform.base.pojo.notify.NotificationParam;
import com.iplatform.base.service.NotificationServiceImpl;
import com.iplatform.base.util.NotificationUtils;
import com.iplatform.model.po.SfNotification;
import com.iplatform.model.po.SfTemplateMessage;
import com.iplatform.model.vo.NotificationConfigVo;
import com.walker.web.ResponseValue;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
/**
 * 消息通知管理,该通知仅是针对业务通知分类管理,同时能实现同步微信消息模板。
 * <pre>
 *     1)目前微信公众号消息,依赖该功能中的同步模板记录
 *     2)平台推送消息列表,请在"消息推送记录" 中查询。
 * </pre>
 * @author 时克英
 * @date 2023-08-24
 */
@RestController
@RequestMapping("/platform/system/notification")
public class NotificationController extends SystemController {
    private NotificationServiceImpl notificationService;
    @Autowired
    public NotificationController(NotificationServiceImpl notificationService){
        this.notificationService = notificationService;
    }
    @RequestMapping(value = "/sms/switch", method = RequestMethod.POST)
    public ResponseValue smsSwitch(Long id){
        SfNotification notification = this.notificationService.get(new SfNotification(id));
        if (notification.getIsSms().intValue() == NotifyConstants.SWITCH_NOT_EXIST) {
            return ResponseValue.error("通知没有配置短信");
        }
        if(notification.getIsSms().intValue() == NotifyConstants.SWITCH_OPEN){
            notification.setIsSms(NotifyConstants.SWITCH_COLSE);
        } else {
            notification.setIsSms(NotifyConstants.SWITCH_OPEN);
        }
        this.notificationService.update(notification);
        return ResponseValue.success();
    }
    @RequestMapping(value = "/routine/switch", method = RequestMethod.POST)
    public ResponseValue routineSwitch(Long id){
        SfNotification notification = this.notificationService.get(new SfNotification(id));
        if (notification.getIsRoutine().intValue() == NotifyConstants.SWITCH_NOT_EXIST) {
            return ResponseValue.error("通知没有配置小程序订阅模板");
        }
        if(notification.getIsRoutine().intValue() == NotifyConstants.SWITCH_OPEN){
            notification.setIsRoutine(NotifyConstants.SWITCH_COLSE);
        } else {
            notification.setIsRoutine(NotifyConstants.SWITCH_OPEN);
        }
        this.notificationService.update(notification);
        return ResponseValue.success();
    }
    @RequestMapping(value = "/wechat/switch", method = RequestMethod.POST)
    public ResponseValue wechatSwitch(Long id){
        SfNotification notification = this.notificationService.get(new SfNotification(id));
        if (notification.getIsWechat().intValue() == NotifyConstants.SWITCH_NOT_EXIST) {
            return ResponseValue.error("通知没有配置公众号模板");
        }
        if(notification.getIsWechat().intValue() == NotifyConstants.SWITCH_OPEN){
            notification.setIsWechat(NotifyConstants.SWITCH_COLSE);
        } else {
            notification.setIsWechat(NotifyConstants.SWITCH_OPEN);
        }
        this.notificationService.update(notification);
        return ResponseValue.success();
    }
    @RequestMapping(value = "/detail", method = RequestMethod.GET)
    public ResponseValue detail(InfoParam request){
        if(request == null || request.getId() == null){
            return ResponseValue.error(Constants.ERROR_ARGUMENT);
        }
        long id = request.getId();
        SfNotification notification = this.notificationService.get(new SfNotification(id));
        NotificationConfigVo vo = new NotificationConfigVo();
        SfTemplateMessage templateMessage = null;
        if (request.getDetailType().equals(NotifyConstants.DETAIL_TYPE_WECHAT)) {
            if (notification.getIsWechat().intValue() == NotifyConstants.SWITCH_NOT_EXIST) {
                return ResponseValue.error("请先配置公众号模板消息");
            }
            templateMessage = this.notificationService.get(new SfTemplateMessage(notification.getWechatId().longValue()));
            vo = NotificationUtils.acquireNotificationConfigVo(templateMessage);
            vo.setStatus(notification.getIsWechat());
        } else if(request.getDetailType().equals(NotifyConstants.DETAIL_TYPE_ROUTINE)){
            if (notification.getIsRoutine().intValue() == NotifyConstants.SWITCH_NOT_EXIST) {
                return ResponseValue.error("请先配置小程序订阅消息");
            }
            templateMessage = this.notificationService.get(new SfTemplateMessage(notification.getRoutineId().longValue()));
            vo = NotificationUtils.acquireNotificationConfigVo(templateMessage);
            vo.setStatus(notification.getIsRoutine());
        } else if(request.getDetailType().equals(NotifyConstants.DETAIL_TYPE_SMS)){
            if (notification.getIsSms().intValue() == NotifyConstants.SWITCH_NOT_EXIST) {
                return ResponseValue.error("请先配置短信模板");
            }
            templateMessage = this.notificationService.get(new SfTemplateMessage(notification.getSmsId().longValue()));
            vo = NotificationUtils.acquireNotificationConfigVo(templateMessage);
            vo.setStatus(notification.getIsSms());
        }
        return ResponseValue.success(vo);
    }
    @RequestMapping(value = "/list", method = RequestMethod.GET)
    public ResponseValue list(NotificationParam param){
        List<SfNotification> data = null;
        if(param == null){
            data = this.notificationService.queryList(null);
        } else {
            data = this.notificationService.queryList(param.getSendType());
        }
        return ResponseValue.success(data);
    }
}
iplatform-base-admin/src/main/java/com/iplatform/base/controller/OnlineUserController.java
New file
@@ -0,0 +1,48 @@
package com.iplatform.base.controller;
import com.iplatform.base.SystemController;
import com.iplatform.base.util.UserUtils;
import com.walker.cache.AbstractCacheProvider;
import com.walker.web.ResponseValue;
import com.walker.web.UserOnlineProvider;
import com.walker.web.UserPrincipal;
import com.walker.web.WebRuntimeException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
@RestController
@RequestMapping("/monitor/online")
public class OnlineUserController extends SystemController {
    private static final int MAX_DATA_SIZE = 64;
    private UserOnlineProvider userOnlineProvider;
    @Autowired
    public OnlineUserController(UserOnlineProvider userOnlineProvider){
        this.userOnlineProvider = userOnlineProvider;
    }
    @RequestMapping("/list")
    public ResponseValue list(){
        AbstractCacheProvider cacheProvider = (AbstractCacheProvider)this.userOnlineProvider;
        Collection<Object> list = cacheProvider.queryListLimit(MAX_DATA_SIZE);
        List<UserPrincipal> data = new ArrayList<>();
        if(list != null && list.size() > 0){
            try {
                for(Object up : list){
//                    logger.debug(up.toString());
                    data.add(UserUtils.toUserPrincipal(up.toString()));
                }
            } catch (Exception ex){
                throw new WebRuntimeException("在线用户缓存对象转换错误:");
            }
        }
        return this.acquireTablePage(data, data.size());
    }
}
iplatform-base-admin/src/main/java/com/iplatform/base/controller/OperateLogController.java
New file
@@ -0,0 +1,57 @@
package com.iplatform.base.controller;
import com.iplatform.base.SystemController;
import com.iplatform.base.pojo.log.OperateLogParam;
import com.iplatform.base.service.LogServiceImpl;
import com.iplatform.model.po.S_oper_log;
import com.walker.db.page.GenericPager;
import com.walker.infrastructure.utils.KeyValue;
import com.walker.web.ResponseValue;
import com.walker.web.log.BusinessType;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.ArrayList;
import java.util.List;
@RestController
@RequestMapping("/monitor/operate_log")
public class OperateLogController extends SystemController {
    private LogServiceImpl logService;
    @Autowired
    public OperateLogController(LogServiceImpl logService){
        this.logService = logService;
    }
    @RequestMapping("/select/get_business_type")
    public ResponseValue getBusinessTypeList(){
        List<KeyValue<Integer, String>> data = new ArrayList<>();
        for(BusinessType businessType : BusinessType.values()){
            data.add(new KeyValue<>(businessType.getIndex(), businessType.getName()));
        }
        return ResponseValue.success(data);
    }
    @RequestMapping("/select/clean")
    public ResponseValue clear(){
        this.logService.execClearOperateLog();
        return ResponseValue.success();
    }
    @RequestMapping("/list")
    public ResponseValue list(OperateLogParam operateLogParam){
//        this.preparePageSearch();
        GenericPager<S_oper_log> pager = null;
        if(operateLogParam == null){
            pager = this.logService.queryPageOperateLogList(null, null, null, null);
        } else {
            pager = this.logService.queryPageOperateLogList(operateLogParam.getOperName()
                    , operateLogParam.getBusinessType(), operateLogParam.getStatus(), operateLogParam.getTitile());
        }
//        return this.acquireTablePage(pager.getDatas(), pager.getTotalRows());
        return ResponseValue.success(pager);
    }
}
iplatform-base-admin/src/main/java/com/iplatform/base/controller/PermitController.java
New file
@@ -0,0 +1,128 @@
package com.iplatform.base.controller;
import com.iplatform.base.ArgumentsConstants;
import com.iplatform.base.SystemController;
import com.iplatform.base.service.CodeServiceImpl;
import com.iplatform.base.support.DictTreeGenerator;
import com.iplatform.model.po.S_dict_data;
import com.walker.file.FileInfo;
import com.walker.infrastructure.tree.TreeNode;
import com.walker.infrastructure.utils.StringUtils;
import com.walker.web.ResponseValue;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
/**
 * 公共权限操作方法。<p></p>
 * 在该对象里面的方法,都无需配置菜单权限,只要登录用户都有权使用。
 * <pre>
 *     1)比如:获取字典树等
 * </pre>
 * @author 时克英
 * @date 2023-03-13
 */
@RestController
@RequestMapping("/permit")
public class PermitController extends SystemController {
//    @RequestMapping("/copyright/get/info")
//    public ResponseValue getCopyRight(){
//        CopyRightVo copyRightVo = new CopyRightVo();
//        copyRightVo.setCompanyName(this.getArgumentVariable(ArgumentsConstants.CONFIG_COPYRIGHT_COMPANY_INFO).getStringValue());
//        copyRightVo.setCompanyImage(this.getArgumentVariable(ArgumentsConstants.CONFIG_COPYRIGHT_COMPANY_IMAGE).getStringValue());
//        return ResponseValue.success(copyRightVo);
//    }
    /**
     * 获取移动端访问站点域名(地址),如:localhost:8080
     * @return
     * @date 2023-05-12
     */
    @RequestMapping("/config/front/domain")
    public ResponseValue getConfigFrontDomain(){
        String data = this.getArgumentVariable(ArgumentsConstants.CONFIG_KEY_SITE_URL).getStringValue();
        return ResponseValue.success("success", data);
    }
    /**
     * 根据文件id下载本地文件
     * @param id 文件id(系统存储的唯一编号)
     * @throws Exception
     * @date 2023-03-13
     */
    @RequestMapping("/file/{id}")
    public void downloadLocalFile(@PathVariable String id) throws Exception{
        FileInfo fileInfo = this.getFileInfo(id);
        if(fileInfo == null){
            throw new IllegalArgumentException("文件未找到,id=" + id);
        }
        logger.info("开始下载本地文件:{}", fileInfo);
        this.downloadSimpleFile(this.getLocalFileData(fileInfo), fileInfo.getFileName());
    }
    /**
     * 根据代码表名字,查询包含的代码项集合。
     * @param dictType
     * @return
     * @date 2023-03-22 该接口在字典管理中存在,这里需要放在公共权限里面一份,方便所有用户查询某个字典类型下的所有项目集合。
     * @date 2023-04-15 使用缓存,这里只能返回一级的情况,如果存在树结构则需要调用方法,参考 API:{@linkplain PermitController#selectDictTreeList(String)}
     */
    @RequestMapping("/dict/data/type/{dictType}")
    public ResponseValue<List<S_dict_data>> dictTypeList(@PathVariable String dictType){
        logger.debug("dictType = " + dictType);
        String id = this.getDictCacheProvider().getDictTypeId(dictType);
        List<S_dict_data> list = this.getDictCacheProvider().getRootChildrenOneLevelList(id);
//        List<S_dict_data> list = this.codeService.queryDictDataByType(dictType);
        if(StringUtils.isEmptyList(list)){
            list = new ArrayList<S_dict_data>(2);
        }
        list.sort(new Comparator<S_dict_data>() {
            @Override
            public int compare(S_dict_data stu1, S_dict_data stu2) {
                return stu1.getDict_sort() - stu2.getDict_sort();
            }
        });
        return ResponseValue.success(list);
    }
    /**
     * 返回代码树结构(列表集合),由前端展示组装。
     * @param dictType 数据字典类型
     * @return
     * @auth 时克英
     * @date 2023-03-13
     */
    @RequestMapping("/dict/list_tree/{dictType}")
    public ResponseValue selectDictTreeList(@PathVariable String dictType){
        if(StringUtils.isEmpty(dictType)){
            return ResponseValue.error("请提供字典类型!");
        }
        List<TreeNode> treeNodeList = new ArrayList<>(8);
//        this.getDictCacheProvider().getCodeChildrenList()
        List<S_dict_data> dictDataList = this.codeService.queryDictTreeList(dictType);
        if(StringUtils.isEmptyList(dictDataList)){
            return ResponseValue.success(treeNodeList);
        }
        // s_dict_data表:第一个根节点的'parent_id'
        // TreeGenerator组件使用的默认父id=0,所以对于字典来说,父id不是0可能是其他数值,所以这里需要先设置。
        long defaultParentId = dictDataList.get(0).getParent_id();
        DictTreeGenerator generator = new DictTreeGenerator(null);
        generator.setDefaultParentId(defaultParentId);
        generator.setEntityList(dictDataList);
        return ResponseValue.success(generator.getTreeRootList());
    }
    @Autowired
    public PermitController(CodeServiceImpl codeService){
        this.codeService = codeService;
    }
    private CodeServiceImpl codeService;
}
iplatform-base-admin/src/main/java/com/iplatform/base/controller/RoleController.java
New file
@@ -0,0 +1,189 @@
package com.iplatform.base.controller;
import com.iplatform.base.SystemController;
import com.iplatform.base.event.RoleSecurityChangeEvent;
import com.iplatform.base.pojo.role.RoleAuthParam;
import com.iplatform.base.pojo.role.RoleParam;
import com.iplatform.base.pojo.role.RoleUserParam;
import com.iplatform.base.service.RoleServiceImpl;
import com.iplatform.base.util.role.SystemRole;
import com.iplatform.core.BeanContextAware;
import com.iplatform.model.po.S_role;
import com.iplatform.model.po.S_user_core;
import com.walker.db.page.GenericPager;
import com.walker.infrastructure.utils.NumberGenerator;
import com.walker.infrastructure.utils.StringUtils;
import com.walker.web.ResponseValue;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
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;
@RestController
@RequestMapping("/system/role")
public class RoleController extends SystemController {
    private RoleServiceImpl roleService;
    @Autowired
    public RoleController(RoleServiceImpl roleService){
        this.roleService = roleService;
    }
    /**
     * 批量添加授权用户。
     * @param roleId 角色ID
     * @param userIds 用户ID集合
     * @return
     * @date 2022-12-21
     */
    @RequestMapping("/select/authUser/all")
    public ResponseValue batchAuthUserList(Long roleId, Long[] userIds){
        if(userIds == null || userIds.length == 0 || roleId == null){
            return ResponseValue.error("参数错误");
        }
        logger.debug(roleId + ", " + userIds);
        this.roleService.execInsertRoleUserList(roleId, userIds);
        return ResponseValue.success();
    }
    /**
     * 取消已授权用户,从角色里移除。
     * @param roleUserParam
     * @return
     * @date 2022-12-21
     */
    @RequestMapping("/select/authUser/cancel")
    public ResponseValue cancelAuthUser(@RequestBody RoleUserParam roleUserParam){
        if(roleUserParam == null || roleUserParam.getRoleId() == null || roleUserParam.getUserId() == null){
            return ResponseValue.error("参数错误");
        }
        this.roleService.execDeleteRoleUser(roleUserParam.getRoleId(), roleUserParam.getUserId());
        return ResponseValue.success();
    }
    @RequestMapping("/select/authUser/unallocatedList")
    public ResponseValue unallocatedList(RoleAuthParam roleAuthParam){
        if(roleAuthParam == null || roleAuthParam.getRoleId() == null){
            return ResponseValue.error();
        }
        // 2023-01-28 这个由于url地址设置不合理,没能匹配: /list 所以暂时不能删除
        this.preparePageSearch();
        S_role s_role = this.roleService.get(new S_role(roleAuthParam.getRoleId()));
        GenericPager<S_user_core> pager = this.roleService.queryUnAllocatedUserList(roleAuthParam.getRoleId(), s_role.getOrg_id(), roleAuthParam.getUserName());
//        return this.acquireTablePage(pager.getDatas(), pager.getTotalRows());
        return ResponseValue.success(pager);
    }
    /**
     * 展示给定角色,已关联用户列表。
     * @param roleAuthParam
     * @return
     * @date 2022-12-21
     */
    @RequestMapping("/select/authUser/allocatedList")
    public ResponseValue allocatedList(RoleAuthParam roleAuthParam){
        if(roleAuthParam == null || roleAuthParam.getRoleId() == null){
            return ResponseValue.error();
        }
        // 2023-01-28 这个由于url地址设置不合理,没能匹配: /list 所以暂时不能删除
        this.preparePageSearch();
        GenericPager<S_user_core> pager = this.roleService.queryAllocatedUserList(roleAuthParam.getRoleId(), roleAuthParam.getUserName());
//        return this.acquireTablePage(pager.getDatas(), pager.getTotalRows());
        return ResponseValue.success(pager);
    }
    @RequestMapping("/edit")
    public ResponseValue saveEdit(@RequestBody SystemRole systemRole){
        if(systemRole == null || StringUtils.isEmpty(systemRole.getRole_name()) || systemRole.getOrg_id() == null
            || systemRole.getRole_id() == null){
            return ResponseValue.error("角色参数错误");
        }
        long orgId = systemRole.getOrg_id();
        S_role existRole = this.roleService.queryRoleByName(orgId, systemRole.getRole_name());
        if(existRole != null && existRole.getRole_id().longValue() != systemRole.getRole_id().longValue()){
            return ResponseValue.error("角色名称已经存在");
        }
        this.roleService.execUpdateRole(systemRole.$clone(), systemRole.getMenuIds());
        // 发送事件通知,让security重新加载权限数据
        BeanContextAware.publishEvent(new RoleSecurityChangeEvent(systemRole.getRole_id()));
        return ResponseValue.success();
    }
    @RequestMapping("/view/{roleId}")
    public ResponseValue view(@PathVariable Long roleId){
        S_role s_role = this.roleService.get(new S_role(roleId));
        if(s_role == null){
            return ResponseValue.error("角色不存在");
        }
        return ResponseValue.success(s_role);
    }
    @RequestMapping("/remove/{roleId}")
    public ResponseValue removeRole(@PathVariable Long roleId){
        if(roleId == null || roleId.longValue() <= 0){
            return ResponseValue.error("参数错误");
        }
        int roleUserCount = this.roleService.queryRoleUserSize(roleId);
        if(roleUserCount > 0){
            return ResponseValue.error("角色已分配用户,无法删除");
        }
        this.roleService.execDeleteRole(roleId);
        return ResponseValue.success();
    }
    @RequestMapping("/add")
    public ResponseValue saveAdd(@RequestBody SystemRole systemRole){
        if(systemRole == null || StringUtils.isEmpty(systemRole.getRole_name()) || systemRole.getOrg_id() == null){
            return ResponseValue.error("角色参数错误");
        }
        long orgId = systemRole.getOrg_id();
        if(orgId <= 0){
            return ResponseValue.error("角色所属机构必须提供");
        }
        S_role existRole = this.roleService.queryRoleByName(orgId, systemRole.getRole_name());
        if(existRole != null){
            return ResponseValue.error("角色名称已经存在");
        }
        systemRole.setRole_id(NumberGenerator.getSequenceNumber());
        this.roleService.execInsertRole(systemRole.$clone(), systemRole.getMenuIds());
        return ResponseValue.success();
    }
    @PostMapping("/select/changeStatus")
    public ResponseValue changeStatus(@RequestBody S_role s_role){
        if(s_role == null || s_role.getRole_id() == null){
            return ResponseValue.error("参数错误");
        }
        this.roleService.execUpdateStatus(s_role.getRole_id(), s_role.getStatus());
        return ResponseValue.success();
    }
    @GetMapping("/list")
    public ResponseValue pageList(RoleParam roleParam){
        if(roleParam == null || roleParam.getOrgId() <= 0){
            return ResponseValue.error("无法查询角色:没有条件");
        }
//        this.preparePageSearch();
        long orgId = 0;
        if(!this.isSupervisor()){
            // 普通用户,只能看到自己顶级机构用户
            orgId = this.getCurrentUser().getOrg_id();
        } else {
            orgId = roleParam.getOrgId();
        }
        GenericPager<S_role> pager = this.roleService.queryPageRoleList(orgId, roleParam.getStatus(), roleParam.getRoleName());
//        return this.acquireTablePage(pager.getDatas(), pager.getTotalRows());
        return ResponseValue.success(pager);
    }
}
iplatform-base-admin/src/main/java/com/iplatform/base/controller/SchedulerController.java
New file
@@ -0,0 +1,74 @@
//package com.iplatform.base.controller;
//
//import com.iplatform.base.SystemController;
//import com.iplatform.model.po.S_scheduler;
//import com.iplatform.scheduler.PlatformSchedulerManager;
//import com.iplatform.scheduler.pojo.SchedulerParam;
//import com.iplatform.scheduler.service.SchedulerServiceImpl;
//import com.walker.db.page.GenericPager;
//import com.walker.scheduler.Scheduler;
//import com.walker.web.ResponseValue;
//import org.springframework.beans.factory.annotation.Autowired;
//import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
//import org.springframework.web.bind.annotation.GetMapping;
//import org.springframework.web.bind.annotation.RequestBody;
//import org.springframework.web.bind.annotation.RequestMapping;
//import org.springframework.web.bind.annotation.RestController;
//
//@RestController
//@RequestMapping("/monitor/job")
//@ConditionalOnProperty(prefix = "iplatform.scheduler", name = "enabled", havingValue = "true", matchIfMissing = true)
//public class SchedulerController extends SystemController {
//
//    private SchedulerServiceImpl schedulerService;
//    private PlatformSchedulerManager platformSchedulerManager;
//
//    @Autowired
//    public SchedulerController(SchedulerServiceImpl schedulerService, PlatformSchedulerManager platformSchedulerManager){
//        this.schedulerService = schedulerService;
//        this.platformSchedulerManager = platformSchedulerManager;
//    }
//
//    @RequestMapping("/select/run")
//    public ResponseValue run(@RequestBody S_scheduler s_scheduler){
//        if(this.platformSchedulerManager == null){
//            return ResponseValue.error("调度模块未开启配置");
//        }
//        if(s_scheduler == null){
//            return ResponseValue.error("调度任务不存在");
//        }
//        Scheduler scheduler = this.platformSchedulerManager.getOneGatherScheduler(s_scheduler.getId());
//        if(scheduler == null){
//            return ResponseValue.error("调度器不存在: " + s_scheduler.getId());
//        }
//        if(scheduler.isStarted() && !scheduler.isPause()){
//            return ResponseValue.error("调度器已运行,无需重复启动");
//        }
//        if(scheduler.isPause()){
//            this.platformSchedulerManager.restartScheduler(s_scheduler.getId());
//            logger.info("调度任务被暂停后重启,id = " + s_scheduler.getId());
//        } else {
//            logger.warn("调度任务已经在运行中,无需重复启动,id = " + s_scheduler.getId());
//        }
//        return ResponseValue.success();
//    }
//
//    @GetMapping("/list")
//    public ResponseValue list(SchedulerParam schedulerParam){
//        if(schedulerParam == null || schedulerParam.getOrgId() <= 0){
//            return ResponseValue.error("无法查询调度任务");
//        }
////        this.preparePageSearch();
//
//        long orgId = 0;
//        if(!this.isSupervisor()){
//            // 普通用户,只能看到自己顶级机构用户
//            orgId = this.getCurrentUser().getOrg_id();
//        } else {
//            orgId = schedulerParam.getOrgId();
//        }
//
//        GenericPager<S_scheduler> pager = this.schedulerService.queryPageSchedulerList(orgId);
//        return this.acquireTablePage(pager.getDatas(), pager.getTotalRows());
//    }
//}
iplatform-base-admin/src/main/java/com/iplatform/base/controller/UserController.java
New file
@@ -0,0 +1,386 @@
package com.iplatform.base.controller;
import com.iplatform.base.*;
import com.iplatform.base.excel.UserDataImportor;
import com.iplatform.base.pojo.UserParam;
import com.iplatform.base.service.LoginServiceImpl;
import com.iplatform.base.service.RoleServiceImpl;
import com.iplatform.base.service.UserServiceImpl;
import com.iplatform.base.util.UserUtils;
import com.iplatform.base.util.role.SystemRole;
import com.iplatform.base.util.user.SystemUser;
import com.iplatform.model.po.S_role;
import com.iplatform.model.po.S_user_core;
import com.walker.db.page.GenericPager;
import com.walker.di.DataImportException;
import com.walker.file.FileInfo;
import com.walker.infrastructure.utils.DateUtils;
import com.walker.infrastructure.utils.NumberGenerator;
import com.walker.infrastructure.utils.StringUtils;
import com.walker.web.ResponseValue;
import com.walker.web.UserOnlineProvider;
import com.walker.web.log.BusinessType;
import com.walker.web.log.Log;
import com.walker.web.log.OperateUser;
//
//
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
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.multipart.MultipartFile;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
//@Api(tags = "用户管理模块")
@RestController
@RequestMapping("/system/user")
public class UserController extends SystemController {
//    private DeptCacheProvider deptCacheProvider;
    private UserServiceImpl userService;
    private RoleServiceImpl roleService;
    // 2023-03-22
    private LoginServiceImpl loginService;
    private UserOnlineProvider userOnlineProvider;
    @Autowired
    public UserController(UserServiceImpl userService, RoleServiceImpl roleService
//            , UserCacheProvider userCacheProvider, DeptCacheProvider deptCacheProvider
            , LoginServiceImpl loginService, UserOnlineProvider userOnlineProvider){
//        this.setDeptCacheProvider(deptCacheProvider);
//        this.setUserCacheProvider(userCacheProvider);
        this.userService = userService;
        this.roleService = roleService;
        this.loginService = loginService;
        this.userOnlineProvider = userOnlineProvider;
    }
    @PostMapping("/import")
    public ResponseValue importExcel(MultipartFile file, boolean updateSupport){
        try {
            UserDataImportor dataImportor = new UserDataImportor(file.getInputStream());
            dataImportor.setId("user_import");
            this.getDataImportEngine().executeImport(dataImportor, this.getCurrentUserPrincipal().getUserName());
            logger.info("成功导入记录:{}", dataImportor.getSuccessSize());
            logger.info("错误结果文件:{}", dataImportor.getErrorFile());
//            FileInfo errorFileInfo = this.uploadFileToRemote(dataImportor.getErrorFile());
            FileInfo errorFileInfo = this.uploadFileToRemote(dataImportor.getErrorFile(), null, String.valueOf(this.getOwner()));
            if(errorFileInfo != null){
                logger.debug("用户导入存在'不符合数据': ,", errorFileInfo.toString());
                logger.debug("这里可以返回给前端,让前端点击链接下载!");
            }
            return ResponseValue.success();
        } catch (Exception e) {
            if(e instanceof DataImportException){
                return ResponseValue.error("导入出现异常:" + e.getMessage());
            }
            logger.error("io异常:" + e.getMessage(), e);
            return ResponseValue.error("上传文件异常:" + e.getMessage());
        }
    }
    /**
     * 下载导入模板,该方法可做成统一接口,适合动态生成的模板。<p></p>
     * 对于特定自定义的模板,业务自己提供下载地址。
     * @author 时克英
     * @date 2023-02-08
     */
    @PostMapping("/select/downloadTemplate")
    public void downloadTemplate(){
        // 要保存的模板临时路径,仅仅提供一个本地路径地址,供导入引擎自动生成并保存!
//        String templatePath = "d:/tmp/template_user.xlsx";
//        TemplateInfo templateInfo = new TemplateInfo();
//        templateInfo.setTemplatePath(templatePath);
//        templateInfo.setTableName("s_user_core");
//
//        File templateFile = this.getDataImportEngine().generateTemplate(templateInfo);
//
//        try {
//            this.downloadSimpleFile(FileCopyUtils.copyToByteArray(templateFile), "系统用户导入");
//        } catch (IOException e) {
//            logger.error("下载'导出用户'模板错误:" + e.getMessage(), e);
//            ServletUtils.renderString(getResponse(), "下载'导出用户'模板错误:" + e.getMessage());
//        }
        this.downloadLocalImportTemplate("s_user_core");
    }
    /**
     * 更新用户归属角色,用户管理 --> 设置角色
     * @param userId
     * @param roleIds
     * @return
     * @date 2022-12-15
     */
    @PostMapping("/select/saveAuthRole")
    public ResponseValue updateAuthRole(Long userId, Long[] roleIds){
        if(userId == null || userId.longValue() <= 0){
            return ResponseValue.error("参数错误");
        }
        this.userService.execUpdateAuthRole(userId, roleIds);
        logger.error("更新用户归属角色后,需要更新用户登录缓存中角色列表,记住修改代码!");
        // 处理缓存的登录信息中角色更新,2023-03-22
        String uuid = this.loginService.queryLoginUUID(userId);
        if(StringUtils.isNotEmpty(uuid)){
            List<String> roleIdList = new ArrayList<>(4);
            for(long roleId : roleIds){
                roleIdList.add(String.valueOf(roleId));
            }
            DefaultUserPrincipal userPrincipal = new DefaultUserPrincipal(this.getUser(userId));
            userPrincipal.setRoleIdList(roleIdList);
            // 缓存登录信息
            this.userOnlineProvider.cacheUserPrincipal(uuid, userPrincipal);
        } else {
            logger.debug("用户从未登录过,不更新登录uuid信息,userId={}", userId);
        }
        return ResponseValue.success();
    }
    /**
     * 前端,选定用户后,获取授权角色信息。
     * @param userId
     * @return
     * @date 2022-12-13
     */
    @GetMapping("/select/authRole/{userId}")
    public ResponseValue authRole(@PathVariable Long userId){
//        List<S_role> roleList = this.userService.queryUserRoleList(userId);
        List<SystemRole> userAuthRoleList = this.userService.queryAuthRoleList(this.getUserRootOrgId(userId), userId);
        Map<String, Object> data = new HashMap<>(4);
        // 本单位所有角色
        data.put("roles", userAuthRoleList);
        // 用户信息
        data.put("user", this.getUserCacheProvider().getUser(userId));
        return ResponseValue.success(data);
    }
//    // @ApiOperation(value = "重置用户密码")
    @PostMapping("/resetPwd")
    public ResponseValue resetPassword(@RequestBody SystemUser systemUser){
        if(systemUser == null || systemUser.getId() == null){
            return ResponseValue.error("参数错误");
        }
        String initPassword = this.getArgumentVariable(ArgumentsConstants.KEY_SECURITY_PASSWORD_INIT).getStringValue();
        this.userService.execResetPassword(systemUser.getId(), initPassword);
        S_user_core user_core = this.userService.get(new S_user_core(systemUser.getId()));
        this.getUserCacheProvider().updateUser(user_core);
        return ResponseValue.success();
    }
    @GetMapping("/remove/{userId}")
    @Log(title = "用户管理", businessType = BusinessType.Delete, isSaveRequestData = true, isSaveResponseData = true)
    public ResponseValue deleteUser(@PathVariable Long userId){
        if(userId == null || userId.longValue() <= 0){
            return ResponseValue.error("参数错误");
        }
        this.userService.execDeleteUser(userId, this.getPlatformCallback(PlatformUserCallback.class));
        this.getUserCacheProvider().removeUser(userId);
        logger.info("删除一个用户,缓存已更新:" + userId);
        return ResponseValue.success();
    }
    @PostMapping("/edit")
    public ResponseValue editUser(@RequestBody SystemUser user_core){
        if(user_core == null){
            return ResponseValue.error("参数不存在");
        }
        if(StringUtils.isEmpty(user_core.getUser_name()) || StringUtils.isEmpty(user_core.getNick_name())){
            return ResponseValue.error("缺少: 登录ID或用户昵称");
        }
        if(user_core.getUser_name().equals(Constants.SUPERVISOR_NAME_DEFAULT)){
            return ResponseValue.error("无法使用该用户名");
        }
        S_user_core existUser = null;
        String phoneNumber = user_core.getPhonenumber();
        if(StringUtils.isNotEmpty(phoneNumber)){
            existUser = this.userService.queryUserByPhone(phoneNumber);
            if(existUser != null && existUser.getId().longValue() != user_core.getId().longValue()){
                return ResponseValue.error("该手机号已被现有用户使用");
            }
        }
        String email = user_core.getEmail();
        if(StringUtils.isNotEmpty(email)){
            existUser = this.userService.queryUserByEmail(email);
            if(existUser != null && existUser.getId().longValue() != user_core.getId().longValue()){
                return ResponseValue.error("该邮箱地址已被现有用户使用");
            }
        }
        this.userService.execUpdateUser(user_core.$clone(), user_core.getRoleIds(), this.getPlatformCallback(PlatformUserCallback.class));
        this.getUserCacheProvider().updateUser(user_core);
        logger.info("编辑用户成功,并更新缓存: " + user_core.getUser_name());
        return ResponseValue.success();
    }
    @PostMapping("/select/changeStatus")
    public ResponseValue changeStatus(@RequestBody SystemUser systemUser){
        if(systemUser == null || systemUser.getId() == null || systemUser.getStatus() == null){
            return ResponseValue.error("缺少参数");
        }
        this.userService.execUpdateUserStatus(systemUser.getId(), systemUser.getStatus());
        S_user_core user_core = this.userService.get(new S_user_core(systemUser.getId()));
        this.getUserCacheProvider().updateUser(user_core);
        return ResponseValue.success();
    }
    @PostMapping("/add")
    @Log(title = "用户管理", businessType = BusinessType.Insert, operatorType = OperateUser.Manage)
    public ResponseValue saveUser(@RequestBody SystemUser user_core){
        if(user_core == null){
            return ResponseValue.error("参数不存在");
        }
        if(StringUtils.isEmpty(user_core.getUser_name()) || StringUtils.isEmpty(user_core.getNick_name())){
            return ResponseValue.error("缺少: 登录ID或用户昵称");
        }
        if(user_core.getUser_name().equals(Constants.SUPERVISOR_NAME_DEFAULT)){
            return ResponseValue.error("无法使用该用户名");
        }
        S_user_core existUser = this.userService.queryUserByLoginId(user_core.getUser_name());
        if(existUser != null){
            return ResponseValue.error("登录名已存在");
        }
        // 检查并设置密码
        String encryptInitPass = null;
        if(StringUtils.isEmpty(user_core.getPassword())){
            // 用户没有录入特定密码,用平台配置的默认密码
            encryptInitPass = this.getArgumentVariable(ArgumentsConstants.KEY_SECURITY_PASSWORD_INIT).getStringValue();
        } else {
            // 用户输入了明文密码
            encryptInitPass = this.encryptPassword(user_core.getPassword());
        }
        user_core.setPassword(encryptInitPass);
        String phoneNumber = user_core.getPhonenumber();
        if(StringUtils.isNotEmpty(phoneNumber)){
            if(this.userService.queryUserByPhone(phoneNumber) != null){
                return ResponseValue.error("该手机号已被现有用户使用");
            }
        }
        String email = user_core.getEmail();
        if(StringUtils.isNotEmpty(email)){
            if(this.userService.queryUserByEmail(email) != null){
                return ResponseValue.error("该邮箱地址已被现有用户使用");
            }
        }
        user_core.setCreate_time(DateUtils.getDateTimeNumber(System.currentTimeMillis()));
        user_core.setCreate_by(this.getCurrentUser().getUser_name());
        user_core.setOrg_id(this.getRootOrgIdByDept(user_core.getDept_id()));
        user_core.setId(IdUtil.generateId());
        // 注意:这里保存必须用生成的 S_user_core 对象,所以通过 SystemUser克隆一个即可。
        // 否则报错, 2022-12-13
        this.userService.execSaveUser(user_core.$clone(), user_core.getRoleIds(), this.getPlatformCallback(PlatformUserCallback.class));
        // 更新缓存
        this.getUserCacheProvider().putUser(user_core);
        logger.info("添加一个用户成功,缓存已加入: " + user_core.getUser_name());
        return ResponseValue.success();
    }
    /**
     * 创建新用户时,选择角色列表。
     * @param deptId
     * @return
     * @date 2022-12-12
     */
    @GetMapping("/select/role/{deptId}")
    public ResponseValue getNewUserRoles(@PathVariable Long deptId){
        long orgId = 0;
        if(deptId == null || deptId.longValue() == 0){
            //普通用户
            orgId = this.getCurrentUser().getOrg_id();
        } else {
            // 超级管理员选了特定机构
            orgId = this.getRootOrgIdByDept(deptId);
        }
        List<S_role> roleList = this.roleService.queryRoleList(orgId);
        Map<String, Object> data = new HashMap<>(4);
        data.put("roles", roleList);
//        data.put("init_password", this.getArgumentVariable(ArgumentsConstants.KEY_SECURITY_PASSWORD_INIT).getStringValue());
        return ResponseValue.success(data);
    }
    /**
     * 编辑用户时,获得用户信息。
     * @param userId
     * @return
     * @date 2022-12-13
     */
//    @GetMapping(value = { "/view", "/view/{userId}" })
    @GetMapping("/view/{userId}")
    public ResponseValue getUserInfo(@PathVariable Long userId){
        if(userId == null || userId <= 0){
            return ResponseValue.error("参数错误");
        }
        long orgId = this.getUserCacheProvider().getUser(userId).getOrg_id();
        List<S_role> roleList = this.roleService.queryRoleList(orgId);
        List<String> roleIds = this.userService.queryUserRoleIdList(userId);
        Map<String, Object> data = new HashMap<>(4);
        // 本单位所有角色
        data.put("roles", roleList);
        // 用户所属角色
        data.put("roleIds", UserUtils.toRoleIdLongList(roleIds));
        // 用户信息
        data.put("data", this.getUserCacheProvider().getUser(userId));
        return ResponseValue.success(data);
    }
    @GetMapping("/list")
    public ResponseValue list(UserParam userParam){
        if(userParam == null){
            return ResponseValue.error("无法查询用户:没有条件");
        }
//        this.preparePageSearch();
        long orgId = 0;
        long deptId = userParam.getDeptId();
        if(!this.isSupervisor()){
            // 普通用户,只能看到自己顶级机构用户
            orgId = this.getCurrentUser().getOrg_id();
        } else {
            // 超级管理员
            if(deptId <= 0){
                // 没有选择机构树,无法查询用户
                return ResponseValue.error("请选择一个机构查询");
            }
            orgId = this.getRootOrgIdByDept(deptId);
        }
        if(orgId == deptId){
            // 点击的顶级机构
            deptId = 0;
        }
        return ResponseValue.error("您没有权限查询!");
//        GenericPager<S_user_core> pager = this.userService.queryPageUserList(orgId
//                , deptId, userParam.getUserName(), userParam.getPhonenumber(), userParam.getStatus());
//        // 补充所在'部门名字'属性,2023-03-23
//        List<S_user_core> data = pager.getDatas();
//        if(!StringUtils.isEmptyList(data)){
//            data.stream().forEach(p -> {
//                p.setParameterString("dept_name", this.getDeptName(p.getDept_id()));
//            });
//        }
//        return this.acquireTablePage(pager.getDatas(), pager.getTotalRows());
//        return ResponseValue.error("您没有权限查询!");
    }
//    private long getRootOrgIdByDept(long deptId){
//        S_dept dept = this.deptCacheProvider.getDept(deptId);
//        if(dept == null){
//            throw new IllegalStateException("缓存中未找到机构: " + dept);
//        }
//        return dept.getOrg_id();
//    }
}
iplatform-base-admin/src/main/java/com/iplatform/base/controller/UserProfileController.java
New file
@@ -0,0 +1,201 @@
package com.iplatform.base.controller;
import com.iplatform.base.Constants;
import com.iplatform.base.SystemController;
import com.iplatform.base.callback.UserProfileCallback;
import com.iplatform.base.config.SecurityUserProperties;
import com.iplatform.base.pojo.UserInfoRequest;
import com.iplatform.base.service.UserServiceImpl;
import com.iplatform.base.util.PlatformRSAUtils;
import com.iplatform.core.BeanContextAware;
import com.iplatform.core.TokenAwareContext;
import com.iplatform.core.TokenEntity;
import com.iplatform.model.po.S_user_core;
import com.iplatform.model.po.S_user_login;
import com.walker.file.FileInfo;
import com.walker.infrastructure.utils.StringUtils;
import com.walker.web.ResponseValue;
import com.walker.web.UserOnlineProvider;
import com.walker.web.UserPrincipal;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
//@Api(tags = "用户档案")
@RestController
@RequestMapping("/system/user/profile")
public class UserProfileController extends SystemController {
    private UserServiceImpl userService;
    private SecurityUserProperties securityUserProperties;
    @Autowired
    public UserProfileController(UserServiceImpl userService, SecurityUserProperties securityUserProperties){
        this.userService = userService;
        this.securityUserProperties = securityUserProperties;
    }
    /**
     * 返回登录用户基本信息。
     * @return
     * @date 2023-02-15
     */
    @GetMapping
    public ResponseValue index(){
        List<String> roleIdList = this.getCurrentUserPrincipal().getRoleIdList();
        Map<String, Object> data = new HashMap<>(4);
        data.put("user", this.getCurrentUser());
        if(!StringUtils.isEmptyList(roleIdList)){
            data.put("roleGroup", StringUtils.collectionToCommaDelimitedString(roleIdList));
        } else {
            data.put("roleGroup", StringUtils.EMPTY_STRING);
        }
        return ResponseValue.success(data);
    }
//    // @ApiOperation(value = "上传头像")
    @PostMapping("/avatar")
    public ResponseValue avatar(MultipartFile file){
        if(file == null){
            return ResponseValue.error("上传头像为空");
        }
        long userId = this.getCurrentUserId();
        try {
            FileInfo fileInfo = this.uploadFileToRemote(file.getInputStream(), "avatar.jpg", String.valueOf(userId), file.getSize(), null, String.valueOf(Constants.OWNER_PLATFORM));
            S_user_core s_user_core = new S_user_core(userId);
            s_user_core.setAvatar(fileInfo.getId());
            // 更新数据库用户记录
            this.userService.save(s_user_core);
            // 更新缓存
            S_user_core cacheUser = this.getUserCacheProvider().getUser(userId);
            cacheUser.setAvatar(fileInfo.getId());
            this.getUserCacheProvider().updateUser(cacheUser);
            return ResponseValue.success("success","imageUrl");
        } catch (Exception e) {
            logger.error("上传头像错误:" + e.getMessage(), e);
            return ResponseValue.error("头像处理异常!");
        }
    }
    /**
     * 后台用户修改资料:姓名和密码,后续扩展。
     * @param request
     * @return
     * @date 2023-08-02
     */
    @RequestMapping(value = "/updateInfo", method = RequestMethod.POST)
    public ResponseValue updateNameAndPassword(@RequestBody UserInfoRequest request){
        if(request == null){
            return ResponseValue.error("请填写更新资料");
        }
        if(StringUtils.isEmpty(request.getRealName()) && StringUtils.isEmpty(request.getPassword())){
            return ResponseValue.error("未填写任何资料");
        }
        S_user_core currentUser = this.getCurrentUser();
        if(StringUtils.isNotEmpty(request.getPassword())){
            String originPassword = PlatformRSAUtils.getRsaDecryptValue(request.getPassword(), PlatformRSAUtils.PRIK);
//            String error = PasswordUtils.filterText(originPassword);
//            if(error != null){
//                return ResponseValue.error(error);
//            }
//            int passLevelConfig = this.securityUserProperties.getPassLevel();
//            if(!PasswordUtils.validateComplex(originPassword, passLevelConfig)){
//                return ResponseValue.error("密码级别过低,请输入:大小写字母、数字以及至少一种特殊符号");
//            }
            String error = this.validatePasswordRule(originPassword);
            if(error != null){
                return ResponseValue.error(error);
            }
            // 设置加密后的密码,直接保存更新
            request.setPassword(this.encryptPassword(originPassword));
            currentUser.setPassword(request.getPassword());
            currentUser.setModify_pwd(1);
        }
        if(StringUtils.isNotEmpty(request.getRealName())){
            currentUser.setNick_name(request.getRealName());
        }
        UserProfileCallback callback = this.getPlatformCallback(UserProfileCallback.class);
        this.getUserService().execUpdateUserInfo(request, currentUser.getId(), callback);
        this.getUserCacheProvider().updateUser(currentUser);
        logger.debug(TokenAwareContext.getCurrentToken().toString());
        return ResponseValue.success();
    }
//    /**
//     * 验证密码是否符合平台政策。
////     * @param encryptPassword 前端修改的密码(密文),RSA加密,后台要解密的
//     * @param originPassword 原始明文密码
//     * @return 返回错误提示,返回空表示成功
//     * @date 2023-08-05
//     */
//    protected String validatePasswordRule(String originPassword){
////        String originPassword = PlatformRSAUtils.getRsaDecryptValue(encryptPassword, PlatformRSAUtils.PRIK);
//        String error = PasswordUtils.filterText(originPassword);
//        if(error != null){
//            return error;
//        }
//        int passLevelConfig = this.securityUserProperties.getPassLevel();
//        if(!PasswordUtils.validateComplex(originPassword, passLevelConfig)){
//            return "密码级别过低,请输入:大小写字母、数字以及至少一种特殊符号";
//        }
//        return null;
//    }
    /**
     * 强制当前用户修改密码
     * @param encryptPassword
     * @return
     * @date 2023-08-05
     */
    @RequestMapping(value = "/force_change_pass", method = RequestMethod.POST)
    public ResponseValue forceChangePassword(String encryptPassword){
        if(StringUtils.isEmpty(encryptPassword)){
            return ResponseValue.error("必须填写修改密码内容");
        }
        String originPassword = PlatformRSAUtils.getRsaDecryptValue(encryptPassword, PlatformRSAUtils.PRIK);
        String error = this.validatePasswordRule(originPassword);
        if(error != null){
            return ResponseValue.error(error);
        }
        long userId = this.getCurrentUserId();
        this.getUserService().execForceChangePassword(userId, this.encryptPassword(originPassword));
        // 更新缓存
        S_user_core userCore = this.getUserService().get(new S_user_core(userId));
        this.getUserCacheProvider().updateUser(userCore);
        // 更新登录用户缓存
        String uuid = null;
        UserPrincipal<S_user_core> userPrincipal = null;
        UserOnlineProvider userOnlineProvider = BeanContextAware.getBeanByType(UserOnlineProvider.class);
        TokenEntity tokenEntity = TokenAwareContext.getCurrentToken();
        if(tokenEntity != null){
            uuid = tokenEntity.getUuid();
            userPrincipal = (UserPrincipal<S_user_core>)userOnlineProvider.getUserPrincipal(uuid);
        } else {
            logger.debug("TokenAwareContext 未获取到token信息,需要从登录缓存中查询,userId={}", userId);
            S_user_login user_login = this.getLoginStrategyManager().getUserLogin(userCore.getUser_name());
            if(user_login == null){
                logger.warn("用户已(强制)修改密码,但未找到登录缓存(user_login),需要重新登录,user = {}", userCore.getUser_name());
                throw new IllegalStateException("");
            }
            uuid = user_login.getUuid();
            userPrincipal = (UserPrincipal<S_user_core>)userOnlineProvider.getUserPrincipal(uuid);
        }
        userPrincipal.getUserInfo().setModify_pwd(1);
        userOnlineProvider.cacheUserPrincipal(uuid, userPrincipal);
        logger.debug("密码已修改,userId={}", userId);
        return ResponseValue.success();
    }
}
iplatform-base-admin/src/main/java/com/iplatform/base/excel/UserDataImportor.java
New file
@@ -0,0 +1,126 @@
package com.iplatform.base.excel;
import com.iplatform.base.di.JdbcExcelDataImportor;
import com.iplatform.base.util.DataImportUtils;
import com.walker.di.ErrorRecord;
import com.walker.di.UpdateResult;
import com.walker.di.UpdateType;
import com.walker.infrastructure.utils.PhoneNumberUtils;
import com.walker.infrastructure.utils.StringUtils;
import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
import java.io.InputStream;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
/**
 * 系统用户导入器实现。
 * @author 时克英
 * @date 2023-02-07
 */
public class UserDataImportor extends JdbcExcelDataImportor {
//    public UserDataImportor(File file){
//        this.setBatchEnabled();
//        this.setBatchSleepMills(200);
//        this.setUpdateType(UpdateType.Override);    // 设置更新策略为:覆盖记录
//        try {
//            this.setSource(new BufferedInputStream(new FileInputStream(file)));
//        } catch (FileNotFoundException e) {
//            throw new RuntimeException(e);
//        }
//    }
    public UserDataImportor(InputStream inputStream){
        this.setBatchEnabled();
        this.setBatchSleepMills(200);
        this.setUpdateType(UpdateType.Override);    // 设置更新策略为:覆盖记录
        this.setSource(inputStream);
    }
    @Override
    protected String acquireTableName() {
        return "s_user_core";
    }
    @Override
    protected boolean isCheckDataExist() {
        return true;
    }
    @Override
    protected UpdateResult checkDataExist(String tableName, List<Map<String, Object>> mapList) {
        // 1 - 根据导入原始数据查询是否在表中存在这些记录
        MapSqlParameterSource sqlParameterSource = new MapSqlParameterSource();
        List<String> paramUserName = DataImportUtils.acquireWhereInStringValues("user_name", mapList);
        sqlParameterSource.addValue("userName", paramUserName);
        List<Map<String, Object>> existList = this.getDataImportService().queryListObjectWhereIn(SQL_QUERY, sqlParameterSource);
        if(!StringUtils.isEmptyList(existList)){
            if(this.logger.isDebugEnabled()){
                for(Map<String, Object> existOne : existList){
                    logger.debug("存在一条数据 = " + existOne);
                }
            }
        }
        // 创建更新结果对象
        UpdateResult updateResult = new UpdateResult();
        // 2 - 设置准备更新的'条件字段集合',可支持多个
        List<String> whereColumnNames = Arrays.asList("user_name");
        // 3 - 找到新写入和需要更新的数据集合
        Object[] result = DataImportUtils.calculateInsertAndUpdateList(mapList, existList, whereColumnNames);
        List<Map<String, Object>> insertList = (List<Map<String, Object>>)result[0];
        List<Map<String, Object>> updateList = (List<Map<String, Object>>)result[1];
        // 4 - 设置要写入集合
        updateResult.setInsertList(insertList);
        // 5 - 如果更新策略为覆盖,则设置要更新集合
        if(this.getUpdateType() == UpdateType.Override){
            // 如果更新数据策略为: 覆盖模式,需要批量更新数据,这里获得更新集合给平台。
            updateResult.setUpdateColumnNames(Arrays.asList("dept_id", "org_id", "nick_name", "user_type", "email", "phonenumber", "sex"));
            updateResult.setWhereColumnNames(whereColumnNames);
            updateResult.setUpdateList(updateList);
        }
        return updateResult;
    }
    private static final String SQL_QUERY = "select * from s_user_core where user_name in (:userName)";
    @Override
    protected void finish(long currentDataRowCount, int readRowIndex) {
    }
    @Override
    protected void emptyDataAfterValidate() {
    }
    @Override
    protected void onValidateError(List<ErrorRecord> errorRecordList) {
    }
    @Override
    protected String validateData(Map<String, String> data, int rowIndex) {
        if(data.get("user_name") == null){
            return "登录名不存在";
        }
        if(data.get("nick_name") == null){
            return "昵称不存在";
        }
        if(data.get("user_type") == null){
            return "用户类型不存在";
        }
        String phoneNumber = data.get("phonenumber");
        if(StringUtils.isNotEmpty(phoneNumber) && !PhoneNumberUtils.isCellPhoneNumber(phoneNumber)){
            return "手机号格式错误";
        }
        return null;
    }
}
iplatform-base-security-consum/pom.xml
@@ -3,15 +3,15 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>iplatform</artifactId>
        <groupId>com.iplatform</groupId>
        <version>2.3.0-SNAPSHOT</version>
        <artifactId>low-consum-manage</artifactId>
        <groupId>com.consum</groupId>
        <version>1.0.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    <artifactId>iplatform-base-security-consum</artifactId>
    <packaging>jar</packaging>
    <version>1.0.0-SNAPSHOT</version>
    <version>3.2.0</version>
    <properties>
    </properties>
@@ -24,35 +24,6 @@
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     -->
    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <scope>test</scope>
        </dependency>
        <!-- 通过基础库引入: spring-security, 2022/10/31 -->
        <dependency>
            <groupId>com.walkersoft</groupId>
            <artifactId>walker-web-security</artifactId>
            <exclusions>
                <exclusion>
                    <artifactId>spring-security-core</artifactId>
                    <groupId>org.springframework.security</groupId>
                </exclusion>
                <exclusion>
                    <artifactId>snakeyaml</artifactId>
                    <groupId>org.yaml</groupId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-core</artifactId>
            <version>5.7.12</version>
        </dependency>
        <!-- 平台基础模块必须存在, 2022/10/31 -->
        <dependency>
            <groupId>com.iplatform</groupId>
@@ -60,18 +31,37 @@
            <scope>provided</scope>
            <exclusions>
                <exclusion>
                    <artifactId>snakeyaml</artifactId>
                    <groupId>org.yaml</groupId>
                    <artifactId>walker-web</artifactId>
                    <groupId>com.walkersoft</groupId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.yaml</groupId>
            <artifactId>snakeyaml</artifactId>
            <version>2.0</version>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt-api</artifactId>
            <version>0.11.5</version>
        </dependency>
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt-impl</artifactId>
            <version>0.11.5</version>
        </dependency>
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt-jackson</artifactId>
            <version>0.11.5</version>
        </dependency>
        <dependency>
            <groupId>com.walkersoft</groupId>
            <artifactId>walker-web</artifactId>
            <version>3.2.0</version>
            <scope>system</scope>
            <systemPath>${project.basedir}/src/main/resources/lib/walker-web-3.2.0.jar</systemPath>
        </dependency>
    </dependencies>
</project>
iplatform-base-security-consum/src/main/java/com/iplatform/security/DefaultAuthenticationFailureHandler.java
@@ -5,6 +5,9 @@
import com.walker.web.ResponseCode;
import com.walker.web.ResponseValue;
import com.walker.web.util.ServletUtils;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.authentication.AccountExpiredException;
@@ -16,9 +19,6 @@
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class DefaultAuthenticationFailureHandler implements AuthenticationFailureHandler {
iplatform-base-security-consum/src/main/java/com/iplatform/security/DefaultAuthenticationFilter.java
@@ -1,12 +1,11 @@
package com.iplatform.security;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
 * DefaultAuthenticationToken 重写后,登录总是报错: 用户名或密码错误,尝试重写该过滤器。
iplatform-base-security-consum/src/main/java/com/iplatform/security/DefaultAuthenticationSuccessHandler.java
@@ -2,14 +2,13 @@
import com.walker.web.TokenGenerator;
import com.walker.web.UserPrincipal;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@Deprecated
iplatform-base-security-consum/src/main/java/com/iplatform/security/DefaultLogoutSuccessHandler.java
@@ -11,14 +11,14 @@
import com.walker.web.TokenGenerator;
import com.walker.web.UserOnlineProvider;
import com.walker.web.util.ServletUtils;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class DefaultLogoutSuccessHandler implements LogoutSuccessHandler {
iplatform-base-security-consum/src/main/java/com/iplatform/security/DefaultSecuritySpi.java
@@ -43,6 +43,7 @@
import com.walker.web.WebUserAgent;
import com.walker.web.util.IdUtils;
import com.walker.web.util.ServletUtils;
import jakarta.servlet.http.HttpServletRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.authentication.AuthenticationManager;
@@ -52,7 +53,6 @@
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.password.PasswordEncoder;
import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
iplatform-base-security-consum/src/main/java/com/iplatform/security/FailedAuthenticationEntryPoint.java
@@ -4,12 +4,14 @@
import com.walker.web.ResponseCode;
import com.walker.web.ResponseValue;
import com.walker.web.util.ServletUtils;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import javax.servlet.ServletException;
import java.io.IOException;
/**
@@ -22,8 +24,8 @@
    protected final transient Logger logger = LoggerFactory.getLogger(getClass());
    @Override
    public void commence(javax.servlet.http.HttpServletRequest request
            , javax.servlet.http.HttpServletResponse response
    public void commence(HttpServletRequest request
            , HttpServletResponse response
            , AuthenticationException authException) throws IOException, ServletException {
        String msg = "认证失败,无权限访问系统资源" + request.getRequestURI();
iplatform-base-security-consum/src/main/java/com/iplatform/security/JwtAuthenticationTokenFilter.java
@@ -17,6 +17,10 @@
import com.walker.web.UserOnlineProvider;
import com.walker.web.UserPrincipal;
import com.walker.web.util.ServletUtils;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
@@ -24,10 +28,6 @@
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.web.filter.OncePerRequestFilter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;
iplatform-base-security-consum/src/main/java/com/iplatform/security/config/WebSecurityConfig.java
@@ -37,6 +37,7 @@
import com.walker.web.security.DefaultSecurityMetadataSource;
import com.walker.web.security.ResourceLoadProvider;
import com.walker.web.token.JwtTokenGenerator;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
@@ -45,6 +46,7 @@
import org.springframework.security.access.AccessDecisionManager;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.security.config.annotation.web.ExceptionHandlingDsl;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UserDetailsService;
@@ -93,6 +95,13 @@
    }
    /**
 * HttpSecurity:忽略 antMatchers 中使用的端点的身份验证,其他安全功能将生效。<br></br>
 * WebSecurity:直接忽略也不会进行 CSRF xss等攻击保护。
 * @param http
 * @return
 * @throws Exception
 */
    /**
     * HttpSecurity:忽略 antMatchers 中使用的端点的身份验证,其他安全功能将生效。<br></br>
     * WebSecurity:直接忽略也不会进行 CSRF xss等攻击保护。
     * @param http
@@ -101,82 +110,89 @@
     */
    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        // 缓存 securityProperties 的结果,避免重复调用
        SecurityProperties securityProperties = this.securityProperties();
        DefaultUserDetailsService userDetailsService = userDetailsService(this.securityProperties(), this.userCacheProvider);
        DefaultUserDetailsService userDetailsService = userDetailsService(securityProperties, this.userCacheProvider);
        http.userDetailsService(userDetailsService);
        // CSRF禁用,因为不使用session
        http.csrf().disable();
        // ???
        http.headers().frameOptions().disable();
        // 注意:禁用CSRF需确保所有接口已通过其他方式保护
        http.csrf(csrf -> csrf.disable());
        // 登录行为由自己实现,参考 AuthController#login
        http.formLogin().disable().httpBasic().disable();
        // 禁用frameOptions以支持iframe嵌套
        // 替换弃用的 headers() 方法
        http.headers(headers -> headers.frameOptions(frameOptions -> frameOptions.disable()));
        // 匿名资源访问权限,返回无权限提示接口
        http.exceptionHandling().authenticationEntryPoint(failedAuthenticationEntryPoint())
                // 已认证用户无权限访问配置
                .accessDeniedHandler(this.accessDeniedHandler())
                .and()
                // 基于token,所以不需要session
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
        // 禁用默认登录和HTTP Basic认证
        http.formLogin(formLogin -> formLogin.disable());
//        http.formLogin().loginProcessingUrl("/login")
//                        .failureHandler(this.authenticationFailureHandler());
        // 注意:这里不能配置上面的登录,否则就不会执行自己实现的/login方法。2022-11-11
        http.logout().logoutUrl("/logout").logoutSuccessHandler(this.logoutSuccessHandler()).permitAll();
        // 异常处理配置
        http.exceptionHandling(exceptionHandling -> exceptionHandling
                .authenticationEntryPoint(failedAuthenticationEntryPoint())
                .accessDeniedHandler(this.accessDeniedHandler()));
        // 匿名访问集合,2022-11-07
        List<String> anonymousList = this.securityProperties().getAnonymousList();
        if(!StringUtils.isEmptyList(anonymousList)){
            http.authorizeHttpRequests().antMatchers(anonymousList.toArray(new String[]{})).permitAll();
        }
//        http.authorizeHttpRequests().antMatchers("/login", "/register", "/captchaImage", "/test/**").permitAll();
//        http.authorizeHttpRequests().antMatchers("/static/**", "/test/**").permitAll();
//        http.authorizeHttpRequests().antMatchers("/security/**").hasAuthority("query_user");
        // 基于token,所以不需要session
        http.sessionManagement(sessionManagement -> sessionManagement.sessionCreationPolicy(SessionCreationPolicy.STATELESS));
        // 2023-03-21 注释掉,调试activiti7时发现和下面重复,
        // http.addFilterBefore(securityInterceptor(), FilterSecurityInterceptor.class);
        /*http.authorizeHttpRequests().withObjectPostProcessor(new ObjectPostProcessor<FilterSecurityInterceptor>(){
            @Override
            public <O extends FilterSecurityInterceptor> O postProcess(O object) {
                object.setAccessDecisionManager(accessDecisionManager());//决策管理器
                object.setSecurityMetadataSource(securityMetadataSource());//安全元数据源
                return object;
            }
        });*/
        // 登出配置
        http.logout(logout -> logout
                .logoutUrl("/logout")
                .logoutSuccessHandler(this.logoutSuccessHandler())
                .permitAll());
        // 2023-01-28 配置自定义认证提供者(密码验证用)
        http.authenticationProvider(this.authenticationProvider(userDetailsService, securityProperties()));
        // 配置匿名访问权限
        configureAnonymousAccess(http, securityProperties);
        // 配置自定义认证提供者
        http.authenticationProvider(this.authenticationProvider(userDetailsService, securityProperties));
        // 所有请求都需要认证
        http.authorizeHttpRequests().anyRequest().authenticated();
        // 使用自定义动态拦截器,拦截所有权限请求,2022-11-02
        http.authorizeHttpRequests(authorizeHttpRequests -> authorizeHttpRequests.anyRequest().authenticated());
        // 添加自定义动态拦截器
        http.addFilterBefore(securityInterceptor(), FilterSecurityInterceptor.class);
        // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        // token拦截过滤器,2022-11-02
        // 必须在这里添加拦截,不能放在'FilterSecurityInterceptor'之后,因为如果放在之后,那么就无法获得用户信息,从而无法
        // 获得用户所具有的权限角色集合:roleIdList。2022-11-14(2)
        // 添加JWT认证过滤器
        http.addFilterBefore(jwtAuthenticationTokenFilter(userDetailsService), UsernamePasswordAuthenticationFilter.class);
//        http.addFilterBefore(jwtAuthenticationTokenFilter(), DefaultAuthenticationFilter.class);
        // 尝试让jwt在URL权限之后才拦截, 2022-11-14(1)
        // 注意:以上 UsernamePasswordAuthenticationFilter 需要去掉才能生效
//        http.addFilterAfter(jwtAuthenticationTokenFilter(), FilterSecurityInterceptor.class);
        // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        if(this.securityProperties().isCorsEnabled()){
            // 解决跨域过滤器,2022-11-06
            http.addFilterBefore(this.corsFilter().getFilter(), JwtAuthenticationTokenFilter.class);
            // 未知?2022-11-11
            http.addFilterBefore(this.corsFilter().getFilter(), LogoutFilter.class);
        } else {
            System.out.println("不添加跨域过滤器: ");
        }
        // 配置跨域过滤器
        configureCorsFilter(http, securityProperties);
        return http.build();
    }
    /**
     * 配置匿名访问权限
     */
    private void configureAnonymousAccess(HttpSecurity http, SecurityProperties securityProperties) throws Exception {
        List<String> anonymousList = securityProperties.getAnonymousList();
        if (!CollectionUtils.isEmpty(anonymousList)) {
            http.authorizeHttpRequests(authorizeHttpRequests -> authorizeHttpRequests
                    .requestMatchers(anonymousList.toArray(new String[0])).permitAll());
        }
    }
    /**
     * 配置跨域过滤器
     */
    private void configureCorsFilter(HttpSecurity http, SecurityProperties securityProperties) throws Exception {
        if (securityProperties.isCorsEnabled()) {
            CorsFilter corsFilter = this.corsFilter().getFilter();
            if (corsFilter != null) {
                http.addFilterBefore(corsFilter, JwtAuthenticationTokenFilter.class)
                        .addFilterBefore(corsFilter, LogoutFilter.class);
                logger.info("跨域过滤器已启用");
            } else {
                logger.warn("跨域过滤器未正确初始化");
            }
        } else {
            logger.info("跨域过滤器未启用");
        }
    }
    /**
     * 获取AuthenticationManager(认证管理器),登录时认证使用
     * @param authenticationConfiguration
     * @return
iplatform-base-security-consum/src/main/java/com/iplatform/security/controller/SecurityController.java
@@ -218,7 +218,7 @@
            } else {
                // 2023-10-13
                logger.debug("存在业务自定义权限回调:{}", securityCallback.getClass().getName());
                Set<String> userPermission = securityCallback.loadUserPermission(userPrincipal.getUserInfo(), menuIdList);
                Set<String> userPermission = securityCallback.loadUserPermission(userPrincipal.getUserInfo(), menuIdList, null);
                if(userPermission != null){
                    perms.addAll(userPermission);
                }
iplatform-base-tcp/pom.xml
New file
@@ -0,0 +1,225 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>low-consum-manage</artifactId>
        <groupId>com.consum</groupId>
        <version>1.0.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.iplatform</groupId>
    <artifactId>iplatform-base-tcp</artifactId>
    <name>iplatform-base-tcp</name>
    <packaging>jar</packaging>
    <version>3.2.0</version>
    <dependencies>
        <!-- 依赖底层通信模块,2024-01-23 -->
        <dependency>
            <groupId>com.walkersoft</groupId>
            <artifactId>walker-tcp</artifactId>
            <version>${walker-web-security.version}</version>
            <systemPath>${project.basedir}/src/main/resources/lib/walker-tcp-3.2.0.jar</systemPath>
            <scope>system</scope>
        </dependency>
        <dependency>
            <groupId>org.postgresql</groupId>
            <artifactId>postgresql</artifactId>
            <version>4.2</version>
            <systemPath>${project.basedir}/src/main/resources/lib/tdsql-pg-connector-java8-1.0.5.jar</systemPath>
            <scope>system</scope>
        </dependency>
        <!-- 提供平台基础环境,2023-04-16 -->
        <dependency>
            <groupId>com.iplatform</groupId>
            <artifactId>iplatform-base</artifactId>
            <exclusions>
                <exclusion>
                    <artifactId>walker-web</artifactId>
                    <groupId>com.walkersoft</groupId>
                </exclusion>
                <exclusion>
                    <artifactId>jjwt</artifactId>
                    <groupId>io.jsonwebtoken</groupId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>com.consum</groupId>
            <artifactId>iplatform-base-security-consum</artifactId>
            <version>3.2.0</version>
            <exclusions>
                <exclusion>
                    <artifactId>walker-web</artifactId>
                    <groupId>com.walkersoft</groupId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>com.iplatform</groupId>
            <artifactId>iplatform-file-server</artifactId>
        </dependency>
        <!-- 阿里druid数据库连接池,如果使用直接引入到业务发布模块中,2023-03-15 -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
        </dependency>
        <dependency>
            <groupId>io.netty</groupId>
            <artifactId>netty-transport</artifactId>
            <version>${netty.version}</version>
        </dependency>
        <dependency>
            <groupId>io.netty</groupId>
            <artifactId>netty-codec</artifactId>
            <version>${netty.version}</version>
        </dependency>
        <dependency>
            <groupId>io.netty</groupId>
            <artifactId>netty-handler</artifactId>
            <version>${netty.version}</version>
        </dependency>
        <dependency>
            <groupId>io.netty</groupId>
            <artifactId>netty-codec-http</artifactId>
            <version>${netty.version}</version>
        </dependency>
        <!-- 扫码包下面的 TcpRequest 注解使用,2023-04-16 -->
        <dependency>
            <groupId>org.reflections</groupId>
            <artifactId>reflections</artifactId>
            <version>0.9.9</version>
        </dependency>
        <!-- RocketMQ Spring 框架依赖。2023-09-18 -->
        <dependency>
            <groupId>org.apache.rocketmq</groupId>
            <artifactId>rocketmq-spring-boot-starter</artifactId>
            <version>2.1.0</version>
            <exclusions>
                <exclusion>
                    <groupId>org.slf4j</groupId>
                    <artifactId>slf4j-api</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-autoconfigure</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-autoconfigure-processor</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-configuration-processor</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-starter-test</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>org.springframework</groupId>
                    <artifactId>spring-messaging</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>org.springframework</groupId>
                    <artifactId>spring-core</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>org.springframework</groupId>
                    <artifactId>spring-context</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>org.springframework</groupId>
                    <artifactId>spring-aop</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>com.fasterxml.jackson.core</groupId>
                    <artifactId>jackson-databind</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-starter</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-starter-validation</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-messaging</artifactId>
        </dependency>
        <!--websocket作为客户端-->
        <dependency>
            <groupId>org.java-websocket</groupId>
            <artifactId>Java-WebSocket</artifactId>
            <version>1.5.4</version>
            <scope>test</scope>
        </dependency>
    </dependencies>
    <build>
        <finalName>ctoms-websocket</finalName>
        <plugins>
            <plugin>
                <!--能够自动寻找springboot引导类,或者手动指定-->
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>${spring-boot.version}</version>
                <executions>
                    <execution>
                        <goals>
                            <goal>repackage</goal>
                        </goals>
                    </execution>
                </executions>
                <configuration>
                    <!-- 2023-11-06 该参数指定了依赖的本地类库,不引入则报错:不能指向本地jar文件 -->
                    <arguments>
                        <argument>${project.basedir}/src/main/resources/lib</argument>
                    </arguments>
                    <!-- 2023-11-06 该参数指定,打包忽略 Main 函数。-->
                    <!--                    <skip>true</skip>-->
                    <!--本地包导入配置-->
                    <includeSystemScope>true</includeSystemScope>
                    <!--自定义分类器配置,防止父子项目打包失败-->
                    <!--                    <classifier>exec</classifier>-->
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-deploy-plugin</artifactId>
                <version>${maven-deploy-plugin.version}</version>
                <executions>
                    <execution>
                        <id>default-deploy</id>
                        <phase>deploy</phase>
                        <goals>
                            <goal>deploy</goal>
                        </goals>
                        <!-- skip默认deploy插件的执行 -->
                        <configuration>
                            <skip>true</skip>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</project>
iplatform-base-tcp/src/main/java/com/iplatform/CtomsTcpApplication.java
New file
@@ -0,0 +1,23 @@
package com.iplatform;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
import org.springframework.scheduling.annotation.EnableAsync;
@SpringBootApplication(scanBasePackages = {"com.walker","com.iplatform"})
@EnableAsync
public class CtomsTcpApplication {
    public static void main(String[] args) throws Exception{
        SpringApplication.run(CtomsTcpApplication.class, args);
    }
//    @Bean
//    public static PropertySourcesPlaceholderConfigurer placeholderConfigurer() {
//        PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer = new PropertySourcesPlaceholderConfigurer();
//        propertySourcesPlaceholderConfigurer.setIgnoreUnresolvablePlaceholders(true);
//        return propertySourcesPlaceholderConfigurer;
//    }
}
iplatform-base-tcp/src/main/java/com/iplatform/tcp/Constants.java
New file
@@ -0,0 +1,16 @@
package com.iplatform.tcp;
public class Constants {
    public static final String CACHE_NAME_EQUIPMENT = "cache.tcp.equip";
    public static final String CACHE_NAME_EQUIPMENT_STATUS = "cache.tcp.equip_status";
    /**
     * TCP模块缓存:长连接对象缓存。
     * @date 2023-09-23
     */
    public static final String CACHE_NAME_CONNECTION = "cache.tcp.connect";
//    public static final String QUEUE_TYPE_MEMORY = "memory";
}
iplatform-base-tcp/src/main/java/com/iplatform/tcp/EngineType.java
New file
@@ -0,0 +1,37 @@
package com.iplatform.tcp;
/**
 * TCP通信引擎类型定义。
 * @author 时克英
 * @date 2023-04-17
 */
public enum EngineType {
    TcpEngine{
        public int getIndex(){
            return INDEX_TCP_ENGINE;
        }
    },
    WebsocketEngine{
        public int getIndex(){
            return INDEX_TCP_WEBSOCKET;
        }
    };
    public int getIndex(){
        throw new AbstractMethodError();
    }
    public EngineType getType(int index){
        if(index == INDEX_TCP_ENGINE){
            return TcpEngine;
        } else if(index == INDEX_TCP_WEBSOCKET){
            return WebsocketEngine;
        } else {
            throw new UnsupportedOperationException("不支持的通信引擎:" + index);
        }
    }
    public static final int INDEX_TCP_ENGINE = 1;
    public static final int INDEX_TCP_WEBSOCKET = 2;
}
iplatform-base-tcp/src/main/java/com/iplatform/tcp/EquipmentCacheProvider.java
New file
@@ -0,0 +1,17 @@
package com.iplatform.tcp;
import com.iplatform.model.po.TcpEquip;
/**
 * TCP设备缓存定义。<p>设备编号唯一,在所有机构中也是唯一的</p>
 * @author 时克英
 * @date 2023-04-16
 */
public interface EquipmentCacheProvider {
    void putEquipment(String key, TcpEquip tcpEquip);
    TcpEquip getEquipment(String key);
    void removeEquipment(String key);
}
iplatform-base-tcp/src/main/java/com/iplatform/tcp/EquipmentStatusCacheProvider.java
New file
@@ -0,0 +1,17 @@
package com.iplatform.tcp;
import com.iplatform.model.po.TcpEquipStatus;
/**
 * 设备状态缓存,一个设备对应一条记录。
 * @author 时克英
 * @date 2023-04-16
 */
public interface EquipmentStatusCacheProvider {
    void putEquipmentStatus(String key, TcpEquipStatus tcpEquip);
    TcpEquipStatus getEquipmentStatus(String key);
    void removeEquipmentStatus(String key);
}
iplatform-base-tcp/src/main/java/com/iplatform/tcp/LiveStatus.java
New file
@@ -0,0 +1,44 @@
package com.iplatform.tcp;
/**
 * 通讯连接状态枚举类型定义
 * @author 时克英
 * @date 2018-11-29
 *
 */
public enum LiveStatus {
    /**
     * 未连接:0
     */
    NotConnect {
        public int getTypeValue(){
            return CONST_NOT_CONNECT;
        }
    },
    /**
     * 已连接:1
     */
    Connected {
        public int getTypeValue(){
            return CONST_CONNECTED;
        }
    };
    public static final int CONST_CONNECTED = 1;
    public static final int CONST_NOT_CONNECT = 0;
    public int getTypeValue(){
        throw new AbstractMethodError();
    }
    public static LiveStatus getType(int index){
        if(index == CONST_CONNECTED){
            return Connected;
        } else if(index == CONST_NOT_CONNECT){
            return NotConnect;
        } else
            throw new IllegalArgumentException("unsupported LiveStatus: " + index);
    }
}
iplatform-base-tcp/src/main/java/com/iplatform/tcp/TcpBaseController.java
New file
@@ -0,0 +1,6 @@
package com.iplatform.tcp;
import com.iplatform.base.SystemController;
public abstract class TcpBaseController extends SystemController {
}
iplatform-base-tcp/src/main/java/com/iplatform/tcp/cache/LocalEquipStatusCacheProvider.java
New file
@@ -0,0 +1,57 @@
package com.iplatform.tcp.cache;
import com.iplatform.model.po.TcpEquipStatus;
import com.iplatform.tcp.Constants;
import com.iplatform.tcp.EquipmentStatusCacheProvider;
import com.iplatform.tcp.service.TcpEquipStatusServiceImpl;
import com.walker.cache.AbstractCacheProvider;
import com.walker.cache.Cache;
import com.walker.infrastructure.utils.StringUtils;
import java.util.List;
public class LocalEquipStatusCacheProvider extends AbstractCacheProvider<TcpEquipStatus> implements EquipmentStatusCacheProvider {
    @Override
    public void putEquipmentStatus(String key, TcpEquipStatus tcpEquipStatus) {
        this.putCacheData(key, tcpEquipStatus);
    }
    @Override
    public TcpEquipStatus getEquipmentStatus(String key) {
        return this.getCacheData(key);
    }
    @Override
    public void removeEquipmentStatus(String key) {
        this.removeCacheData(key);
    }
    @Override
    protected int loadDataToCache(Cache cache) {
        List<TcpEquipStatus> hosts = this.tcpEquipStatusService.selectAll(new TcpEquipStatus());
        if(!StringUtils.isEmptyList(hosts)){
            for(TcpEquipStatus h : hosts){
                cache.put(h.getEquipNum(), h);
            }
            return hosts.size();
        }
        return 0;
    }
    @Override
    public String getProviderName() {
        return Constants.CACHE_NAME_EQUIPMENT_STATUS;
    }
    @Override
    public Class<?> getProviderType() {
        return TcpEquipStatus.class;
    }
    public void setTcpEquipStatusService(TcpEquipStatusServiceImpl tcpEquipStatusService) {
        this.tcpEquipStatusService = tcpEquipStatusService;
    }
    private TcpEquipStatusServiceImpl tcpEquipStatusService;
}
iplatform-base-tcp/src/main/java/com/iplatform/tcp/cache/LocalEquipmentCacheProvider.java
New file
@@ -0,0 +1,57 @@
package com.iplatform.tcp.cache;
import com.iplatform.model.po.TcpEquip;
import com.iplatform.tcp.Constants;
import com.iplatform.tcp.EquipmentCacheProvider;
import com.iplatform.tcp.service.TcpEquipServiceImpl;
import com.walker.cache.AbstractCacheProvider;
import com.walker.cache.Cache;
import com.walker.infrastructure.utils.StringUtils;
import java.util.List;
public class LocalEquipmentCacheProvider extends AbstractCacheProvider<TcpEquip> implements EquipmentCacheProvider {
    @Override
    protected int loadDataToCache(Cache cache) {
        List<TcpEquip> hosts = this.tcpEquipService.selectAll(new TcpEquip());
        if(!StringUtils.isEmptyList(hosts)){
            for(TcpEquip h : hosts){
                cache.put(h.getNum(), h);
            }
            return hosts.size();
        }
        return 0;
    }
    @Override
    public String getProviderName() {
        return Constants.CACHE_NAME_EQUIPMENT;
    }
    @Override
    public Class<?> getProviderType() {
        return TcpEquip.class;
    }
    public void setTcpEquipService(TcpEquipServiceImpl tcpEquipService) {
        this.tcpEquipService = tcpEquipService;
    }
    private TcpEquipServiceImpl tcpEquipService;
    @Override
    public void putEquipment(String key, TcpEquip tcpEquip) {
        this.putCacheData(key, tcpEquip);
    }
    @Override
    public TcpEquip getEquipment(String key) {
        return this.getCacheData(key);
    }
    @Override
    public void removeEquipment(String key) {
        this.removeCacheData(key);
    }
}
iplatform-base-tcp/src/main/java/com/iplatform/tcp/cache/RedisEquipStatusCacheProvider.java
New file
@@ -0,0 +1,69 @@
package com.iplatform.tcp.cache;
import com.iplatform.model.po.TcpEquipStatus;
import com.iplatform.tcp.Constants;
import com.iplatform.tcp.EquipmentStatusCacheProvider;
import com.iplatform.tcp.service.TcpEquipStatusServiceImpl;
import com.walker.cache.Cache;
import com.walker.support.redis.cache.RedisCacheProvider;
public class RedisEquipStatusCacheProvider extends RedisCacheProvider<TcpEquipStatus> implements EquipmentStatusCacheProvider {
    public RedisEquipStatusCacheProvider(){
        this.setUseRedis(true);
        this.setLoadPage(false);
    }
    @Override
    public void putEquipmentStatus(String key, TcpEquipStatus tcpEquipStatus) {
        this.putCacheData(key, tcpEquipStatus);
    }
    @Override
    public TcpEquipStatus getEquipmentStatus(String key) {
        return this.getCacheData(key);
    }
    @Override
    public void removeEquipmentStatus(String key) {
        this.removeCacheData(key);
    }
    @Override
    public String getProviderName() {
        return Constants.CACHE_NAME_EQUIPMENT_STATUS;
    }
    @Override
    public Class<?> getProviderType() {
        return TcpEquipStatus.class;
    }
    @Override
    protected int loadDataToCache(Cache cache) {
//        List<TcpEquipStatus> hosts = this.tcpEquipStatusService.selectAll(new TcpEquipStatus());
//        if(!StringUtils.isEmptyList(hosts)){
//            // ------------------------- 切换成普通缓存步骤:3
//            if(this.isUseRedis()){
//                // 如果redis中缓存数量和数据库中不一致(少),则清空redis缓存,重新加载数据库数据到缓存中。
//                long totalCache = cache.getPersistentSize();
//                if(totalCache != hosts.size()){
//                    logger.info("redis缓存中用户数量小于实际用户,需要清空缓存重新加载! cache = " + totalCache + ", db = " + hosts.size());
//                    cache.clear();
//
//                    for(TcpEquipStatus h : hosts){
//                        cache.put(h.getEquipNum(), h);
//                    }
//                }
//            }//------------------------------------------
//            return hosts.size();
//        }
        return 0;
    }
    public void setTcpEquipStatusService(TcpEquipStatusServiceImpl tcpEquipStatusService) {
        this.tcpEquipStatusService = tcpEquipStatusService;
    }
    private TcpEquipStatusServiceImpl tcpEquipStatusService;
}
iplatform-base-tcp/src/main/java/com/iplatform/tcp/cache/RedisEquipmentCacheProvider.java
New file
@@ -0,0 +1,69 @@
package com.iplatform.tcp.cache;
import com.iplatform.model.po.TcpEquip;
import com.iplatform.tcp.Constants;
import com.iplatform.tcp.EquipmentCacheProvider;
import com.iplatform.tcp.service.TcpEquipServiceImpl;
import com.walker.cache.Cache;
import com.walker.support.redis.cache.RedisCacheProvider;
public class RedisEquipmentCacheProvider extends RedisCacheProvider<TcpEquip> implements EquipmentCacheProvider {
    public RedisEquipmentCacheProvider(){
        this.setUseRedis(true);
        this.setLoadPage(false);
    }
    @Override
    public void putEquipment(String key, TcpEquip tcpEquip) {
        this.putCacheData(key, tcpEquip);
    }
    @Override
    public TcpEquip getEquipment(String key) {
        return this.getCacheData(key);
    }
    @Override
    public void removeEquipment(String key) {
        this.removeCacheData(key);
    }
    @Override
    public String getProviderName() {
        return Constants.CACHE_NAME_EQUIPMENT;
    }
    @Override
    public Class<?> getProviderType() {
        return TcpEquip.class;
    }
    @Override
    protected int loadDataToCache(Cache cache) {
//        List<TcpEquip> hosts = this.tcpEquipService.selectAll(new TcpEquip());
//        if(!StringUtils.isEmptyList(hosts)){
//            // ------------------------- 切换成普通缓存步骤:3
//            if(this.isUseRedis()){
//                // 如果redis中缓存数量和数据库中不一致(少),则清空redis缓存,重新加载数据库数据到缓存中。
//                long totalCache = cache.getPersistentSize();
//                if(totalCache != hosts.size()){
//                    logger.info("redis缓存中用户数量小于实际用户,需要清空缓存重新加载! cache = " + totalCache + ", db = " + hosts.size());
//                    cache.clear();
//
//                    for(TcpEquip h : hosts){
//                        cache.put(h.getNum(), h);
//                    }
//                }
//            }//------------------------------------------
//            return hosts.size();
//        }
        return 0;
    }
    public void setTcpEquipService(TcpEquipServiceImpl tcpEquipService) {
        this.tcpEquipService = tcpEquipService;
    }
    private TcpEquipServiceImpl tcpEquipService;
}
iplatform-base-tcp/src/main/java/com/iplatform/tcp/config/LoadBalanceMQConfig.java
New file
@@ -0,0 +1,82 @@
package com.iplatform.tcp.config;
import com.iplatform.core.PlatformConfiguration;
import com.iplatform.tcp.lb.DefaultResponseWriter;
import com.walker.push.rocketmq.RocketMQEnhanceTemplate;
import com.walker.tcp.lb.ResponseWriter;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
 * 长连接负载方式时,启用MQ模板以及相关配置。
 * @author 时克英
 * @date 2023-09-26
 */
@Configuration
@ConditionalOnProperty(prefix = "iplatform.tcp", name = "load-balance-enabled", havingValue = "true", matchIfMissing = false)
@ConditionalOnClass({RocketMQTemplate.class})
public class LoadBalanceMQConfig extends PlatformConfiguration {
    private RocketMQTemplate rocketMQTemplate;
    @Autowired
    public LoadBalanceMQConfig(RocketMQTemplate rocketMQTemplate){
        this.rocketMQTemplate = rocketMQTemplate;
    }
    /**
     * 默认RocketMQ监听器配置,测试使用。正式需要删除
     * @param connectionManager
//     * @param template
     * @return
     * @date 2023-09-27
     */
//    @Bean
//    public DefaultMqListener defaultMqListener(ConnectionManager connectionManager, RocketMQEnhanceTemplate template){
//        DefaultMqListener listener = new DefaultMqListener();
//        listener.setConnectionManager(connectionManager);
//        listener.setRocketMQEnhanceTemplate(template);
//        return listener;
//    }
    /**
     * 监听对象放在业务中处理,平台注释该对象,业务通过MQ接收其他节点发送的聊天消息。<br>
     *
     * 1)如果要在平台测试负责聊天,请放开注释,启用:SimpleMqListener
     * @date 2023-12-14
     */
//    @Bean
//    public SimpleMqListener simpleMqListener(ConnectionManager connectionManager){
//        SimpleMqListener listener = new SimpleMqListener();
//        listener.setConnectionManager(connectionManager);
//        return listener;
//    }
    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    //~ 以下为正式配置,上面为测试内容。
    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    /**
     * 长连接响应消息写入通道具体实现。
     * <p>目前在负载模式中,发送消息到MQ,因为<code>LongConnectionMeta</code>从Redis反序列化后,无法执行子类write,所以通过该对象完成写入。</p>
     * @return
     * @date 2023-09-27
     */
    @Bean
    public ResponseWriter responseWriter(){
        return new DefaultResponseWriter();
    }
    @Bean
    public RocketMQEnhanceTemplate rocketMQEnhanceTemplate(){
        RocketMQEnhanceTemplate template = new RocketMQEnhanceTemplate();
        template.setTemplate(this.rocketMQTemplate);
//        template.setEnabledIsolation();
//        template.setEnvironmentName();
        return template;
    }
}
iplatform-base-tcp/src/main/java/com/iplatform/tcp/config/LocalTcpCacheConfig.java
New file
@@ -0,0 +1,43 @@
package com.iplatform.tcp.config;
import com.iplatform.core.PlatformConfiguration;
import com.iplatform.tcp.EquipmentCacheProvider;
import com.iplatform.tcp.EquipmentStatusCacheProvider;
import com.iplatform.tcp.cache.LocalEquipStatusCacheProvider;
import com.iplatform.tcp.cache.LocalEquipmentCacheProvider;
import com.iplatform.tcp.service.TcpEquipServiceImpl;
import com.iplatform.tcp.service.TcpEquipStatusServiceImpl;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
@ConditionalOnProperty(prefix = "iplatform.cache", name = "redis-enabled", havingValue = "false", matchIfMissing = true)
public class LocalTcpCacheConfig extends PlatformConfiguration {
    /**
     * 配置TCP设备信息缓存
     * @param tcpEquipService
     * @return
     * @date 2023-04-16
     */
    @Bean
    public EquipmentCacheProvider equipmentCacheProvider(TcpEquipServiceImpl tcpEquipService){
        LocalEquipmentCacheProvider localEquipmentCacheProvider = new LocalEquipmentCacheProvider();
        localEquipmentCacheProvider.setTcpEquipService(tcpEquipService);
        return localEquipmentCacheProvider;
    }
    /**
     * 配置设备状态缓存,一个设备一条记录。
     * @param tcpEquipStatusService
     * @return
     * @date 2023-04-16
     */
    @Bean
    public EquipmentStatusCacheProvider equipmentStatusCacheProvider(TcpEquipStatusServiceImpl tcpEquipStatusService){
        LocalEquipStatusCacheProvider localEquipStatusCacheProvider = new LocalEquipStatusCacheProvider();
        localEquipStatusCacheProvider.setTcpEquipStatusService(tcpEquipStatusService);
        return localEquipStatusCacheProvider;
    }
}
iplatform-base-tcp/src/main/java/com/iplatform/tcp/config/RedisTcpCacheConfig.java
New file
@@ -0,0 +1,69 @@
package com.iplatform.tcp.config;
import com.iplatform.core.PlatformConfiguration;
import com.iplatform.tcp.EquipmentCacheProvider;
import com.iplatform.tcp.EquipmentStatusCacheProvider;
import com.iplatform.tcp.cache.RedisEquipStatusCacheProvider;
import com.iplatform.tcp.cache.RedisEquipmentCacheProvider;
import com.iplatform.tcp.service.TcpEquipServiceImpl;
import com.iplatform.tcp.service.TcpEquipStatusServiceImpl;
import com.walker.support.redis.RedisHelper;
import com.walker.support.redis.cache.RedisCacheProvider;
import com.walker.tcp.lb.RedisConnectionMetaCache;
import com.walker.tcp.lb.RedisConnectionNameCache;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
@ConditionalOnProperty(prefix = "iplatform.cache", name = "redis-enabled", havingValue = "true", matchIfMissing = false)
@ConditionalOnClass({RedisCacheProvider.class})
@AutoConfigureAfter(RedisAutoConfiguration.class)
public class RedisTcpCacheConfig extends PlatformConfiguration {
    /**
     * 长连接负载模式中,连接(注册)名称与通道ID关系缓存定义。
     * @param redisHelper
     * @return
     * @date 2023-09-26
     */
    @Bean
    public RedisConnectionNameCache redisConnectionNameCache(RedisHelper redisHelper){
        RedisConnectionNameCache cache = new RedisConnectionNameCache();
        cache.setRedisHelper(redisHelper);
        return cache;
    }
    /**
     * 长连接负载模式中,连接元数据缓存定义。
     * @param redisHelper
     * @return
     * @date 2023-09-26
     */
    @Bean
    public RedisConnectionMetaCache redisConnectionMetaCache(RedisHelper redisHelper){
        RedisConnectionMetaCache cache = new RedisConnectionMetaCache();
        cache.setRedisHelper(redisHelper);
//        cache.setExpiredSeconds();
        return cache;
    }
    @Bean
    public EquipmentCacheProvider equipmentCacheProvider(RedisHelper redisHelper, TcpEquipServiceImpl tcpEquipService){
        RedisEquipmentCacheProvider redisEquipmentCacheProvider = new RedisEquipmentCacheProvider();
        redisEquipmentCacheProvider.setRedisHelper(redisHelper);
        redisEquipmentCacheProvider.setTcpEquipService(tcpEquipService);
        return redisEquipmentCacheProvider;
    }
    @Bean
    public EquipmentStatusCacheProvider equipmentStatusCacheProvider(RedisHelper redisHelper, TcpEquipStatusServiceImpl tcpEquipStatusService){
        RedisEquipStatusCacheProvider redisEquipStatusCacheProvider = new RedisEquipStatusCacheProvider();
        redisEquipStatusCacheProvider.setRedisHelper(redisHelper);
        redisEquipStatusCacheProvider.setTcpEquipStatusService(tcpEquipStatusService);
        return redisEquipStatusCacheProvider;
    }
}
iplatform-base-tcp/src/main/java/com/iplatform/tcp/config/TcpBeanPostProcessorConfig.java
New file
@@ -0,0 +1,29 @@
package com.iplatform.tcp.config;
import com.walker.tcp.ActionCallablePostProcessor;
import com.walker.tcp.ConnectionManager;
import com.walker.tcp.ProtocolResolverPostProcessor;
import com.walker.tcp.RequestPostProcessor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class TcpBeanPostProcessorConfig {
//    public static ConnectionManager connectionManager;
    @Bean
    public ProtocolResolverPostProcessor protocolResolverPostProcessor(){
        return new ProtocolResolverPostProcessor();
    }
    @Bean
    public ActionCallablePostProcessor actionCallablePostProcessor(){
        return new ActionCallablePostProcessor();
    }
    @Bean
    public RequestPostProcessor requestPostProcessor(){
        return new RequestPostProcessor();
    }
}
iplatform-base-tcp/src/main/java/com/iplatform/tcp/config/TcpConfig.java
New file
@@ -0,0 +1,213 @@
package com.iplatform.tcp.config;
import com.iplatform.base.config.TcpProperties;
import com.iplatform.core.PlatformConfiguration;
import com.iplatform.core.config.LoadBalanceProperties;
import com.iplatform.tcp.EngineType;
import com.iplatform.tcp.EquipmentCacheProvider;
import com.iplatform.tcp.EquipmentStatusCacheProvider;
import com.iplatform.tcp.lb.DefaultLbConnectionManager;
import com.iplatform.tcp.service.TcpEquipStatusServiceImpl;
import com.iplatform.tcp.support.PersistentConnectionManager;
import com.iplatform.tcp.support.PlatformSharpProtocolResolver;
import com.walker.infrastructure.ApplicationRuntimeException;
import com.walker.push.rocketmq.RocketQueueManager;
import com.walker.queue.QueueManager;
import com.walker.tcp.ConnectionManager;
import com.walker.tcp.ProtocolResolver;
import com.walker.tcp.handler.LongHandler;
import com.walker.tcp.lb.RedisConnectionMetaCache;
import com.walker.tcp.lb.RedisConnectionNameCache;
import com.walker.tcp.lb.ResponseWriter;
import com.walker.tcp.netty.DefaultLongEngine;
import com.walker.tcp.netty.DefaultLongHandler;
import com.walker.tcp.netty.DefaultServerInitializer;
import com.walker.tcp.protocol.LineProtocolResolver;
import com.walker.tcp.support.MemoryQueueManager;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.lang.Nullable;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@Configuration
@ConditionalOnProperty(prefix = "iplatform.tcp", name = "enabled", havingValue = "true", matchIfMissing = false)
public class TcpConfig extends PlatformConfiguration {
    // 迁移到base中
//    @Bean
//    public TcpProperties tcpProperties(){
//        return new TcpProperties();
//    }
    /**
     * 配置设备连接管理器。
     * @param equipmentCacheProvider
     * @param equipmentStatusCacheProvider
     * @param tcpEquipStatusService
     * @return
     * @date 2023-04-16
     * @date 2023-07-19 添加条件限制,如果设置自定义连接管理器,则平台不再创建,由业务自己配置。
     * @date 2023-09-26 添加长连接元数据缓存配置,仅在集群模式可用。
     */
    @ConditionalOnProperty(prefix = "iplatform.tcp", name = "custom-connection-manager", havingValue = "false", matchIfMissing = false)
    @Bean
    public ConnectionManager connectionManager(EquipmentCacheProvider equipmentCacheProvider
            , EquipmentStatusCacheProvider equipmentStatusCacheProvider, TcpEquipStatusServiceImpl tcpEquipStatusService
            , TcpProperties tcpProperties
            , @Nullable RedisConnectionMetaCache connectionMetaCache, @Nullable RedisConnectionNameCache connectionNameCache
            , @Nullable ResponseWriter responseWriter){
        Map<Integer, String> connectionTypeMap = new HashMap<>(4);
        connectionTypeMap.put(EngineType.INDEX_TCP_ENGINE, "TCP长连接");
        connectionTypeMap.put(EngineType.INDEX_TCP_WEBSOCKET, "WebSocket连接");
        if(tcpProperties.isLoadBalanceEnabled()){
            DefaultLbConnectionManager connectionManager = new DefaultLbConnectionManager();
            connectionManager.setMultipleEngine(true);
            connectionManager.setConnectionTypeMap(connectionTypeMap);
            // 下面这两个缓存参数,只有在Redis启用时才存在,单机模式可为空。2023-09-26
            connectionManager.setConnectionMetaCache(connectionMetaCache);
            connectionManager.setConnectionNameCache(connectionNameCache);
            connectionManager.setConnectionHost(tcpProperties.getConnectionHost());
            connectionManager.setResponseWriter(responseWriter);
//            connectionManager.setEquipCache(equipmentCacheProvider);
//            connectionManager.setStatusCache(equipmentStatusCacheProvider);
//            connectionManager.setTcpEquipStatusService(tcpEquipStatusService);
            logger.debug("getConnectionHost = {}", tcpProperties.getConnectionHost());
            logger.info("connectionMetaCache = {}", connectionMetaCache.getClass().getName());
            return connectionManager;
        } else {
            PersistentConnectionManager connectionManager = new PersistentConnectionManager();
            connectionManager.setMultipleEngine(true);
            connectionManager.setConnectionTypeMap(connectionTypeMap);
            connectionManager.setEquipCache(equipmentCacheProvider);
            connectionManager.setStatusCache(equipmentStatusCacheProvider);
            connectionManager.setTcpEquipStatusService(tcpEquipStatusService);
            return connectionManager;
        }
    }
    /**
     * 配置协议解析器:最基本的文本换行,一行是一个消息体。
     * @return
     * @date 2023-04-16
     */
    @Bean
    public LineProtocolResolver lineProtocolResolver(){
        return new LineProtocolResolver();
    }
    @Bean
    public PlatformSharpProtocolResolver platformSharpProtocolResolver(){
        PlatformSharpProtocolResolver resolver = new PlatformSharpProtocolResolver();
        return resolver;
    }
    /**
     * 配置消息队列管理器实现。
     * @param connectionManager
     * @return
     */
    @Bean
    public QueueManager queueManager(ConnectionManager connectionManager
            , TcpProperties tcpProperties, ThreadPoolTaskExecutor executor){
        if(tcpProperties.isLoadBalanceEnabled()){
            RocketQueueManager queueManager = new RocketQueueManager();
            queueManager.setConnectionManager(connectionManager);
            queueManager.setId(1);
            queueManager.setName("报文消息队列管理器【集群】");
            queueManager.setExecutor(executor);
            queueManager.startup();
            return queueManager;
        } else {
            MemoryQueueManager queueManager = new MemoryQueueManager();
            queueManager.setConnectionManager(connectionManager);
            queueManager.setId(1);
            queueManager.setName("报文消息队列管理器【内置】");
            queueManager.setMaxWorkerThread(1);
            queueManager.startup(); // 必须启动
            return queueManager;
        }
    }
    private List<ProtocolResolver<?>> acquireProtocolResolverList(){
        List<ProtocolResolver<?>> protocolResolverList = new ArrayList<>(4);
        protocolResolverList.add(lineProtocolResolver());
        protocolResolverList.add(platformSharpProtocolResolver());
        return protocolResolverList;
    }
    @Bean
    public LongHandler tcpServerHandler(QueueManager queueManager
            , ConnectionManager connectionManager, TcpProperties tcpProperties){
        logger.debug("...... tcp_scan_packages = {}", tcpProperties.getScanPackagesTcp());
        LongHandler longHandler = new LongHandler();
        longHandler.setEmptyMsgDisconnect(false);
        longHandler.setEngineId(EngineType.INDEX_TCP_ENGINE);
//        longHandler.setScanPackages("com.walker.tcp,com.iplatform.tcp");
        longHandler.setScanPackages(tcpProperties.getScanPackagesTcp());
        longHandler.setQueueManager(queueManager);
        longHandler.setConnectionManager(connectionManager);
        longHandler.setProtocolResolverList(this.acquireProtocolResolverList());
        longHandler.setConnectionHost(tcpProperties.getConnectionHost());
        return longHandler;
    }
    @Bean
    public DefaultLongHandler nettyLongHandler(ConnectionManager connectionManager, LongHandler tcpServerHandler){
        DefaultLongHandler longHandler = new DefaultLongHandler();
        longHandler.setConnectionManager(connectionManager);
        longHandler.setTcpServerHandler(tcpServerHandler);
        return longHandler;
    }
    /**
     * 配置:TCP 服务初始化器。
     * @param nettyLongHandler
     * @return
     * @date 2023-04-16
     */
    @Bean
    public DefaultServerInitializer nettyServerInitializer(DefaultLongHandler nettyLongHandler, TcpProperties tcpProperties){
        DefaultServerInitializer initializer = new DefaultServerInitializer();
        initializer.setHandler(nettyLongHandler);
        initializer.setProtocolResolverList(this.acquireProtocolResolverList());
        initializer.setShowLog(tcpProperties.isShowLog());
//        initializer.setTimeOutAll();
        return initializer;
    }
    /**
     * 配置TCP引擎。
     * @param nettyServerInitializer
     * @param connectionManager
     * @return
     */
    @Bean
    public DefaultLongEngine tcpLongEngine(DefaultServerInitializer nettyServerInitializer
            , ConnectionManager connectionManager, TcpProperties tcpProperties
            , LoadBalanceProperties loadBalanceProperties){
        DefaultLongEngine longEngine = new DefaultLongEngine();
        longEngine.setId(EngineType.INDEX_TCP_ENGINE);
        longEngine.setName("测试TCP长连接引擎");
        longEngine.setServerInitializer(nettyServerInitializer);
        longEngine.setConnectionManager(connectionManager);
        longEngine.setPort(tcpProperties.getPortTcp());
        longEngine.setBossThreadNum(tcpProperties.getBossThreadNum());
        longEngine.setWorkerThreadNum(tcpProperties.getWorkerThreadNum());
        longEngine.setOpenHeartBeat(tcpProperties.isOpenHeartBeat());
        try {
            longEngine.start();
        } catch (Exception e) {
            throw new ApplicationRuntimeException("启动'DefaultLongEngine'失败:" + e.getMessage(), e);
        }
        return longEngine;
    }
}
iplatform-base-tcp/src/main/java/com/iplatform/tcp/config/WebsocketConfig.java
New file
@@ -0,0 +1,147 @@
package com.iplatform.tcp.config;
import com.iplatform.base.config.TcpProperties;
import com.iplatform.core.config.LoadBalanceProperties;
import com.iplatform.tcp.EngineType;
import com.iplatform.tcp.support.TestConnectionCallback;
import com.iplatform.tcp.support.WebSocketPush;
import com.iplatform.tcp.util.ws.LoginAction;
import com.walker.infrastructure.ApplicationRuntimeException;
import com.walker.push.PushManager;
import com.walker.push.Pushable;
import com.walker.queue.QueueManager;
import com.walker.tcp.ConnectionCallback;
import com.walker.tcp.ConnectionManager;
import com.walker.tcp.ProtocolResolver;
import com.walker.tcp.netty.WebSocketEngine;
import com.walker.tcp.netty.WebSocketNettyHandler;
import com.walker.tcp.netty.WebSocketServerInitializer;
import com.walker.tcp.websocket.WebsocketHandler;
import com.walker.tcp.websocket.WebsocketProtocolResolver;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.lang.Nullable;
import java.util.ArrayList;
import java.util.List;
@Configuration
@ConditionalOnProperty(prefix = "iplatform.tcp", name = "enabled", havingValue = "true", matchIfMissing = false)
public class WebsocketConfig {
    @Bean
    public WebsocketProtocolResolver websocketProtocolResolver(){
        WebsocketProtocolResolver websocketProtocolResolver =  new WebsocketProtocolResolver();
        websocketProtocolResolver.setPermitNotRegisterConnect(false);
        return websocketProtocolResolver;
    }
    private List<ProtocolResolver<?>> acquireProtocolResolverList(){
        List<ProtocolResolver<?>> protocolResolverList = new ArrayList<>(2);
        protocolResolverList.add(websocketProtocolResolver());
        return protocolResolverList;
    }
    /**
     * 配置业务定义的处理方法实现。
     * @param connectionManager
     * @param queueManager
     * @return
     * @date 2023-04-17
     */
    @Bean
    public WebsocketHandler wsServerHandler(ConnectionManager connectionManager
            , QueueManager queueManager, TcpProperties tcpProperties){
        WebsocketHandler websocketHandler = new WebsocketHandler();
        websocketHandler.setEmptyMsgDisconnect(false);
        websocketHandler.setEngineId(EngineType.INDEX_TCP_WEBSOCKET);
        websocketHandler.setConnectionManager(connectionManager);
        websocketHandler.setQueueManager(queueManager);
        websocketHandler.setProtocolResolverList(this.acquireProtocolResolverList());
//        websocketHandler.setScanPackages("com.iplatform.tcp.util.ws");
        websocketHandler.setScanPackages(tcpProperties.getScanPackagesWs());
        // 2023-09-27
        websocketHandler.setConnectionHost(tcpProperties.getConnectionHost());
        return websocketHandler;
    }
    @Bean
    public WebSocketNettyHandler wsNettyLongHandler(ConnectionManager connectionManager
            , WebsocketHandler wsServerHandler, TcpProperties tcpProperties){
        WebSocketNettyHandler webSocketNettyHandler = new WebSocketNettyHandler();
        webSocketNettyHandler.setConnectionManager(connectionManager);
        webSocketNettyHandler.setTcpServerHandler(wsServerHandler);
        webSocketNettyHandler.setUri(tcpProperties.getWebsocketUri());
        return webSocketNettyHandler;
    }
    @Bean
    public WebSocketServerInitializer wsServerInitializer(WebSocketNettyHandler wsNettyLongHandler, TcpProperties tcpProperties){
        WebSocketServerInitializer webSocketServerInitializer = new WebSocketServerInitializer();
        webSocketServerInitializer.setHandler(wsNettyLongHandler);
        webSocketServerInitializer.setProtocolResolverList(this.acquireProtocolResolverList());
        webSocketServerInitializer.setShowLog(tcpProperties.isShowLog());
        return webSocketServerInitializer;
    }
    @Bean
    public WebSocketEngine webSocketEngine(ConnectionManager connectionManager
            , WebSocketServerInitializer webSocketServerInitializer
            , TcpProperties tcpProperties, @Nullable ConnectionCallback connectionCallback
            , LoadBalanceProperties loadBalanceProperties){
        WebSocketEngine webSocketEngine = new WebSocketEngine();
        webSocketEngine.setId(EngineType.INDEX_TCP_WEBSOCKET);
        webSocketEngine.setName("websocket测试引擎");
        webSocketEngine.setConnectionManager(connectionManager);
        webSocketEngine.setServerInitializer(webSocketServerInitializer);
        webSocketEngine.setOpenHeartBeat(tcpProperties.isOpenHeartBeat());
        webSocketEngine.setBossThreadNum(tcpProperties.getBossThreadNum());
        webSocketEngine.setWorkerThreadNum(tcpProperties.getWorkerThreadNum());
//        webSocketEngine.setHeartBeatTimeInterval(180000);
        webSocketEngine.setHeartBeatTimeInterval(tcpProperties.getHeartBeatSeconds() * 1000);
        webSocketEngine.setPort(tcpProperties.getPortWs());
        // 2023-08-25
        if(connectionCallback != null){
            webSocketEngine.setConnectionCallback(connectionCallback);
        } else {
            webSocketEngine.setConnectionCallback(new TestConnectionCallback());
        }
        // 延时启动,测试用。正式注释掉。2023-09-27
        webSocketEngine.setEngineStartDelaySeconds(30);
        try {
            webSocketEngine.start();
        } catch (Exception e) {
            throw new ApplicationRuntimeException("启动'WebSocketEngine'失败:" + e.getMessage(), e);
        }
        return webSocketEngine;
    }
    /**
     * Websocket登录认证动作配置
     * @return
     * @date 2023-04-17
     */
    @Bean
    public LoginAction wsLoginAction(){
        LoginAction loginAction = new LoginAction();
        return loginAction;
    }
    /**
     * 配置'WebSocketPush'推送实现,同时注册到管理器中。
     * @param webSocketEngine
     * @param pushManager
     * @return
     * @date 2023-04-21
     */
    @Bean
    public Pushable websocketPush(WebSocketEngine webSocketEngine, PushManager pushManager){
        WebSocketPush webSocketPush = new WebSocketPush();
        webSocketPush.setWebSocketEngine(webSocketEngine);
        webSocketPush.startup();
        pushManager.register(webSocketPush);
        return webSocketPush;
    }
}
iplatform-base-tcp/src/main/java/com/iplatform/tcp/controller/ConnectionManagerController.java
New file
@@ -0,0 +1,42 @@
package com.iplatform.tcp.controller;
import com.iplatform.tcp.TcpBaseController;
import com.walker.db.page.GenericPager;
import com.walker.db.page.ListPageContext;
import com.walker.tcp.Connection;
import com.walker.tcp.ConnectionManager;
import com.walker.web.ResponseValue;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@RestController
@RequestMapping("/tcp/connection")
public class ConnectionManagerController extends TcpBaseController {
    private ConnectionManager connectionManager;
    @Autowired
    public ConnectionManagerController(ConnectionManager connectionManager){
        this.connectionManager = connectionManager;
    }
    @RequestMapping("/list")
    public ResponseValue list(){
        List<Connection> list = this.connectionManager.queryAllConnectionList();
        if(list != null && list.size() > 256){
            logger.warn("TCP连接过多,仅展示前256个");
        }
        ListPageContext.setCurrentPageSize(256);
        GenericPager<Connection> pager = ListPageContext.createGenericPager(list, 1, list.size());
        return ResponseValue.success(pager);
    }
    @RequestMapping("/select/send_test")
    public ResponseValue sendTestCommand(){
        return ResponseValue.success();
    }
}
iplatform-base-tcp/src/main/java/com/iplatform/tcp/controller/EquipController.java
New file
@@ -0,0 +1,43 @@
package com.iplatform.tcp.controller;
import com.iplatform.model.po.TcpEquip;
import com.iplatform.tcp.TcpBaseController;
import com.iplatform.tcp.pojo.EquipParam;
import com.iplatform.tcp.service.TcpEquipServiceImpl;
import com.walker.db.page.GenericPager;
import com.walker.infrastructure.utils.StringUtils;
import com.walker.tcp.ProtocolResolverPostProcessor;
import com.walker.web.ResponseValue;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@RestController
@RequestMapping("/tcp/equip")
public class EquipController extends TcpBaseController {
    private TcpEquipServiceImpl tcpEquipService;
//    private ProtocolResolverPostProcessor protocolResolverPostProcessor;
    @Autowired
    public EquipController(TcpEquipServiceImpl tcpEquipService){
        this.tcpEquipService = tcpEquipService;
    }
    @RequestMapping("/list")
    public ResponseValue list(EquipParam equipParam){
//        PageSearch pageSearch = ListPageContext.getPageSearch();
//        GenericPager<TcpEquip> pager = this.tcpEquipService.selectSplit(new TcpEquip(), pageSearch.getPageIndex(), pageSearch.getPageSize());
        GenericPager<TcpEquip> pager = this.tcpEquipService.selectSplit(new TcpEquip());
        List<TcpEquip> data = pager.getDatas();
        if(!StringUtils.isEmptyList(data)){
            for(TcpEquip e : data){
                e.setParameterString("deptName", this.getDeptName(e.getDept()));
                e.setParameterString("protocolResolverName", ProtocolResolverPostProcessor.getProtocolResolver(e.getProtocolResolver()).getName());
            }
        }
        return ResponseValue.success(pager);
    }
}
iplatform-base-tcp/src/main/java/com/iplatform/tcp/controller/TestTcpController.java
New file
@@ -0,0 +1,179 @@
package com.iplatform.tcp.controller;
import com.iplatform.base.SystemController;
import com.iplatform.tcp.util.ws.WebBroadCastResponse;
import com.iplatform.tcp.util.ws.WebDataResponse;
import com.walker.infrastructure.utils.DateUtils;
import com.walker.infrastructure.utils.NumberGenerator;
import com.walker.infrastructure.utils.StringUtils;
import com.walker.push.Notification;
import com.walker.push.NotificationChannel;
import com.walker.push.PushManager;
import com.walker.push.PushResult;
import com.walker.tcp.Connection;
import com.walker.tcp.ConnectionManager;
import com.walker.tcp.connect.LongConnection;
import com.walker.tcp.lb.LongConnectionMeta;
import com.walker.tcp.lb.RedisConnectionMetaCache;
import com.walker.tcp.lb.RedisConnectionNameCache;
import com.walker.tcp.netty.WebSocketEngine;
import com.walker.web.ResponseValue;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.lang.Nullable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.*;
@RestController
@RequestMapping("/test/tcp")
public class TestTcpController extends SystemController {
    private WebSocketEngine webSocketEngine;
    private PushManager pushManager;
    private RedisConnectionMetaCache connectionMetaCache;
    private RedisConnectionNameCache connectionNameCache;
    @Autowired
    public TestTcpController(WebSocketEngine webSocketEngine, PushManager pushManager
            , @Nullable RedisConnectionMetaCache connectionMetaCache
            , @Nullable RedisConnectionNameCache connectionNameCache){
        this.webSocketEngine = webSocketEngine;
        this.pushManager = pushManager;
        this.connectionMetaCache = connectionMetaCache;
        this.connectionNameCache = connectionNameCache;
    }
    /**
     * 获取websocket基本情况。
     * @return
     * @date 2023-12-14
     */
    @RequestMapping("/ws/info")
    public ResponseValue getWebsocketLinkInfo(){
        Map<String, Object> infoMap = new HashMap<>(8);
        if(this.connectionNameCache != null){
            long totalWebsocketSize = this.connectionNameCache.getCache().getPersistentSize();
            infoMap.put("total_websocket", totalWebsocketSize);
            infoMap.put("create_time", connectionNameCache.getCreateTime());
            infoMap.put("cache_name", this.connectionNameCache.getProviderName());
        }
        if(this.connectionMetaCache != null){
            infoMap.put("connection_meta_size", this.connectionMetaCache.getCache().getPersistentSize());
        }
        return ResponseValue.success(infoMap);
    }
    /**
     * 获取一个链接信息。
     * @param userId 连接绑定的用户名称(name),一般为用户ID。
     * @return
     * @date 2023-12-14
     */
    @RequestMapping("/ws/connection")
    public ResponseValue getWebsocketConnection(String userId){
        if(StringUtils.isEmpty(userId)){
            return ResponseValue.error("userId is required!");
        }
        Map<String, Object> infoMap = new HashMap<>(8);
        ConnectionManager connectionManager = this.webSocketEngine.getConnectionManager();
        if(connectionManager != null){
            LongConnection connection = (LongConnection) connectionManager.getConnectionByName(userId);
            if(connection == null){
                return ResponseValue.success("连接不存在:" + userId);
            }
            if(connection instanceof LongConnectionMeta){
                infoMap.put("type", "集群连接对象(LongConnectionMeta),不是当前主机物理连接");
            }
            infoMap.put("id", connection.getId());
            infoMap.put("host", connection.getConnectionHost());
            infoMap.put("name", connection.getName());
            infoMap.put("alreadyLogin", connection.getAlreadyLogin());
            infoMap.put("engineId", connection.getEngineId());
            infoMap.put("lastTime", connection.getLastTime());
        } else {
            infoMap.put("error", "connectionManager 为空");
        }
        return ResponseValue.success(infoMap);
    }
    /**
     * 向所有链接广播消息。
     * @param name
     * @param uid
     * @return
     */
    @RequestMapping("/ws/send_broadcast")
    public ResponseValue sendTestCommand(String name, String uid){
        WebBroadCastResponse msg = new WebBroadCastResponse();
        msg.setMessageId(String.valueOf(NumberGenerator.getSequenceNumber()));
        msg.setName(uid);
        msg.setData(name + ": " + msg.getMessageId());
        webSocketEngine.sendBroadcast(msg);
        return ResponseValue.success();
    }
    /**
     * 向浏览器(用户)推送一个websocket实时消息。
     * @param userIds
     * @return
     * @date 2024-01-23
     */
    @RequestMapping("/ws/send")
    public ResponseValue sendToUser(String userIds){
        if (StringUtils.isNotEmpty(userIds)){
            String[] split = userIds.split(",");
            List<String> ids = Arrays.asList(split);
            for (String id : ids) {
                Connection connection = this.webSocketEngine.getConnectionManager().getConnectionByName(id);
                if (connection!=null){
                    WebDataResponse msg = new WebDataResponse();
                    msg.setMessageId(NumberGenerator.getLongSequenceId());
                    msg.setName(id);    // 指定用户发送,该id与浏览器端注册的id保持一致。
                    msg.setData("有新消息,速度刷新");
                    webSocketEngine.sendResponse(msg);
                }
            }
        }
        return ResponseValue.success();
    }
    @RequestMapping("/push")
    public ResponseValue pushOne(String type, String userId){
        PushResult pushResult = null;
        if(type.equals("1")){
            logger.info("推送短信测试,user = 0");
            pushResult = this.pushManager.push(this.acquireNotification(NotificationChannel.Sms, "13838277463"), null);
        } else if(type.equals("2")){
            if(StringUtils.isEmpty(userId)){
                userId = "0";
            }
            pushResult = this.pushManager.push(this.acquireNotification(NotificationChannel.WebSocket, userId), null);
        } else {
            throw new UnsupportedOperationException("暂不支持其他推送方式:" + type);
        }
        return ResponseValue.success(pushResult);
    }
    private Notification acquireNotification(NotificationChannel channel, String user){
        Notification n = new Notification();
        n.setId(NumberGenerator.generatorHexUUID());
        n.setCreateTime(DateUtils.getDateTimeNumber());
        n.setCreator("creator");
        n.setFrom("shikeying");
        n.setContent("这是一个测试");
        List<NotificationChannel> channels = new ArrayList<>(2);
        channels.add(channel);
        n.setChannelList(channels);
        n.setPersistent(true);
        n.setTitle("title");
        List<String> userList = new ArrayList<>(2);
        userList.add(user);
        n.setReceiverList(userList);
        return n;
    }
}
iplatform-base-tcp/src/main/java/com/iplatform/tcp/lb/DefaultLbConnectionManager.java
New file
@@ -0,0 +1,31 @@
package com.iplatform.tcp.lb;
import com.walker.tcp.Connection;
import com.walker.tcp.lb.LoadBalanceConnectionManager;
import com.walker.tcp.lb.LongConnectionMeta;
/**
 * 支持集群的长连接管理器。
 * @author 时克英
 * @date 2023-09-26
 */
public class DefaultLbConnectionManager extends LoadBalanceConnectionManager {
//    private ResponseWriter responseWriter = new DefaultResponseWriter();
    @Override
    protected LongConnectionMeta acquireConnectionMeta(Connection connection) {
        MqConnectionMeta meta = new MqConnectionMeta(connection.getId());
        meta.setEngineId(connection.getEngineId());
        meta.setName(connection.getName());
        meta.setConnectionHost(connection.getConnectionHost());
        meta.setLastTime(connection.getLastTime());
        meta.setAlreadyLogin(connection.getAlreadyLogin());
        meta.setLongConnection(true);
//        meta.setProtocolResolver(connection.getProtocolResolver());
        meta.setResponseWriter(this.getResponseWriter());
        logger.debug("准备更新(保存或更新)一个链接:{}", meta);
        return meta;
    }
}
iplatform-base-tcp/src/main/java/com/iplatform/tcp/lb/DefaultMqListener.java
New file
@@ -0,0 +1,96 @@
package com.iplatform.tcp.lb;
import com.iplatform.base.Constants;
import com.iplatform.tcp.util.ws.LoginResponse;
import com.iplatform.tcp.util.ws.WebDataResponse;
import com.walker.infrastructure.utils.JsonUtils;
import com.walker.push.rocketmq.EnhanceMessageHandler;
import com.walker.push.rocketmq.tcp.MqResponse;
import com.walker.tcp.Connection;
import com.walker.tcp.ConnectionManager;
import com.walker.tcp.Response;
import com.walker.tcp.lb.LongConnectionMeta;
import org.apache.rocketmq.spring.core.RocketMQListener;
import java.util.Map;
/**
 * 平台默认的MQ队列监听器,业务需要根据情况继承该对象,实现自己的业务。
 * <p>需要重写方法:{@linkplain DefaultMqListener#handleMessage(MqResponse)}</p>
 * @author 时克英
 * @date 2023-09-26
 * @date 2024-01-23 无需使用集群模式,单机即可,MQ不用开启监听。
 */
//@RocketMQMessageListener(topic = "${iplatform.tcp.connection-host}", consumerGroup = "consumer-group")
//public class DefaultMqListener extends EnhanceMessageHandler<MqResponse> implements RocketMQListener<MqResponse> {
public class DefaultMqListener extends EnhanceMessageHandler<MqResponse> implements RocketMQListener<MqResponse> {
//    protected final transient Logger logger = LoggerFactory.getLogger(this.getClass());
    @Override
    public void onMessage(MqResponse mqResponse) {
        this.dispatchMessage(mqResponse);
    }
    @Override
    protected void handleMessage(MqResponse message) throws Exception {
        // 具体执行业务消息方法。
        // 这里聊天业务中,需要推送到本机连接的客户端(或浏览器)
//        Response<?> response = message.getResponse();
        Response<?> response = this.translateResponse(message.getResponse());
        Connection conn = connectionManager.getConnectionByName(response.getName());
        if(conn == null || !conn.isConnected()){
            logger.debug("mq消息已接收,但长连接不存在无法推送,response = {}", response);
            return;
        }
        if(conn instanceof LongConnectionMeta){
            throw new IllegalStateException("这个应该是本地物理连接,但找到的是:LongConnectionMeta,name=" + response.getName());
        }
        conn.write(response);
    }
    /**
     * 转换成实际发送的业务数据,业务可以继承该类,并重写方法。
     * @param json
     * @return
     * @throws Exception
     */
    protected Response<?> translateResponse(String json) throws Exception{
        Map<String, Object> responseMap = JsonUtils.jsonStringToObject(json, Map.class);
        if(!responseMap.containsKey(KEY_PROTOCOL_NUM)){
            throw new IllegalArgumentException("responseMap中必须包含协议号字段:" + KEY_PROTOCOL_NUM);
        }
        String protocolNum = responseMap.get(KEY_PROTOCOL_NUM).toString();
        if(protocolNum.equals("login")){
            return JsonUtils.jsonStringToObject(json, LoginResponse.class);
        } else if(protocolNum.equals(Constants.PUSH_SCOPE_DATA)){
            return JsonUtils.jsonStringToObject(json, WebDataResponse.class);
        } else {
            throw new UnsupportedOperationException("未实现的 websocket.response对象转换:" + json);
        }
    }
    private final String KEY_PROTOCOL_NUM = "protocolNum";
    @Override
    protected void handleMaxRetriesExceeded(MqResponse message) {
    }
    @Override
    protected boolean isRetry() {
        return false;
    }
    @Override
    protected boolean throwException() {
        return true;
    }
    public void setConnectionManager(ConnectionManager connectionManager) {
        this.connectionManager = connectionManager;
    }
    private ConnectionManager connectionManager;
}
iplatform-base-tcp/src/main/java/com/iplatform/tcp/lb/DefaultResponseWriter.java
New file
@@ -0,0 +1,51 @@
package com.iplatform.tcp.lb;
import com.iplatform.base.PlatformRuntimeException;
import com.iplatform.core.BeanContextAware;
import com.walker.infrastructure.utils.DateUtils;
import com.walker.infrastructure.utils.JsonUtils;
import com.walker.push.rocketmq.RocketMQEnhanceTemplate;
import com.walker.push.rocketmq.tcp.MqResponse;
import com.walker.tcp.Response;
import com.walker.tcp.lb.ResponseWriter;
import com.walker.web.util.IdUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class DefaultResponseWriter implements ResponseWriter {
    private final transient SendStatusCallback callback = new SendStatusCallback();
    protected final transient Logger logger = LoggerFactory.getLogger(this.getClass());
    @Override
    public Object write(String type, Response<?> response, String connectionHost, String channelId) {
        if(type.equals(TYPE_MQ)){
            logger.debug("+++++++++++++++++++++++++");
            BeanContextAware.getBeanByType(RocketMQEnhanceTemplate.class)
                    .sendAsync(connectionHost, this.acquireMqResponse(response), callback);
            if(this.logger.isDebugEnabled()){
                logger.debug("MqConnectionMeta 发送一个消息:{}", response);
            }
        } else {
            throw new UnsupportedOperationException("不支持的 ResponseWriter.type = " + type);
        }
        return null;
    }
    private MqResponse acquireMqResponse(Response<?> response){
        MqResponse data = new MqResponse();
//        data.setResponse(response);
        try {
            data.setResponse(JsonUtils.objectToJsonString(response));
        } catch (Exception e) {
            throw new PlatformRuntimeException("Response<?> --> String 失败:" + e.getMessage(), e);
        }
        data.setKey(IdUtils.fastSimpleUUID());
        data.setRetryTimes(1);
        data.setSource(response.getProtocolNum());
        data.setSendTime(DateUtils.getDateTimeNumber());
        data.setTopic(response.getTopic());
        return data;
    }
}
iplatform-base-tcp/src/main/java/com/iplatform/tcp/lb/MqConnectionMeta.java
New file
@@ -0,0 +1,81 @@
package com.iplatform.tcp.lb;
import com.walker.tcp.lb.LongConnectionMeta;
/**
 * 平台实现的长连接元数据对象。
 * <pre>
 *     1) 该对象表示一个物理连接的映射,会缓存到Redis中。
 *     2) 本机节点只能获取自己的链路连接,并通过连接直接发送消息。
 *     3) 连接元数据作用是,在其他节点中通过Redis中记录的连接映射以MQ消息形式发送出去。
 * </pre>
 * @author 时克英
 * @date 2023-09-26
 */
public class MqConnectionMeta extends LongConnectionMeta {
//    private final transient SendStatusCallback callback = new SendStatusCallback();
    public MqConnectionMeta(){}
    public MqConnectionMeta(String id) {
        super(id);
    }
//    @JsonIgnore
//    @Override
//    public void write(Response<?> response) {
//        // 通过MQ发送消息到目标主机,该目标就是当前连接绑定的主机标识。
//        // 暂时使用异步任务发送,2023-09-26
//        // 这里发送的主题就是全局连接对应的主机信息:
////        BeanContextAware.getBeanByType(RocketMQEnhanceTemplate.class)
////                .sendAsync(response.getTopic(), this.acquireMqResponse(response), callback);
//        logger.info("+++++++++++++++++++++++++");
//        BeanContextAware.getBeanByType(RocketMQEnhanceTemplate.class)
//                .sendAsync(this.getConnectionHost(), this.acquireMqResponse(response), callback);
//        if(this.logger.isDebugEnabled()){
//            logger.debug("MqConnectionMeta 发送一个消息:{}", response);
//        }
//    }
//    @Override
//    protected void onWrite(Response<?> response){
//        logger.info("+++++++++++++++++++++++++");
//        BeanContextAware.getBeanByType(RocketMQEnhanceTemplate.class)
//                .sendAsync(this.getConnectionHost(), this.acquireMqResponse(response), callback);
//        if(this.logger.isDebugEnabled()){
//            logger.debug("MqConnectionMeta 发送一个消息:{}", response);
//        }
//    }
//    private MqResponse acquireMqResponse(Response<?> response){
//        MqResponse data = new MqResponse();
//        data.setResponse(response);
//        data.setKey(IdUtils.fastSimpleUUID());
//        data.setRetryTimes(1);
//        data.setSource(response.getProtocolNum());
//        data.setSendTime(DateUtils.getDateTimeNumber());
//        data.setTopic(response.getTopic());
//        return data;
//    }
    @Override
    public void disconnect() {
        // nothing;
    }
    @Override
    public boolean isConnected() {
        return true;
    }
    @Override
    public String toString(){
        return new StringBuilder("MqConnectionMeta{id=").append(this.getId())
                .append(", alreadyLogin=").append(this.getAlreadyLogin())
                .append(", host=").append(this.getConnectionHost())
                .append(", name=").append(this.getName())
                .append(", lastTime=").append(this.getLastTime())
                .append("}").toString();
    }
}
iplatform-base-tcp/src/main/java/com/iplatform/tcp/lb/MqResponseUtils.java
New file
@@ -0,0 +1,18 @@
package com.iplatform.tcp.lb;
import com.walker.push.rocketmq.tcp.MqResponse;
import java.util.Map;
public class MqResponseUtils {
    public static final MqResponse acquireMqResponse(Map<String, Object> map){
        int retryTimes = Integer.parseInt(map.get("retryTimes").toString());
        MqResponse mqResponse = new MqResponse();
        mqResponse.setRetryTimes(retryTimes);
        mqResponse.setKey(map.get("key").toString());
        mqResponse.setSource(map.get("source").toString());
        mqResponse.setTopic(map.get("topic") != null? map.get("topic").toString() : "");
        return mqResponse;
    }
}
iplatform-base-tcp/src/main/java/com/iplatform/tcp/lb/SendStatusCallback.java
New file
@@ -0,0 +1,26 @@
package com.iplatform.tcp.lb;
import org.apache.rocketmq.client.producer.SendCallback;
import org.apache.rocketmq.client.producer.SendResult;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
 * 消息发送回调定义。
 * <p>暂时还不知道如何使用(在哪个地方使用)</p>
 * @date 2023-09-26
 */
public class SendStatusCallback implements SendCallback {
    protected final transient Logger logger = LoggerFactory.getLogger(this.getClass());
    @Override
    public void onSuccess(SendResult sendResult) {
        logger.debug("消息发送成功:{}", sendResult.getMsgId());
    }
    @Override
    public void onException(Throwable throwable) {
        logger.error("消息发送失败:", throwable);
    }
}
iplatform-base-tcp/src/main/java/com/iplatform/tcp/lb/SimpleMqListener.java
New file
@@ -0,0 +1,144 @@
package com.iplatform.tcp.lb;
import com.iplatform.base.Constants;
import com.iplatform.base.PlatformRuntimeException;
import com.iplatform.tcp.util.ws.LoginResponse;
import com.iplatform.tcp.util.ws.WebDataResponse;
import com.walker.infrastructure.utils.JsonUtils;
import com.walker.push.rocketmq.tcp.MqResponse;
import com.walker.tcp.Connection;
import com.walker.tcp.ConnectionManager;
import com.walker.tcp.Response;
import com.walker.tcp.lb.LongConnectionMeta;
import org.apache.rocketmq.spring.annotation.RocketMQMessageListener;
import org.apache.rocketmq.spring.core.RocketMQListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Map;
/**
 * MQ消息监听接收处理。
 * <pre>
 *     1) consumerGroup:消费者组,这个是针对同一个主题和Tag。
 *     资料介绍:
 *     A.一个消费者组,代表着一群topic相同,tag相同(即逻辑相同)的Consumer。通过一个消费者组,则可容易的进行负载均衡以及容错
 *     B.使用时,一个节点下,一个topic加一个tag可以对应一个consumer。一个消费者组就是横向上多个节点的相同consumer为一个消费组。
 * </pre>
 * @date 2023-10-07 修改消费者组,尝试解决多个队列消息无法接收问题。
 * @date 2023-12-14 监听对象放在业务中处理,平台注释该对象,业务通过MQ接收其他节点发送的聊天消息。如果要测试,请打开注解。(标注废弃是为了引起注意!)
 */
//@RocketMQMessageListener(topic = "${iplatform.tcp.connection-host}", selectorExpression = "*", consumerGroup = "${iplatform.tcp.connection-host}-consumer-group")
@Deprecated
public class SimpleMqListener implements RocketMQListener<String> {
    protected final transient Logger logger = LoggerFactory.getLogger(this.getClass());
    @Override
    public void onMessage(String s) {
        logger.debug("mq = {}", s);
//        MqResponse mqResponse = null;
        Map<String, Object> mqResponse = null;
        try {
//            mqResponse = JsonUtils.jsonStringToObject(s, MqResponse.class);
            mqResponse = JsonUtils.jsonStringToObject(s, Map.class);
        } catch (Exception e) {
            throw new PlatformRuntimeException("mq消息转换json对象失败:" + e.getMessage(), e);
        }
        MqResponse message = MqResponseUtils.acquireMqResponse(mqResponse);
        // 超过最大重试次数时调用子类方法处理
        if (message.getRetryTimes() > getMaxRetryTimes()) {
            handleMaxRetriesExceeded(message);
            return;
        }
        Response<?> response = null;
        try {
            response = this.translateResponse(mqResponse.get("response").toString());
        } catch (Exception e) {
            throw new PlatformRuntimeException("转换成'Response'失败:" + e.getMessage(), e);
        }
        try {
            long now = System.currentTimeMillis();
//            handleMessage(message);
            Connection conn = connectionManager.getConnectionByName(response.getName());
            if(conn == null || !conn.isConnected()){
                logger.debug("mq消息已接收,但长连接不存在无法推送,response = {}", response);
                return;
            }
            if(conn instanceof LongConnectionMeta){
                throw new IllegalStateException("这个应该是本地物理连接,但找到的是:LongConnectionMeta,name=" + response.getName());
            }
            conn.write(response);
            long costTime = System.currentTimeMillis() - now;
            logger.debug("消息{}消费成功,耗时[{}ms]", message.getKey(), costTime);
        } catch (Exception e) {
            logger.error("消息{}消费异常", message.getKey(),e);
            // 是捕获异常还是抛出,由子类决定
            if (throwException()) {
                //抛出异常,由DefaultMessageListenerConcurrently类处理
                throw new RuntimeException(e);
            }
            //此时如果不开启重试机制,则默认ACK了
//            if (isRetry()) {
//                handleRetry(message);
//            }
        }
    }
    /**
     * 转换成实际发送的业务数据,业务可以继承该类,并重写方法。
     * @param json
     * @return
     * @throws Exception
     */
    protected Response<?> translateResponse(String json) throws Exception{
        Map<String, Object> responseMap = JsonUtils.jsonStringToObject(json, Map.class);
        if(!responseMap.containsKey(KEY_PROTOCOL_NUM)){
            throw new IllegalArgumentException("responseMap中必须包含协议号字段:" + KEY_PROTOCOL_NUM);
        }
        String protocolNum = responseMap.get(KEY_PROTOCOL_NUM).toString();
        if(protocolNum.equals("login")){
            return JsonUtils.jsonStringToObject(json, LoginResponse.class);
        } else if(protocolNum.equals(Constants.PUSH_SCOPE_DATA)){
            return JsonUtils.jsonStringToObject(json, WebDataResponse.class);
        } else {
            throw new UnsupportedOperationException("未实现的 websocket.response对象转换:" + json);
        }
    }
    protected void handleMaxRetriesExceeded(MqResponse message) {
    }
    protected boolean throwException() {
        return true;
    }
    /**
     * 最大重试次数
     *
     * @return 最大重试次数,默认5次
     */
    protected int getMaxRetryTimes() {
        return MAX_RETRY_TIMES;
    }
    /**
     * 默认重试次数
     */
    private static final int MAX_RETRY_TIMES = 3;
    public void setConnectionManager(ConnectionManager connectionManager) {
        this.connectionManager = connectionManager;
    }
    private ConnectionManager connectionManager;
    private final String KEY_PROTOCOL_NUM = "protocolNum";
}
iplatform-base-tcp/src/main/java/com/iplatform/tcp/pojo/EquipParam.java
New file
@@ -0,0 +1,16 @@
package com.iplatform.tcp.pojo;
import com.walker.web.param.ParamRequest;
public class EquipParam extends ParamRequest {
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    private String name;
}
iplatform-base-tcp/src/main/java/com/iplatform/tcp/service/TcpEquipServiceImpl.java
New file
@@ -0,0 +1,9 @@
package com.iplatform.tcp.service;
import com.walker.jdbc.service.BaseServiceImpl;
import org.springframework.stereotype.Service;
@Service
public class TcpEquipServiceImpl extends BaseServiceImpl {
}
iplatform-base-tcp/src/main/java/com/iplatform/tcp/service/TcpEquipStatusServiceImpl.java
New file
@@ -0,0 +1,8 @@
package com.iplatform.tcp.service;
import com.walker.jdbc.service.BaseServiceImpl;
import org.springframework.stereotype.Service;
@Service
public class TcpEquipStatusServiceImpl extends BaseServiceImpl {
}
iplatform-base-tcp/src/main/java/com/iplatform/tcp/support/PersistentConnectionManager.java
New file
@@ -0,0 +1,133 @@
package com.iplatform.tcp.support;
import com.iplatform.model.po.TcpEquip;
import com.iplatform.model.po.TcpEquipStatus;
import com.iplatform.tcp.EngineType;
import com.iplatform.tcp.EquipmentCacheProvider;
import com.iplatform.tcp.EquipmentStatusCacheProvider;
import com.iplatform.tcp.LiveStatus;
import com.iplatform.tcp.service.TcpEquipStatusServiceImpl;
import com.walker.infrastructure.utils.DateUtils;
import com.walker.infrastructure.utils.NumberGenerator;
import com.walker.tcp.Connection;
import com.walker.tcp.support.SimpleEngineConnectionManager;
/**
 * 可持久化存储连接状态的连接管理器实现。
 * @author 时克英
 * @date 2018-11-29
 *
 */
public class PersistentConnectionManager extends SimpleEngineConnectionManager {
    public void setWebsocketPersistent(boolean websocketPersistent) {
        this.websocketPersistent = websocketPersistent;
    }
    private boolean websocketPersistent = false;
    @Override
    protected void onSaveConnection(Connection connection) throws Exception {
        if(connection.getEngineId() == EngineType.INDEX_TCP_WEBSOCKET && !this.websocketPersistent){
            // websocket连接不需要持久化
            return;
        }
        logger.debug("保存了一个连接:" + connection.getName());
        String num = connection.getName();
        TcpEquipStatus cacheStatus = statusCache.getEquipmentStatus(num);
        if(cacheStatus == null){
            // 数据库还没有状态记录
            TcpEquipStatus newStatus = this.createStatus(connection, num);
            this.tcpEquipStatusService.insert(newStatus);
            statusCache.putEquipmentStatus(num, newStatus);
        } else {
            // 已经存在记录(更新缓存数据,并也更新status表)
            cacheStatus.setStartTime(Long.parseLong(DateUtils.getDateTimeSecondForShow(System.currentTimeMillis())));
            cacheStatus.setLiveStatus(LiveStatus.CONST_CONNECTED);
            this.tcpEquipStatusService.save(cacheStatus);
//            statusCache.updateCacheData(num, cacheStatus);
        }
        this.afterSaveConnection(cacheStatus);
    }
    private TcpEquipStatus createStatus(Connection connection, String num){
        TcpEquip equip = this.getEquipmentFromCache(num);
        TcpEquipStatus entity = new TcpEquipStatus();
        entity.setCreateTime(NumberGenerator.getSequenceNumber());
        entity.setEquipNum(num);
        entity.setId(NumberGenerator.getLongSequenceNumber());
        entity.setLiveStatus(LiveStatus.CONST_CONNECTED);
        entity.setStartTime(Long.parseLong(DateUtils.getDateTimeSecondForShow(connection.getCreateTimeMills())));
        if(equip == null){
            // 没有注册设备,但仍允许上报数据
            entity.setEquipId(0L);
            entity.setDept(0L);
        } else {
            entity.setEquipId(equip.getId());
            entity.setDept(equip.getDept());
        }
        return entity;
    }
    private TcpEquip getEquipmentFromCache(String num){
        return equipCache.getEquipment(num);
    }
    @Override
    protected void onDeleteConnection(int engineId, String name) throws Exception {
        if(engineId == EngineType.INDEX_TCP_WEBSOCKET && !this.websocketPersistent){
            // websocket连接不需要持久化
            return;
        }
        logger.debug("删除了一个连接:" + name);
        TcpEquipStatus cacheStatus = statusCache.getEquipmentStatus(name);
        cacheStatus.setEndTime(Long.parseLong(DateUtils.getDateTimeSecondForShow(System.currentTimeMillis())));
        cacheStatus.setLiveStatus(LiveStatus.CONST_NOT_CONNECT);
        this.tcpEquipStatusService.save(cacheStatus);
        this.afterDeleteConnection(cacheStatus);
    }
    @Override
    protected void onUpdateLastTime(int engineId, String name, long lastTime) throws Exception {
        logger.debug("更新了一个连接:" + name);
    }
    /**
     * 保存连接之后动作,由子类实现。目前为websocket推送做准备
     * @param cacheStatus
     */
    protected void afterSaveConnection(TcpEquipStatus cacheStatus){
//        OnlineResponse response = new OnlineResponse();
//        response.setName(cacheStatus.getEquipNum());
//        this.webSocketEngine.sendBroadcast(response);
    }
    /**
     * 设备断开连接之后动作,由子类实现。目前为websocket推送做准备
     * @param cacheStatus
     */
    protected void afterDeleteConnection(TcpEquipStatus cacheStatus){
//        OfflineResponse response = new OfflineResponse();
//        response.setName(cacheStatus.getEquipNum());
//        this.webSocketEngine.sendBroadcast(response);
    }
    public void setStatusCache(EquipmentStatusCacheProvider statusCache) {
        this.statusCache = statusCache;
    }
    public void setEquipCache(EquipmentCacheProvider equipCache) {
        this.equipCache = equipCache;
    }
    public void setTcpEquipStatusService(TcpEquipStatusServiceImpl tcpEquipStatusService) {
        this.tcpEquipStatusService = tcpEquipStatusService;
    }
    private EquipmentStatusCacheProvider statusCache;
    private EquipmentCacheProvider equipCache;
    private TcpEquipStatusServiceImpl tcpEquipStatusService;
}
iplatform-base-tcp/src/main/java/com/iplatform/tcp/support/PlatformSharpProtocolResolver.java
New file
@@ -0,0 +1,55 @@
package com.iplatform.tcp.support;
import com.walker.tcp.littleD.Constants;
import com.walker.tcp.littleD.LittleDProtocolResolver;
/**
 * 平台TCP通信协议解析器实现。
 * <p>沿用小D智能终端协议规范,#为结束标志,前缀可修改(2位),协议编号4位</p>
 * @author 时克英
 * @date 2023-04-17
 */
public class PlatformSharpProtocolResolver extends LittleDProtocolResolver {
    public PlatformSharpProtocolResolver(){
        this.setDelimiter(Constants.DATA_END_FLAG);
        this.setProtocolFeature(Constants.BODY_ID);
        this.setName("平台通信解析器");
        this.setOrder(1);
    }
//    @Override
//    protected String onResolve(String data, int size) throws ProtocolException {
//        if(size < 6){
//            throw new ProtocolException(ERR_PROTOCOL_SIZE);
//        }
//
//        if(!source.startsWith(this.getProtocolFeature().toString())){
//            throw new ProtocolException(ERR_PROTOCOL_FEATURE);
//        }
//        return source.substring(2, 6);
//    }
//    @Override
//    protected Response<?> doCreateOneResponse() {
//        // 返回心跳响应
//        return new HeartBeatResponse();
//    }
//    @Override
//    public String getAuthenticateInfo(Request<?> request) throws AuthenticateException {
//        if(request instanceof LoginRequest){
//            String clientId = request.getName();
//            if(StringUtils.isEmpty(clientId)){
//                throw new AuthenticateException("请求信息中不存在设备ID号,无法认证");
//            }
//
//            if(!isPermitNotRegisterConnect() && !this.isRegistered(clientId)){
//                throw new AuthenticateException("设备未在列表中注册,无法认证。clientId = " + clientId);
//            }
//            return clientId;
//
//        } else {
//            throw new AuthenticateException("connection通道认证失败,请求不是登录");
//        }
//    }
}
iplatform-base-tcp/src/main/java/com/iplatform/tcp/support/TestConnectionCallback.java
New file
@@ -0,0 +1,32 @@
package com.iplatform.tcp.support;
import com.walker.tcp.Connection;
import com.walker.tcp.ConnectionCallback;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
 * 测试 Tcp 或 Websocket 连接回调实现。
 * @author 时克英
 * @date 2023-08-26
 */
public class TestConnectionCallback implements ConnectionCallback {
    private final transient Logger logger = LoggerFactory.getLogger(getClass());
    @Override
    public void onIdle(Connection connection) {
    }
    @Override
    public void onAfterLogin(Connection connection) {
        logger.info("连接认证成功,已发送登录响应,这里可以直接再次发送业务消息。");
//        String userId = connection.getName();
//        // 用户是否,门户前端用户?
//        boolean isFrontUser = false;
//        if(isFrontUser){
//            connection.write(Response);
//        }
    }
}
iplatform-base-tcp/src/main/java/com/iplatform/tcp/support/WebSocketPush.java
New file
@@ -0,0 +1,131 @@
package com.iplatform.tcp.support;
import com.iplatform.tcp.util.ws.WebBroadCastResponse;
import com.iplatform.tcp.util.ws.WebDataResponse;
import com.walker.infrastructure.utils.StringUtils;
import com.walker.push.AbstractPushObject;
import com.walker.push.Notification;
import com.walker.push.NotificationChannel;
import com.walker.push.PushException;
import com.walker.push.PushResult;
import com.walker.push.util.PushUtils;
import com.walker.tcp.netty.WebSocketEngine;
import java.util.ArrayList;
import java.util.List;
/**
 * WebSocket方式实现的推送对象。
 * @author 时克英
 * @date 2023-04-21
 */
public class WebSocketPush extends AbstractPushObject<WebBroadCastResponse> {
    public WebSocketPush(){
        this.setId(NotificationChannel.WebSocket.getIndex());
        this.setName(NotificationChannel.WebSocket.getName() + "推送");
    }
    @Override
    protected PushResult doPushContent(Notification notification, List<WebBroadCastResponse> data) throws PushException {
        PushResult pushResult = PushUtils.acquireSuccessPushResult();
        String error = null;
        for(WebBroadCastResponse response : data){
            if(notification.getBroadcast()){
                webSocketEngine.sendBroadcast(response);
                logger.debug("websocket广播直接发送,没有状态");
                return pushResult;
            }
            error = this.webSocketEngine.sendResponse(response);
            if(StringUtils.isNotEmpty(error)){
                if(pushResult.getCode() == 0){
                    pushResult.setCode(-1);
                }
                // 这里无需转换用户信息,因为使用的就是userId,一致的!
                pushResult.addOneFailed(response.getName());
            }
        }
        return pushResult;
    }
//    @Override
//    protected PushResult doPushContent(Notification notification) throws PushException {
//        if(notification.getBroadcast()){
//            logger.debug("websocket推送广播");
//
//            WebBroadCastResponse msg = new WebBroadCastResponse();
//            msg.setMessageId(String.valueOf(NumberGenerator.getSequenceNumber()));
//            msg.setData(notification.getContent());
//            webSocketEngine.sendBroadcast(msg);
//            return PushUtils.acquireSuccessPushResult();
//        }
//
//        // 普通推送消息
//        PushResult pushResult = new PushResult();
//        WebDataResponse response = null;
//        String error = null;
//
//        List<String> receiverList = notification.getReceiverList();
//        for(String user : receiverList){
//            response = new WebDataResponse();
//            response.setMessageId(String.valueOf(NumberGenerator.getSequenceNumber()));
//            response.setData(notification.getContent());
//            response.setName(user); // websocket用户标识为:userId
//            error = this.webSocketEngine.sendResponse(response);
//
//            if(StringUtils.isNotEmpty(error)){
//                if(pushResult.getCode() == 0){
//                    pushResult.setCode(-1);
//                }
//                pushResult.addOneFailed(user);
//            }
//        }
//        return pushResult;
//    }
    @Override
    public NotificationChannel getNotificationChannel() {
        return NotificationChannel.WebSocket;
    }
    @Override
    public List<WebBroadCastResponse> translateToTarget(Notification notification) {
        List<WebBroadCastResponse> data = new ArrayList<>(2);
        WebBroadCastResponse webBroadCastResponse = null;
        if(notification.getBroadcast()){
            logger.debug("websocket推送广播");
            webBroadCastResponse = new WebBroadCastResponse();
            webBroadCastResponse.setMessageId(notification.getId());
            webBroadCastResponse.setData(notification.getContent());
            data.add(webBroadCastResponse);
            return data;
        }
        // 非广播,则需要把所有接收人集合返回
        int i = 1;
        for(String user: notification.getReceiverList()){
            webBroadCastResponse = new WebDataResponse();
            webBroadCastResponse.setMessageId(notification.getId() + i);
            webBroadCastResponse.setData(notification.getContent());
            webBroadCastResponse.setName(user); // websocket用户标识为:userId
            data.add(webBroadCastResponse);
            i++;
        }
        return data;
    }
    @Override
    public void startup() {
    }
    public void setWebSocketEngine(WebSocketEngine webSocketEngine) {
        this.webSocketEngine = webSocketEngine;
    }
    private WebSocketEngine webSocketEngine;
}
iplatform-base-tcp/src/main/java/com/iplatform/tcp/util/LocationNowAction.java
New file
@@ -0,0 +1,28 @@
package com.iplatform.tcp.util;
import com.walker.tcp.Request;
import com.walker.tcp.Response;
import com.walker.tcp.support.AbstractActionCall;
/**
 * 指令下发请求处理器,终端接到指令后,会发起确认请求,系统会调用该接口。</p>
 * 该处理器对应功能:立即定位指令
 * @author 时克英
 * @date 2018-08-22
 * @date 2023-04-17 移植过来,用于演示用法。
 */
//@Component
public class LocationNowAction extends AbstractActionCall {
    @Override
    public Response<?> action(Request<?> request) {
        logger.info("接收到指令下发后的,确认请求,不需要响应。请求信息 = " + request);
        // 可以再次更新业务状态,或者处理自己的商业过程
        return null;
    }
    @Override
    public String getRequestProtocol() {
        return "AP16";
    }
}
iplatform-base-tcp/src/main/java/com/iplatform/tcp/util/ws/LoginAction.java
New file
@@ -0,0 +1,29 @@
package com.iplatform.tcp.util.ws;
import com.walker.tcp.Request;
import com.walker.tcp.Response;
import com.walker.tcp.support.AbstractActionCall;
/**
 * websocket登录认证请求处理器
 * @author 时克英
 * @date 2019-01-10
 *
 */
public class LoginAction extends AbstractActionCall {
    @Override
    public Response<?> action(Request<?> request) {
        logger.debug("接收到用户websocket登录请求:" + request.getName());
        LoginResponse response = new LoginResponse();
        response.setName(request.getName());
        response.setStatus(0);
        logger.debug(response.toString());
        return response;
    }
    @Override
    public String getRequestProtocol() {
        return "login";
    }
}
iplatform-base-tcp/src/main/java/com/iplatform/tcp/util/ws/LoginRequest.java
New file
@@ -0,0 +1,34 @@
package com.iplatform.tcp.util.ws;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.walker.infrastructure.utils.JsonUtils;
import com.walker.tcp.TcpRequest;
import com.walker.tcp.data.AbstractStringRequest;
import com.walker.tcp.util.WebSocketUtils;
@TcpRequest("login")
public class LoginRequest extends AbstractStringRequest {
    /**
     *
     */
    private static final long serialVersionUID = 8346500913809847710L;
    @Override
    protected void translateData(String source) {
//        JSONObject json = JSONObject.parseObject(source);
//        this.setName(json.getString(WebSocketUtils.WEB_SOCKET_KEY_UID));
        ObjectNode json = null;
        try {
            json = JsonUtils.jsonStringToObjectNode(source);
            this.setName(json.get(WebSocketUtils.WEB_SOCKET_KEY_UID).asText());
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
    @Override
    public String getProtocolNum() {
        return "login";
    }
}
iplatform-base-tcp/src/main/java/com/iplatform/tcp/util/ws/LoginResponse.java
New file
@@ -0,0 +1,43 @@
package com.iplatform.tcp.util.ws;
import com.walker.tcp.websocket.JsonResponse;
import java.util.Map;
public class LoginResponse extends JsonResponse {
    /**
     *
     */
    private static final long serialVersionUID = 1167352207355638142L;
    private int status = 0;
    public void setStatus(int status) {
        this.status = status;
    }
//    @Override
//    protected void translateProperties(JSONObject result) {
//        result.put("status", status);
//    }
    @Override
    public String getProtocolNum() {
        return "login";
    }
    @Override
    protected void translateProperties(Map<String, Object> result) {
        result.put("status", status);
    }
    @Override
    public String toString(){
        return new StringBuilder("[protocol=").append(this.getProtocolNum())
                .append(", name=").append(this.getName())
                .append(", messageId=").append(this.getMessageId())
                .append(", status=").append(this.status)
                .append("]").toString();
    }
}
iplatform-base-tcp/src/main/java/com/iplatform/tcp/util/ws/OfflineResponse.java
New file
@@ -0,0 +1,29 @@
package com.iplatform.tcp.util.ws;
import com.walker.tcp.websocket.JsonResponse;
import java.util.Map;
/**
 * 设备离线后,推送给websocket的响应对象定义
 * @author 时克英
 * @date 2019-01-10
 *
 */
public class OfflineResponse extends JsonResponse {
    /**
     *
     */
    private static final long serialVersionUID = -4170534196705654880L;
    @Override
    protected void translateProperties(Map<String, Object> result) {
        result.put("name", this.getName());
    }
    @Override
    public String getProtocolNum() {
        return "equip_offline";
    }
}
iplatform-base-tcp/src/main/java/com/iplatform/tcp/util/ws/OnlineResponse.java
New file
@@ -0,0 +1,29 @@
package com.iplatform.tcp.util.ws;
import com.walker.tcp.websocket.JsonResponse;
import java.util.Map;
/**
 * 设备离线后,推送给websocket的响应对象定义
 * @author 时克英
 * @date 2019-01-10
 *
 */
public class OnlineResponse extends JsonResponse {
    /**
     *
     */
    private static final long serialVersionUID = -4170534196705654880L;
    @Override
    protected void translateProperties(Map<String, Object> result) {
        result.put("name", this.getName());
    }
    @Override
    public String getProtocolNum() {
        return "equip_online";
    }
}
iplatform-base-tcp/src/main/java/com/iplatform/tcp/util/ws/StepTimeFilter.java
New file
@@ -0,0 +1,32 @@
package com.iplatform.tcp.util.ws;
import java.util.concurrent.TimeUnit;
import com.walker.tcp.Context;
import com.walker.tcp.filter.DefaultFilter;
/**
 * 让接收数据有规律间隔的,过滤器实现
 * @author Administrator
 *
 */
public class StepTimeFilter extends DefaultFilter {
    @Override
    public boolean  doFilter(Context context) throws Exception {
//        long spanTime = context.getPreviousTimeSpan();
//        if(context.getPreviousData() != null && spanTime != 0){
//            long span = Math.abs(spanTime - 600);
//            TimeUnit.MILLISECONDS.sleep(span);
//            logger.debug("~~~~~~~~~~~~~~过滤器执行一次,间隔:" + span + ", spanTime = " + spanTime);
//            logger.debug(context.getPreviousData());
//        } else {
//            TimeUnit.MILLISECONDS.sleep(600);
//            logger.debug("~~~~~~~~~~~~~~过滤器执行一次,间隔:600 mills" );
//        }
        TimeUnit.MILLISECONDS.sleep(500);
        return true;
    }
}
iplatform-base-tcp/src/main/java/com/iplatform/tcp/util/ws/TestHelloAction.java
New file
@@ -0,0 +1,19 @@
package com.iplatform.tcp.util.ws;
import com.walker.tcp.Request;
import com.walker.tcp.Response;
import com.walker.tcp.support.AbstractActionCall;
import com.walker.tcp.websocket.HelloResponse;
public class TestHelloAction extends AbstractActionCall {
    @Override
    public Response<?> action(Request<?> request) {
        return new HelloResponse();
    }
    @Override
    public String getRequestProtocol() {
        return "hello";
    }
}
iplatform-base-tcp/src/main/java/com/iplatform/tcp/util/ws/WebBroadCastResponse.java
New file
@@ -0,0 +1,50 @@
package com.iplatform.tcp.util.ws;
import com.iplatform.base.Constants;
import com.walker.tcp.websocket.JsonResponse;
import java.util.Map;
/**
 * websocket广播消息响应对象
 * @author 时克英
 * @date 2018-10-25
 *
 */
public class WebBroadCastResponse extends JsonResponse {
    /**
     *
     */
    private static final long serialVersionUID = -7104409587873056275L;
    private String data;
    public String getData() {
        return data;
    }
    public WebBroadCastResponse(){
        this.setProtocolNum(Constants.PUSH_SCOPE_BROAD_CAST);
    }
    public void setData(String say) {
        this.data = say;
    }
//    @Override
//    protected void translateProperties(JSONObject json) {
//        json.put("data", data);
//    }
    // 在构造方法中设置初始化,2023-09-28
//    @Override
//    public String getProtocolNum() {
//        return Constants.PUSH_SCOPE_BROAD_CAST;
//    }
    @Override
    protected void translateProperties(Map<String, Object> result) {
        result.put("data", data);
    }
}
iplatform-base-tcp/src/main/java/com/iplatform/tcp/util/ws/WebDataResponse.java
New file
@@ -0,0 +1,20 @@
package com.iplatform.tcp.util.ws;
import com.iplatform.base.Constants;
/**
 * 推送普通的网页消息,响应定义。
 * @author 时克英
 * @date 2023-04-21
 */
public class WebDataResponse extends WebBroadCastResponse{
    public WebDataResponse(){
        this.setProtocolNum(Constants.PUSH_SCOPE_DATA);
    }
//    @Override
//    public String getProtocolNum() {
//        return Constants.PUSH_SCOPE_DATA;
//    }
}
iplatform-base-tcp/src/main/resources/application-dev.yml
New file
@@ -0,0 +1,476 @@
spring:
  application:
    name: deploy
  datasource:
    # 是否显示dao中打印的SQL语句
    show-sql: true
##    driver-class-name: sunje.goldilocks.jdbc.GoldilocksDriver
    # 注意:MySQL服务端,需要调整两个参数,否则服务端会主动断开连接
    # wait_timeout: 超过改时间(秒)服务端主动断开
    # interactive_timeout: 客户端工具交互超过这个时间(秒)会端口,表现为navicat
    driver-class-name: org.postgresql.Driver
    username: tbase
    password: ENC(SDQnluhnbTXI2XBdvnpBMw==)
#    url: jdbc:postgresql://116.198.40.76:30004/ctoms?useUnicode=true&characterEncoding=UTF-8&useSSL=false&stringtype=unspecified
    url: jdbc:postgresql://116.198.40.76:30004/ctoms?targetServerType=master&useUnicode=true&characterEncoding=UTF-8&useSSL=false&stringtype=unspecified
#    username: root
#    url: jdbc:mysql://116.198.40.76:3306/iplatform?useUnicode=true&characterEncoding=UTF-8&useSSL=false
    # 达梦数据库
#    driver-class-name: dm.jdbc.driver.DmDriver
#    username: iplatform
#    url: jdbc:dm://116.198.39.83:5236?schema=iplatform
    #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    # 使用 Hikari 连接池
    #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#    type: com.walker.jdbc.ds.DefaultDataSource
#    hikari:
#      minimum-idle: 5
#      idle-timeout: 600000
#      pool-name: databasePool_walker
#      # 连接最大超时时间
#      connection-timeout: 30000
#      # 连接池最大数量
#      maximum-pool-size: 10
#      # 控制池中连接最大生存期
#      # max-lifetime: 70000
#      # 此属性控制测试连接是否活跃的最长时间。此值必须小于 connectionTimeout
#      validation-timeout: 10000
#      #
#      connection-test-query: select 1
    #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    # 使用 Druid 连接池
    #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    type: com.walker.jdbc.ds.MyDruidDataSource
#    type: com.ctoms.base.support.PostgresqlDataSource
#    type: com.ctoms.base.support.PostgresqlClusterDataSource
    druid:
      initial-size: 5
      min-idle: 10
      max-active: 20
      # 配置获取连接等待超时的时间
      max-wait: 60000
      # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
      time-between-eviction-runs-millis: 60000
      # 配置一个连接在池中最小生存的时间,单位是毫秒 超过这个时间每次会回收默认3个连接
      min-evictable-idle-time-millis: 30000
      # 线上配置的mysql断开闲置连接时间为1小时,数据源配置回收时间为3分钟,以最后一次活跃时间开始算
      max-evictable-idle-time-millis: 180000
      validation-query: select 1
      test-while-idle: true
      test-on-borrow: false
      test-on-return: false
      pool-prepared-statements: true
      max-open-prepared-statements: 100
      use-global-data-source-stat: true
      webStatFilter:
        enabled: false
        statViewServlet:
          enabled: false
          # 设置白名单,不填则允许所有访问
#          allow:
#          url-pattern: /druid/*
#          # 控制台管理用户名和密码
#          login-username: ruoyi
        filter:
          stat:
            enabled: false
            # 慢SQL记录
            log-slow-sql: true
            slow-sql-millis: 1000
            merge-sql: true
          wall:
            config:
              multi-statement-allow: false
  redis:
    host: 116.198.40.76
    port: 6379
    password: ENC(VQ9j5YF08eKywBGZzMFq4g==)
    # 内网环境不认该参数,2023-11-10
#    database: 2
  mvc:
    pathmatch:
      # 加该配置是因为 swagger3 启动报错,2023-02-23
      matching-strategy: ant_path_matcher
    hiddenmethod:
      filter:
        enabled: true # 加上该配置可以接收: application/x-www-form-urlencoded请求参数为对象。支付通知中使用。2023-02-26
  servlet:
    multipart:
      #  maxFileSize 是单个文件大小
      #  maxRequestSize是设置总上传的数据大小
      enabled: true
      max-file-size: 5MB
      max-request-size: 10MB
server:
  port: 8083
  servlet:
    context-path: /tcp
    session:
      timeout: 30m
logging:
  level:
    root: info
    org:
      springframework: info
    io.swagger: error
    com:
      walker: debug
      iplatform: debug
      ctoms: debug
    RocketmqRemoting: error # MQ打印该日志,目前没有找到解决资料。2023-09-27
  charset:
    # 控制台编码
    console: UTF-8
    # 输出文件编码
    file: UTF-8
#  file: # logging.file.path 和 logging.file.name,只会有一个生效,配了path不要配name,配了name不要配path,只配path时name默认为spring.log,想路径和文件名同时生效可配置logging.file.name=d:/logs/mylog.log
#    name: ${spring.application.name}.log #日志文件名
#    path: logs  #日志存储路径
#    max-history: 30 #保留多少天的日志
#    max-size: 10MB
#  pattern:
#    rolling-file-name: ${LOG_FILE}.%d{yyyy-MM-dd}.%i.gz
  logback:
    rollingpolicy:
      # 单个文件最大为30MB,超过之后会打包成一个日志文件
      max-file-size: 10MB
      # 文件保存7天
      max-history: 1
      # 打包文件格式,默认: ${LOG_FILE}.%d{yyyy-MM-dd}.%i.gz,书写格式为:文件路径/文件名.%i.文件后缀,其中%i不可省去,否则无日志显示
      # 例如: 日期为:2023/07/13 ,则打包文件之后为: log/ota.2023-07-13.0.gz,0表示日志的第一部分,后续就是,1,2,3...
      # 如果是压缩包,里面会多一个名log/ota.2023-07-13.0的日志文件
      # 如下面的例子,打包之后为: log/2023-07/ota.2020-07-13.0.log,这是一个日志文件
      file-name-pattern: ${logging.file.path}%d{yyyy-MM}/iplatform-tcp.%d{yyyy-MM-dd}.%i.log
  file:
    name: ${logging.file.path}iplatform-tcp.log
    path: D:/log/
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# 平台配置支持功能
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
iplatform:
  # 平台缓存,是否启用redis缓存,默认使用基于内存缓存.
  # 注意:如果开启redis缓存,业务工程必须依赖(walker-support-redis)
  cache:
    redis-enabled: true
    # redis 缓存是否要重建,如果设置 true,则系统启动后会删除缓存,并执行默认重新加载方法。2023-08-26
    # 注意:该参数一般在测试阶段使用,正式环境不要使用!
    redis-rebuild: true
    # 机构用户是否很庞大,如果是则缓存中会关闭初始化加载,同时机构树也不会放入缓存,2023-07-17
    org-user-big: false
    # 单独加载机构
    org-alone-big: false
  # 相似度分析模块使用(已废弃)
#  similarity:
#    # 是否启用相似度模块,如果禁用则要确保注入时支持为空
#    enabled: false
#    milvus-host: 172.16.60.65
#    milvus-port: 19530
#    word-file: d:/dev_tools/ai/w2v_sogou_dim300_vocab.txt
#    embedding-file: d:/dev_tools/ai/w2v_sogou_dim300.npy
  # 调度器模块,是否启用
  scheduler:
    enabled: false
    # 是否支持数据库存储任务(废弃该配置)
    database-enabled: false
  # 数据采集模块
  gather:
    enabled: false
  # 权限控制相关
  security:
    # 匿名可访问地址(开放地址)
    anonymous-list:
      - /login
      - /register
      - /captcha/*      # 验证码统一放开
      - /test/**        # 开发测试,仅开发板可用
      - /template/ctoms/**
      - /swagger-resources/** # swagger
      - /swagger-ui/**  # swagger
      - /v3/**          # swagger
      - /pay/notify/**  # 支付通知
      - /jmreport/**    # 积木报表
      - /file/**          # 登录用户可访问系统文件,2023-06-09
      - /third_party/unified/** # 系统第三方登录地址,前端获取网院token后,调用后台地址实现隐式登录,2023-10-23
      - /door/**
      - /oss/** # 测试使用,正式应配置到permit下,2023-12-13
    # 允许所有认证用户都可访问地址,请慎重否则权限都过大,一般API都应该设置在这里
    permit-list:
#      - /permit          # 公用权限,2023-03-13,平台代码已经添加过了,这里不需要配置
      - /getInfo
      - /getRouters
      - /getMenus         # 新界面菜单,2023-05-12
      - /logout
      - /api/**
      - /dqyy/**              #档期预约
      - /pxjd/**              #培训资源
      - /tr/**                #教师
      - /base/**              #基地
      - /bjgl/class/**        #班级管理
      - /app/**               #app
      - /system/**            #平台系统
      - /report/**            #费用汇总
      - /settlement/**            #费用汇总记录
      - /resource/**            #资源
      - /evaluation/**        #教学评估
      - /stat/workload/**     #校内师资工作量
      - /bjgl/bus/**     #接送站
      - /base/statistics/report/**     #基地统计
      - /all/base/statistics/**  #全网统计
    # 超级管理员密码,加密后的秘文
    supervisor-password: ENC(i2QGr3kEu49MbMrW7dR+Q7jiPn1WpeuIbCfemGkhy+aMTamfXxrmKyS6j4GqG2TDm/7MTF8Q/+xqQB5pvy6rqA==)
    # 是否允许配置跨域响应头, true 启用, false 不启用。2022-12-28
    # 在Gateway模式中,需要关闭跨域配置,因为网关也会配置。
    cors-enabled: true
    # 用户名密码方式登录,配置的验证码类型:code/sms/slide/jigsaw,2023-03-14
    login-captcha-user-pass: code
    # 手机验证码方式登录,配置的验证码类型:code/sms/slide/jigsaw,2023-03-14
    login-captcha-sms-code: sms
    # 是否允许后台用户(非App)登录手机端?2023-03-20
    allow-pc-user-access-app: true
    # 是否支持手机登录时(不存在手机号)直接注册?目前电商系统支持! 2023-06-26
    allow-mobile-login-reg: true
    # PC端token失效分钟,默认:120分钟,2023-03-28
    token-expire-web: 120
    # 移动端token失效分钟,2023-03-28
    token-expire-mobile: 120
    # 用户体系(用户名)都是手机号,2023-06-28
    # 一般系统PC端通常都不是手机号作为用户名,只有在互联网相关系统中会存在手机号作为登录账号
    user-name-is-phone: false
    # 2023-07-11 登录策略配置,列表中存在的策略都表示启用(不启用的删掉)
    login-strategy-list:
#      - com.iplatform.base.support.strategy.WebOnceLoginStrategy ##同一账号只能登录一个ip
#      - com.iplatform.base.support.strategy.MobileOnceLoginStrategy
    # 用户相关安全配置,2023-08-03
    user:
      # 用户修改密码等级,分四级:1-2-3-4,常量:PasswordUtils#
      pass-level: 1
      # 是否强制用户修改默认密码?
      pass-default-modify: false
  # 代码生成,2022-11-26
  gen:
    # 作者
    author: Mike
    # 默认生成包路径 system 需改成自己的模块名称 如 system monitor tool
    package-name: com.iplatform.model.po
    # 自动去除表前缀,默认是false
    auto-remove-pre: false
    # 表前缀(生成类名不会包含表前缀,多个用逗号分隔)
    table-prefix: s_
  log:
    # 是否打开登录日志,2023-01-05
    # 如果不打开,则设备登录的uuid更新操作也无法获得,用于记录每个登录用户的uuid(用户登录角色更新),2023-03-23
    login-enabled: false
    # 是否打开操作日志,2023-01-05
    operate-enabled: false
  # 验证码相关配置,2023-01-27
  captcha:
    # 图形验证码实现类, TextCaptchaProvider | DefaultCaptchaProvider
    image-captcha-class: com.iplatform.base.captcha.TextCaptchaProvider
    # 短信验证码实现类(配置废弃,2023-04-25)
    sms-captcha-class: com.iplatform.base.captcha.MockSmsCaptchaProvider
  # 平台文件存储配置,2023-02-15
  file:
    # FileStoreType = FileSystem 本地磁盘存储根路径
    file-root: d:/tmp/
    # 调用远程上传文件,是否按照本地文件处理,2023-07-03
    # 在测试过程中如果没有FTP等服务,可以设置为true临时存储本地
    remote-as-local: false
    # oss实现类型:aws_s3(亚马逊) | tx(腾讯) | ali(阿里) | qn(七牛),2023-12-13
    oss-type: aws_s3
    # oss访问前缀,如:http://localhost:8082/admin/oss/,2023-12-13
    oss-prefix: http://localhost:8082/admin/oss/
    oss-access-key: ENC(BTHETNjs5ddRjYrdMF9ltmw8DMIEYTuyXIgDOeLIHy0=)
    oss-secret-key: ENC(jROCWGpiPULZrU1wBZAsXvQQovvqnhoJKWpmYD/3nbkNGDikxfEvCXW5jn3QsYnG)
    # oss服务地址
    oss-endpoint: https://eos-beijing-1.cmecloud.cn
    # oss默认的桶名称
    oss-bucket-name: ctoms-file
    protocol: https
#    # oss实现类型:aws_s3(亚马逊) | tx(腾讯) | ali(阿里) | qn(七牛),2023-12-13
#    oss-type: aws_s3
#    # oss访问前缀,如:http://localhost:8082/admin/oss/,2023-12-13
#    oss-prefix: http://localhost:8082/admin/oss/
#    # oss服务地址
#    oss-endpoint: http://10.10.5.191:8080
#    # oss默认的桶名称
#    oss-bucket-name: ctoms-file
    ftp:
      ip: 116.198.40.76
      port: 22
      user-name: mysftp
      password:
      private-key:
      # ftp服务上面的存储根路径,只能是linux路径
#      file-root: /files/
      file-root: /train/
  # 接口文档生成,swagger3,2023-02-23
  swagger:
    enable: false
    # 包路径,暂时不用
    base-package: com.iplatform.base.controller
    title: 云原生微服务平台
    description: 一个快速web开发框架
  # TCP通信配置,2024-01-23
  tcp:
    # 是否开启引擎
    enabled: true
    # 是否打开心跳
    open-heart-beat: true
    # 心跳时间(秒),2023-08-28
    heart-beat-seconds: 60
    # 是否显示详细日志
    show-log: false
    # 请求队列类型:memory 基于内存
    queue-type: memory
    # tcp方式要扫描的请求对象的包空间
    scan-packages-tcp: com.walker.tcp,com.iplatform.tcp
    # websocket方式要扫描的请求对象的包空间
    scan-packages-ws: com.iplatform.tcp.util.ws
    # tcp长连接的端口
    port-tcp: 7878
    # websocket连接端口
    port-ws: 60001
    # websocket连接uri
#    websocket-uri: ws://jmy.jinmingyuan.com:60001/websocket
    websocket-uri: ws://www.shikeying.com:60000/websocket
    # 通信线程数量
    boss-thread-num: 2
    # 业务处理线程数量
    worker-thread-num: 4
    # 启用自定义连接管理器,如果true则业务需要提供自定义实现(配置),2023-07-18
    custom-connection-manager: false
    # 是否开启集群模式,连接管理器会默认加载负载支持,2023-09-26
    load-balance-enabled: false
    # 长连接主机(标记)信息,可以是任意字符串,只要集群中每个服务配置不同即可。
    # 在集群模式中,每个连接要加上主机标识,以便别人推送消息时,知道在哪个主机上推送。2023-09-19
    connection-host: topic_master
  # 推送模块配置,2023-04-25
  push:
    # 短信推送者名称:mock_sms_push(模拟短信),alidy_sms_push(阿里大鱼短信)
    sms-push-name: alidy_sms_push
    # 短信验证码模板ID,根据实际情况(每个平台可能不同)
    sms-template-code: SMS_285145505
    # 对于一般业务提醒(不包括:邮件、短信),推送的方式。推送者ID组合
    # && 表示并列,|| 表示或(只要一个成功就OK)
    # 2023-04-26,可用的 id = 'tcp', 'web_socket', 'wx', 'system'
#    message-type: tcp && web_socket && wx && system
    # 如果是或者方式,需要把最可靠的放前面,因为系统会找到第一个发送成功即可。
    message-type: system || tcp || web_socket || wx
    # 邮件通知发送信息配置,2023-04-26
    mail-server: smtp.126.com
    mail-from: hnzzzhsl@126.com
    mail-password: 123456
  # RestTemplate连接池配置,2023-08-18
  rest:
    # 连接保持活动时间,默认:600秒
    keep-alive-duration-seconds: 600
    # 空闲连接数量,默认:200个
    max-idle-connections: 200
    # 连接超时,默认:2秒
    connect-timeout-seconds: 2
    # 读超时,默认3秒
    read-timeout-seconds: 3
    # 写超时,默认3秒
    write-timeout-seconds: 3
  # 集群支持相关配置,2023-09-29
  lb:
    # 本机服务节点ID,每个主机一个唯一数字编号,从1开始
    server-id: 8
  # 2024-02-29 api监控相关
  api:
    # 2024-02-29 是否打开接口时间统计记录,如果:true 则会记录接口调用时间,写入表:s_api_time
    time-enabled: false
  # 网院统一认证配置选项,2023-10-23
  unified:
    # 是否开启测试模式,测试模式用于本地测试,无法连接网院认证接口
    test-mode: true
    # 网院提供的认证地址,关闭测试模式后,该选项生效
    remote-url: http://10.30.2.86:8001/uaa/person/current
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# RocketMQ队列配置,2023-09-26
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
rocketmq:
  # 服务地址,多个用逗号分开
#  name-server: 116.198.40.76:9876
  name-server: 116.198.39.83:9898
  producer:
    # 发送消息超时时间,默认3000
    send-message-timeout: 3000
    # 生产者组
    group: producer_group
    # 发送消息失败重试次数,默认2
    retryTimesWhenSendFailed: 2
    # 异步消息重试此处,默认2
    retryTimesWhenSendAsyncFailed: 2
    # 消息最大长度,默认1024 * 1024 * 4(默认4M)
    # 1024 * 128 = 128K
    maxMessageSize: 131072
    # 压缩消息阈值,默认4k(1024 * 4)
    compressMessageBodyThreshold: 4096
    # 是否在内部发送失败时重试另一个broker,默认false
    retryNextServer: false
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# 集中培训业务器配置,2023-10-26
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
ctoms:
  # 调度器配置,2023-10-26
  scheduler:
    # 是否允许集群模式启用,
    # true:表示集群部署,则业务打包发布中不包含调度模块(ctoms-scheduler-server),调度模块需要独立打包;
    # false:表示单机部署,调度模块会被自动打包(作为一个类库)发布;
    cluster-enabled: false
    # 发布类型,
    # single:业务单应用打包,直接能运行调度任务
    # scheduler:调度任务包(独立打包支持集群)
    deploy-type: single
  # 消息推送请求地址
  push-url: "https://push.cpoc.cn/sendv2" #互联网
  link-app-url: "https://jmy.jinmingyuan.com/ctoms-h5/#/?" #移动跳转
  link-pc-url: "https://jmy.jinmingyuan.com"
#  push-url: "http://10.5.6.99:9001/sendv2" #综合网
#  push-url: "http://push.cpoc.cn:9001/sendv2" #综合网
iplatform-base-tcp/src/main/resources/application-line-dev.yml
New file
@@ -0,0 +1,480 @@
# 对应邮政内网开发环境配置
spring:
  application:
    name: deploy
  datasource:
    # 是否显示dao中打印的SQL语句
    show-sql: true
    ##    driver-class-name: sunje.goldilocks.jdbc.GoldilocksDriver
    # 注意:MySQL服务端,需要调整两个参数,否则服务端会主动断开连接
    # wait_timeout: 超过改时间(秒)服务端主动断开
    # interactive_timeout: 客户端工具交互超过这个时间(秒)会端口,表现为navicat
    username: cent_glyyb_ctoms_uat_user
    password: ENC(QlpRYCjAXS8pElGluLTsCA==)
#    url: jdbc:postgresql://10.1.149.211:11023/cent_uat_db?currentSchema=cent_glyyb_ctoms_uat_schema&useUnicode=true&characterEncoding=UTF-8&useSSL=false&stringtype=unspecified
    url: jdbc:postgresql://10.3.80.15:11002/cent_uat_db?currentSchema=cent_glyyb_ctoms_uat_schema&useUnicode=true&characterEncoding=UTF-8&useSSL=false&stringtype=unspecified
    #    username: root
    #    url: jdbc:mysql://116.198.40.76:3306/iplatform?useUnicode=true&characterEncoding=UTF-8&useSSL=false
    # 达梦数据库
    #    driver-class-name: dm.jdbc.driver.DmDriver
    #    username: iplatform
    #    url: jdbc:dm://116.198.39.83:5236?schema=iplatform
    #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    # 使用 Hikari 连接池
    #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    #    type: com.walker.jdbc.ds.DefaultDataSource
    #    hikari:
    #      minimum-idle: 5
    #      idle-timeout: 600000
    #      pool-name: databasePool_walker
    #      # 连接最大超时时间
    #      connection-timeout: 30000
    #      # 连接池最大数量
    #      maximum-pool-size: 10
    #      # 控制池中连接最大生存期
    #      # max-lifetime: 70000
    #      # 此属性控制测试连接是否活跃的最长时间。此值必须小于 connectionTimeout
    #      validation-timeout: 10000
    #      #
    #      connection-test-query: select 1
    #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    # 使用 Druid 连接池
    #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    type: com.walker.jdbc.ds.MyDruidDataSource
    druid:
      initial-size: 5
      min-idle: 10
      max-active: 20
      # 配置获取连接等待超时的时间
      max-wait: 60000
      # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
      time-between-eviction-runs-millis: 60000
      # 配置一个连接在池中最小生存的时间,单位是毫秒 超过这个时间每次会回收默认3个连接
      min-evictable-idle-time-millis: 30000
      # 线上配置的mysql断开闲置连接时间为1小时,数据源配置回收时间为3分钟,以最后一次活跃时间开始算
      max-evictable-idle-time-millis: 180000
      validation-query: select 1
      test-while-idle: true
      test-on-borrow: false
      test-on-return: false
      pool-prepared-statements: true
      max-open-prepared-statements: 100
      use-global-data-source-stat: true
      webStatFilter:
        enabled: false
        statViewServlet:
          enabled: false
          # 设置白名单,不填则允许所有访问
        #          allow:
        #          url-pattern: /druid/*
        #          # 控制台管理用户名和密码
        #          login-username: ruoyi
        filter:
          stat:
            enabled: false
            # 慢SQL记录
            log-slow-sql: true
            slow-sql-millis: 1000
            merge-sql: true
          wall:
            config:
              multi-statement-allow: false
  redis:
    host: 10.2.38.137
    port: 31512
    password: ENC(JgSbHpKSmIUMVvqeEwbst9xY3WxyJmwcNQ70gz0aZM8=)
    # 内网环境不认该参数,2023-11-10
  #    database: 2
  mvc:
    pathmatch:
      # 加该配置是因为 swagger3 启动报错,2023-02-23
      matching-strategy: ant_path_matcher
    hiddenmethod:
      filter:
        enabled: true # 加上该配置可以接收: application/x-www-form-urlencoded请求参数为对象。支付通知中使用。2023-02-26
  servlet:
    multipart:
      #  maxFileSize 是单个文件大小
      #  maxRequestSize是设置总上传的数据大小
      enabled: true
      max-file-size: 5MB
      max-request-size: 10MB
server:
  port: 8083
  servlet:
    context-path: /tcp
    session:
      timeout: 30m
logging:
  level:
    root: info
    org:
      springframework: info
    io.swagger: error
    com:
      walker: debug
      iplatform: debug
      ctoms: debug
    RocketmqRemoting: error # MQ打印该日志,目前没有找到解决资料。2023-09-27
  charset:
    # 控制台编码
    console: UTF-8
    # 输出文件编码
    file: UTF-8
  #  file: # logging.file.path 和 logging.file.name,只会有一个生效,配了path不要配name,配了name不要配path,只配path时name默认为spring.log,想路径和文件名同时生效可配置logging.file.name=d:/logs/mylog.log
  #    name: ${spring.application.name}.log #日志文件名
  #    path: logs  #日志存储路径
  #    max-history: 30 #保留多少天的日志
  #    max-size: 10MB
  #  pattern:
  #    rolling-file-name: ${LOG_FILE}.%d{yyyy-MM-dd}.%i.gz
  logback:
    rollingpolicy:
      # 单个文件最大为30MB,超过之后会打包成一个日志文件
      max-file-size: 10MB
      # 文件保存7天
      max-history: 1
      # 打包文件格式,默认: ${LOG_FILE}.%d{yyyy-MM-dd}.%i.gz,书写格式为:文件路径/文件名.%i.文件后缀,其中%i不可省去,否则无日志显示
      # 例如: 日期为:2023/07/13 ,则打包文件之后为: log/ota.2023-07-13.0.gz,0表示日志的第一部分,后续就是,1,2,3...
      # 如果是压缩包,里面会多一个名log/ota.2023-07-13.0的日志文件
      # 如下面的例子,打包之后为: log/2023-07/ota.2020-07-13.0.log,这是一个日志文件
      file-name-pattern: ${logging.file.path}%d{yyyy-MM}/iplatform-tcp.%d{yyyy-MM-dd}.%i.log
  file:
    name: ${logging.file.path}iplatform-tcp.log
    path: D:/log/
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# 平台配置支持功能
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
iplatform:
  # 平台缓存,是否启用redis缓存,默认使用基于内存缓存.
  # 注意:如果开启redis缓存,业务工程必须依赖(walker-support-redis)
  cache:
    redis-enabled: true
    # redis 缓存是否要重建,如果设置 true,则系统启动后会删除缓存,并执行默认重新加载方法。2023-08-26
    # 注意:该参数一般在测试阶段使用,正式环境不要使用!
    redis-rebuild: false
    # 机构用户是否很庞大,如果是则缓存中会关闭初始化加载,同时机构树也不会放入缓存,2023-07-17
    org-user-big: true
    # 单独加载机构
    org-alone-big: true
  # 相似度分析模块使用(已废弃)
  #  similarity:
  #    # 是否启用相似度模块,如果禁用则要确保注入时支持为空
  #    enabled: false
  #    milvus-host: 172.16.60.65
  #    milvus-port: 19530
  #    word-file: d:/dev_tools/ai/w2v_sogou_dim300_vocab.txt
  #    embedding-file: d:/dev_tools/ai/w2v_sogou_dim300.npy
  # 调度器模块,是否启用
  scheduler:
    enabled: false
    # 是否支持数据库存储任务(废弃该配置)
    database-enabled: false
  # 数据采集模块
  gather:
    enabled: false
  # 权限控制相关
  security:
    # 匿名可访问地址(开放地址)
    anonymous-list:
      - /login
      - /register
      - /captcha/*      # 验证码统一放开
      - /test/**        # 开发测试,仅开发板可用
      - /template/ctoms/**
      - /swagger-resources/** # swagger
      - /swagger-ui/**  # swagger
      - /v3/**          # swagger
      - /pay/notify/**  # 支付通知
      - /jmreport/**    # 积木报表
      - /file/**          # 登录用户可访问系统文件,2023-06-09
      - /third_party/unified/** # 系统第三方登录地址,前端获取网院token后,调用后台地址实现隐式登录,2023-10-23
    # 允许所有认证用户都可访问地址,请慎重否则权限都过大,一般API都应该设置在这里
    permit-list:
      #      - /permit          # 公用权限,2023-03-13,平台代码已经添加过了,这里不需要配置
      - /getInfo
      - /getRouters
      - /getMenus         # 新界面菜单,2023-05-12
      - /logout
      - /api/**
      - /dqyy/**              #档期预约
      - /pxjd/**              #培训资源
      - /tr/**                #教师
      - /base/**              #基地
      - /bjgl/class/**        #班级管理
      - /app/**               #app
      - /system/**            #平台系统
      - /report/**            #费用汇总
      - /settlement/**            #费用汇总记录
      - /door/**            #门户
    # 超级管理员密码,加密后的秘文
    supervisor-password: ENC(5hx5es5ySh5vySqCDFb61o1vhDVyOiA+gOBwkHjW1TbyUQwVWOVgKY+KUBOynTxOVDFFwGULPdCT8S4pJaSjGQ==)
    # 是否允许配置跨域响应头, true 启用, false 不启用。2022-12-28
    # 在Gateway模式中,需要关闭跨域配置,因为网关也会配置。
    cors-enabled: true
    # 用户名密码方式登录,配置的验证码类型:code/sms/slide/jigsaw,2023-03-14
    login-captcha-user-pass: code
    # 手机验证码方式登录,配置的验证码类型:code/sms/slide/jigsaw,2023-03-14
    login-captcha-sms-code: sms
    # 是否允许后台用户(非App)登录手机端?2023-03-20
    allow-pc-user-access-app: true
    # 是否支持手机登录时(不存在手机号)直接注册?目前电商系统支持! 2023-06-26
    allow-mobile-login-reg: true
    # PC端token失效分钟,默认:120分钟,2023-03-28
    token-expire-web: 120
    # 移动端token失效分钟,2023-03-28
    token-expire-mobile: 120
    # 用户体系(用户名)都是手机号,2023-06-28
    # 一般系统PC端通常都不是手机号作为用户名,只有在互联网相关系统中会存在手机号作为登录账号
    user-name-is-phone: false
    # 2023-07-11 登录策略配置,列表中存在的策略都表示启用(不启用的删掉)
    login-strategy-list:
    #      - com.iplatform.base.support.strategy.WebOnceLoginStrategy ##同一账号只能登录一个ip
    #      - com.iplatform.base.support.strategy.MobileOnceLoginStrategy
    # 用户相关安全配置,2023-08-03
    user:
      # 用户修改密码等级,分四级:1-2-3-4,常量:PasswordUtils#
      pass-level: 1
      # 是否强制用户修改默认密码?
      pass-default-modify: false
  # 代码生成,2022-11-26
  gen:
    # 作者
    author: Mike
    # 默认生成包路径 system 需改成自己的模块名称 如 system monitor tool
    package-name: com.iplatform.model.po
    # 自动去除表前缀,默认是false
    auto-remove-pre: false
    # 表前缀(生成类名不会包含表前缀,多个用逗号分隔)
    table-prefix: s_
  log:
    # 是否打开登录日志,2023-01-05
    # 如果不打开,则设备登录的uuid更新操作也无法获得,用于记录每个登录用户的uuid(用户登录角色更新),2023-03-23
    login-enabled: true
    # 是否打开操作日志,2023-01-05
    operate-enabled: false
  # 验证码相关配置,2023-01-27
  captcha:
    # 图形验证码实现类, TextCaptchaProvider | DefaultCaptchaProvider
    image-captcha-class: com.iplatform.base.captcha.TextCaptchaProvider
    # 短信验证码实现类(配置废弃,2023-04-25)
    sms-captcha-class: com.iplatform.base.captcha.MockSmsCaptchaProvider
  # 平台文件存储配置,2023-02-15
  file:
    # FileStoreType = FileSystem 本地磁盘存储根路径
    file-root: d:/tmp/
    # 调用远程上传文件,是否按照本地文件处理,2023-07-03
    # 在测试过程中如果没有FTP等服务,可以设置为true临时存储本地
    remote-as-local: false
    # oss实现类型:aws_s3(亚马逊) | tx(腾讯) | ali(阿里) | qn(七牛),2023-12-13
    oss-type: aws_s3
    # oss访问前缀,如:http://localhost:8082/admin/oss/,2023-12-13
    oss-prefix: http://localhost:8082/admin/oss/
    oss-access-key: ENC(kLcOGmTADXxiDphSUZvMevg1Hw+mUhiX7w8Z7U1FQD8=)
    oss-secret-key: ENC(w+UXEgJ4OhBqDdEP/ev6y4AisCUcUUAdDsUal24SvI1Ml8POKkIVlheaQn9qeo34)
    # oss服务地址
    oss-endpoint: http://10.2.36.9:8080
    # oss默认的桶名称
    oss-bucket-name: ctoms-file
    ftp:
      ip: 116.198.40.76
      port: 22
      user-name: mysftp
      password: ENC(zHTagUyi1g+/syVY8gVOww==)
      private-key:
      # ftp服务上面的存储根路径,只能是linux路径
      #      file-root: /files/
      file-root: /train/
  # 接口文档生成,swagger3,2023-02-23
  swagger:
    enable: false
    # 包路径,暂时不用
    base-package: com.iplatform.base.controller
    title: 云原生微服务平台
    description: 一个快速web开发框架
  # TCP通信配置,2024-01-23
  tcp:
    # 是否开启引擎
    enabled: true
    # 是否打开心跳
    open-heart-beat: true
    # 心跳时间(秒),2023-08-28
    heart-beat-seconds: 60
    # 是否显示详细日志
    show-log: false
    # 请求队列类型:memory 基于内存
    queue-type: memory
    # tcp方式要扫描的请求对象的包空间
    scan-packages-tcp: com.walker.tcp,com.iplatform.tcp
    # websocket方式要扫描的请求对象的包空间
    scan-packages-ws: com.iplatform.tcp.util.ws
    # tcp长连接的端口
    port-tcp: 7878
    # websocket连接端口
    port-ws: 60001
    # websocket连接uri
    websocket-uri: wss://ctomsuat.chinapost.com.cn/ws
    # 通信线程数量
    boss-thread-num: 2
    # 业务处理线程数量
    worker-thread-num: 4
    # 启用自定义连接管理器,如果true则业务需要提供自定义实现(配置),2023-07-18
    custom-connection-manager: false
    # 是否开启集群模式,连接管理器会默认加载负载支持,2023-09-26
    load-balance-enabled: false
    # 长连接主机(标记)信息,可以是任意字符串,只要集群中每个服务配置不同即可。
    # 在集群模式中,每个连接要加上主机标识,以便别人推送消息时,知道在哪个主机上推送。2023-09-19
    connection-host: topic_master
  # 推送模块配置,2023-04-25
  push:
    # 短信推送者名称:mock_sms_push(模拟短信),alidy_sms_push(阿里大鱼短信)
    sms-push-name: alidy_sms_push
    # 短信验证码模板ID,根据实际情况(每个平台可能不同)
    sms-template-code: SMS_285145505
    # 对于一般业务提醒(不包括:邮件、短信),推送的方式。推送者ID组合
    # && 表示并列,|| 表示或(只要一个成功就OK)
    # 2023-04-26,可用的 id = 'tcp', 'web_socket', 'wx', 'system'
    #    message-type: tcp && web_socket && wx && system
    # 如果是或者方式,需要把最可靠的放前面,因为系统会找到第一个发送成功即可。
    message-type: system || tcp || web_socket || wx
    # 邮件通知发送信息配置,2023-04-26
    mail-server: smtp.126.com
    mail-from: hnzzzhsl@126.com
    mail-password: ENC(8Xr7W72lyjRVi0InopfITw==)
  # 聊天相关配置,2023-07-07
  #  chat:
  #    # 是否启用机器人答复,2023-07-12
  #    robot-enabled: true
  #    # 机器人具体实现对象,2023-07-12
  #    robot-class: com.iplatform.chat.support.TestRobot
  #    # 是否开启mongo存储,如果false则默认使用数据库表
  #    mongo-enabled: true
  #    mongo:
  #      ip: 116.198.40.76
  #      port: 27017
  #      # 聊天数据库名称,默认:chat
  #      database: czt_ops_test
  #      user-name: czt_ops_test
  #      password: czt_ops_test
  #      max-size: 10
  #      min-size: 2
  #      # 连接最大空闲时间,20分钟
  #      max-idle-time-seconds: 1200
  #      # 连接等待最大时间,10秒
  #      max-wait-time-seconds: 10
  # RestTemplate连接池配置,2023-08-18
  rest:
    # 连接保持活动时间,默认:600秒
    keep-alive-duration-seconds: 600
    # 空闲连接数量,默认:200个
    max-idle-connections: 200
    # 连接超时,默认:2秒
    connect-timeout-seconds: 2
    # 读超时,默认3秒
    read-timeout-seconds: 3
    # 写超时,默认3秒
    write-timeout-seconds: 3
  # 集群支持相关配置,2023-09-29
  lb:
    # 本机服务节点ID,每个主机一个唯一数字编号,从1开始
    server-id: 8
  # 2024-02-29 api监控相关
  api:
    # 2024-02-29 是否打开接口时间统计记录,如果:true 则会记录接口调用时间,写入表:s_api_time
    time-enabled: false
  # 网院统一认证配置选项,2023-10-23
  unified:
    # 是否开启测试模式,测试模式用于本地测试,无法连接网院认证接口
    test-mode: false
    # 网院提供的认证地址,关闭测试模式后,该选项生效
    remote-url: http://10.5.6.105:8001/uaa/person/current
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# RocketMQ队列配置,2023-09-26
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
rocketmq:
  # 服务地址,多个用逗号分开
  #  name-server: 116.198.40.76:9876
  name-server: 116.198.39.83:9898
  producer:
    # 发送消息超时时间,默认3000
    send-message-timeout: 3000
    # 生产者组
    group: producer_group
    # 发送消息失败重试次数,默认2
    retryTimesWhenSendFailed: 2
    # 异步消息重试此处,默认2
    retryTimesWhenSendAsyncFailed: 2
    # 消息最大长度,默认1024 * 1024 * 4(默认4M)
    # 1024 * 128 = 128K
    maxMessageSize: 131072
    # 压缩消息阈值,默认4k(1024 * 4)
    compressMessageBodyThreshold: 4096
    # 是否在内部发送失败时重试另一个broker,默认false
    retryNextServer: false
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# 集中培训业务器配置,2023-10-26
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
ctoms:
  # 调度器配置,2023-10-26
  scheduler:
    # 是否允许集群模式启用,
    # true:表示集群部署,则业务打包发布中不包含调度模块(ctoms-scheduler-server),调度模块需要独立打包;
    # false:表示单机部署,调度模块会被自动打包(作为一个类库)发布;
    cluster-enabled: false
    # 发布类型,
    # single:业务单应用打包,直接能运行调度任务
    # scheduler:调度任务包(独立打包支持集群)
    deploy-type: single
#  push-url: "https://push.cpoc.cn/sendv2" #互联网
  push-url: "http://10.5.6.99:9001/sendv2" #综合网
  link-app-url: "https://ctomsuat.chinapost.com.cn/ctoms-h5/#/?" #移动跳转
  link-pc-url: "http://websocket:8083/tcp/"
#  push-url: "http://push.cpoc.cn:9001/sendv2" #综合网
iplatform-base-tcp/src/main/resources/application-line-prod.yml
New file
@@ -0,0 +1,480 @@
# 对应邮政内网开发环境配置
spring:
  application:
    name: deploy
  datasource:
    # 是否显示dao中打印的SQL语句
    show-sql: true
    ##    driver-class-name: sunje.goldilocks.jdbc.GoldilocksDriver
    # 注意:MySQL服务端,需要调整两个参数,否则服务端会主动断开连接
    # wait_timeout: 超过改时间(秒)服务端主动断开
    # interactive_timeout: 客户端工具交互超过这个时间(秒)会端口,表现为navicat
    username: cent_glyyb_ctoms_sc_user
    password: ENC(Z/HgOaZnJvhO9EiwCu71jA==)
    url: jdbc:postgresql://10.1.153.107:11004/cent_ctoms_sc_db?currentSchema=cent_glyyb_ctoms_sc_schema&targetServerType=primary&useUnicode=true&characterEncoding=UTF-8&useSSL=false&stringtype=unspecified
    #    username: root
    #    url: jdbc:mysql://116.198.40.76:3306/iplatform?useUnicode=true&characterEncoding=UTF-8&useSSL=false
    # 达梦数据库
    #    driver-class-name: dm.jdbc.driver.DmDriver
    #    username: iplatform
    #    url: jdbc:dm://116.198.39.83:5236?schema=iplatform
    #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    # 使用 Hikari 连接池
    #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    #    type: com.walker.jdbc.ds.DefaultDataSource
    #    hikari:
    #      minimum-idle: 5
    #      idle-timeout: 600000
    #      pool-name: databasePool_walker
    #      # 连接最大超时时间
    #      connection-timeout: 30000
    #      # 连接池最大数量
    #      maximum-pool-size: 10
    #      # 控制池中连接最大生存期
    #      # max-lifetime: 70000
    #      # 此属性控制测试连接是否活跃的最长时间。此值必须小于 connectionTimeout
    #      validation-timeout: 10000
    #      #
    #      connection-test-query: select 1
    #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    # 使用 Druid 连接池
    #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    type: com.walker.jdbc.ds.MyDruidDataSource
#    type: com.ctoms.base.support.PostgresqlClusterDataSource
    druid:
      initial-size: 5
      min-idle: 10
      max-active: 20
      # 配置获取连接等待超时的时间
      max-wait: 60000
      # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
      time-between-eviction-runs-millis: 60000
      # 配置一个连接在池中最小生存的时间,单位是毫秒 超过这个时间每次会回收默认3个连接
      min-evictable-idle-time-millis: 30000
      # 线上配置的mysql断开闲置连接时间为1小时,数据源配置回收时间为3分钟,以最后一次活跃时间开始算
      max-evictable-idle-time-millis: 180000
      validation-query: select 1
      test-while-idle: true
      test-on-borrow: false
      test-on-return: false
      pool-prepared-statements: true
      max-open-prepared-statements: 100
      use-global-data-source-stat: true
      webStatFilter:
        enabled: false
        statViewServlet:
          enabled: false
          # 设置白名单,不填则允许所有访问
        #          allow:
        #          url-pattern: /druid/*
        #          # 控制台管理用户名和密码
        #          login-username: ruoyi
        filter:
          stat:
            enabled: false
            # 慢SQL记录
            log-slow-sql: true
            slow-sql-millis: 1000
            merge-sql: true
          wall:
            config:
              multi-statement-allow: false
  redis:
    host: 10.2.38.137
    port: 32157
    password: ENC(Kzn/kublbAelEFPfxxw8DZoNqudtoNUpUb/AOl1/s60=)
    # 内网环境不认该参数,2023-11-10
  #    database: 2
  mvc:
    pathmatch:
      # 加该配置是因为 swagger3 启动报错,2023-02-23
      matching-strategy: ant_path_matcher
    hiddenmethod:
      filter:
        enabled: true # 加上该配置可以接收: application/x-www-form-urlencoded请求参数为对象。支付通知中使用。2023-02-26
  servlet:
    multipart:
      #  maxFileSize 是单个文件大小
      #  maxRequestSize是设置总上传的数据大小
      enabled: true
      max-file-size: 5MB
      max-request-size: 10MB
server:
  port: 8083
  servlet:
    context-path: /tcp
    session:
      timeout: 30m
logging:
  level:
    root: info
    org:
      springframework: info
    io.swagger: error
    com:
      walker: debug
      iplatform: debug
      ctoms: debug
    RocketmqRemoting: error # MQ打印该日志,目前没有找到解决资料。2023-09-27
  charset:
    # 控制台编码
    console: UTF-8
    # 输出文件编码
    file: UTF-8
  #  file: # logging.file.path 和 logging.file.name,只会有一个生效,配了path不要配name,配了name不要配path,只配path时name默认为spring.log,想路径和文件名同时生效可配置logging.file.name=d:/logs/mylog.log
  #    name: ${spring.application.name}.log #日志文件名
  #    path: logs  #日志存储路径
  #    max-history: 30 #保留多少天的日志
  #    max-size: 10MB
  #  pattern:
  #    rolling-file-name: ${LOG_FILE}.%d{yyyy-MM-dd}.%i.gz
  logback:
    rollingpolicy:
      # 单个文件最大为30MB,超过之后会打包成一个日志文件
      max-file-size: 10MB
      # 文件保存7天
      max-history: 1
      # 打包文件格式,默认: ${LOG_FILE}.%d{yyyy-MM-dd}.%i.gz,书写格式为:文件路径/文件名.%i.文件后缀,其中%i不可省去,否则无日志显示
      # 例如: 日期为:2023/07/13 ,则打包文件之后为: log/ota.2023-07-13.0.gz,0表示日志的第一部分,后续就是,1,2,3...
      # 如果是压缩包,里面会多一个名log/ota.2023-07-13.0的日志文件
      # 如下面的例子,打包之后为: log/2023-07/ota.2020-07-13.0.log,这是一个日志文件
      file-name-pattern: ${logging.file.path}%d{yyyy-MM}/iplatform-tcp.%d{yyyy-MM-dd}.%i.log
  file:
    name: ${logging.file.path}iplatform-tcp.log
    path: D:/log/
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# 平台配置支持功能
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
iplatform:
  # 平台缓存,是否启用redis缓存,默认使用基于内存缓存.
  # 注意:如果开启redis缓存,业务工程必须依赖(walker-support-redis)
  cache:
    redis-enabled: true
    # redis 缓存是否要重建,如果设置 true,则系统启动后会删除缓存,并执行默认重新加载方法。2023-08-26
    # 注意:该参数一般在测试阶段使用,正式环境不要使用!
    redis-rebuild: false
    # 机构用户是否很庞大,如果是则缓存中会关闭初始化加载,同时机构树也不会放入缓存,2023-07-17
    org-user-big: true
    # 单独加载机构
    org-alone-big: true
  # 相似度分析模块使用(已废弃)
  #  similarity:
  #    # 是否启用相似度模块,如果禁用则要确保注入时支持为空
  #    enabled: false
  #    milvus-host: 172.16.60.65
  #    milvus-port: 19530
  #    word-file: d:/dev_tools/ai/w2v_sogou_dim300_vocab.txt
  #    embedding-file: d:/dev_tools/ai/w2v_sogou_dim300.npy
  # 调度器模块,是否启用
  scheduler:
    enabled: false
    # 是否支持数据库存储任务(废弃该配置)
    database-enabled: false
  # 数据采集模块
  gather:
    enabled: false
  # 权限控制相关
  security:
    # 匿名可访问地址(开放地址)
    anonymous-list:
      - /login
      - /register
      - /captcha/*      # 验证码统一放开
      - /test/**        # 开发测试,仅开发板可用
      - /template/ctoms/**
      - /swagger-resources/** # swagger
      - /swagger-ui/**  # swagger
      - /v3/**          # swagger
      - /pay/notify/**  # 支付通知
      - /jmreport/**    # 积木报表
      - /file/**          # 登录用户可访问系统文件,2023-06-09
      - /third_party/unified/** # 系统第三方登录地址,前端获取网院token后,调用后台地址实现隐式登录,2023-10-23
    # 允许所有认证用户都可访问地址,请慎重否则权限都过大,一般API都应该设置在这里
    permit-list:
      #      - /permit          # 公用权限,2023-03-13,平台代码已经添加过了,这里不需要配置
      - /getInfo
      - /getRouters
      - /getMenus         # 新界面菜单,2023-05-12
      - /logout
      - /api/**
      - /dqyy/**              #档期预约
      - /pxjd/**              #培训资源
      - /tr/**                #教师
      - /base/**              #基地
      - /bjgl/class/**        #班级管理
      - /app/**               #app
      - /system/**            #平台系统
      - /report/**            #费用汇总
      - /settlement/**            #费用汇总记录
      - /door/**            #门户
    # 超级管理员密码,加密后的秘文
    supervisor-password: ENC(5hx5es5ySh5vySqCDFb61o1vhDVyOiA+gOBwkHjW1TbyUQwVWOVgKY+KUBOynTxOVDFFwGULPdCT8S4pJaSjGQ==)
    # 是否允许配置跨域响应头, true 启用, false 不启用。2022-12-28
    # 在Gateway模式中,需要关闭跨域配置,因为网关也会配置。
    cors-enabled: true
    # 用户名密码方式登录,配置的验证码类型:code/sms/slide/jigsaw,2023-03-14
    login-captcha-user-pass: code
    # 手机验证码方式登录,配置的验证码类型:code/sms/slide/jigsaw,2023-03-14
    login-captcha-sms-code: sms
    # 是否允许后台用户(非App)登录手机端?2023-03-20
    allow-pc-user-access-app: true
    # 是否支持手机登录时(不存在手机号)直接注册?目前电商系统支持! 2023-06-26
    allow-mobile-login-reg: true
    # PC端token失效分钟,默认:120分钟,2023-03-28
    token-expire-web: 120
    # 移动端token失效分钟,2023-03-28
    token-expire-mobile: 120
    # 用户体系(用户名)都是手机号,2023-06-28
    # 一般系统PC端通常都不是手机号作为用户名,只有在互联网相关系统中会存在手机号作为登录账号
    user-name-is-phone: false
    # 2023-07-11 登录策略配置,列表中存在的策略都表示启用(不启用的删掉)
    login-strategy-list:
    #      - com.iplatform.base.support.strategy.WebOnceLoginStrategy ##同一账号只能登录一个ip
    #      - com.iplatform.base.support.strategy.MobileOnceLoginStrategy
    # 用户相关安全配置,2023-08-03
    user:
      # 用户修改密码等级,分四级:1-2-3-4,常量:PasswordUtils#
      pass-level: 1
      # 是否强制用户修改默认密码?
      pass-default-modify: false
  # 代码生成,2022-11-26
  gen:
    # 作者
    author: Mike
    # 默认生成包路径 system 需改成自己的模块名称 如 system monitor tool
    package-name: com.iplatform.model.po
    # 自动去除表前缀,默认是false
    auto-remove-pre: false
    # 表前缀(生成类名不会包含表前缀,多个用逗号分隔)
    table-prefix: s_
  log:
    # 是否打开登录日志,2023-01-05
    # 如果不打开,则设备登录的uuid更新操作也无法获得,用于记录每个登录用户的uuid(用户登录角色更新),2023-03-23
    login-enabled: true
    # 是否打开操作日志,2023-01-05
    operate-enabled: false
  # 验证码相关配置,2023-01-27
  captcha:
    # 图形验证码实现类, TextCaptchaProvider | DefaultCaptchaProvider
    image-captcha-class: com.iplatform.base.captcha.TextCaptchaProvider
    # 短信验证码实现类(配置废弃,2023-04-25)
    sms-captcha-class: com.iplatform.base.captcha.MockSmsCaptchaProvider
  # 平台文件存储配置,2023-02-15
  file:
    # FileStoreType = FileSystem 本地磁盘存储根路径
    file-root: d:/tmp/
    # 调用远程上传文件,是否按照本地文件处理,2023-07-03
    # 在测试过程中如果没有FTP等服务,可以设置为true临时存储本地
    remote-as-local: false
    # oss实现类型:aws_s3(亚马逊) | tx(腾讯) | ali(阿里) | qn(七牛),2023-12-13
    oss-type: aws_s3
    # oss访问前缀,如:http://localhost:8082/admin/oss/,2023-12-13
    oss-prefix: http://localhost:8082/admin/oss/
    oss-access-key: 5YOZCZF4AFDGJBAFEFF9
    oss-secret-key: aE3EXCkMflK5VtOZeAI1ySX7acWoPB1KhOBOPGgf
    # oss服务地址
    oss-endpoint: http://10.2.36.9:8080
    # oss默认的桶名称
    oss-bucket-name: ctoms-file
    ftp:
      ip: 116.198.40.76
      port: 22
      user-name: mysftp
      password:
      private-key:
      # ftp服务上面的存储根路径,只能是linux路径
      #      file-root: /files/
      file-root: /train/
  # 接口文档生成,swagger3,2023-02-23
  swagger:
    enable: false
    # 包路径,暂时不用
    base-package: com.iplatform.base.controller
    title: 云原生微服务平台
    description: 一个快速web开发框架
  # TCP通信配置,2024-01-23
  tcp:
    # 是否开启引擎
    enabled: true
    # 是否打开心跳
    open-heart-beat: true
    # 心跳时间(秒),2023-08-28
    heart-beat-seconds: 60
    # 是否显示详细日志
    show-log: false
    # 请求队列类型:memory 基于内存
    queue-type: memory
    # tcp方式要扫描的请求对象的包空间
    scan-packages-tcp: com.walker.tcp,com.iplatform.tcp
    # websocket方式要扫描的请求对象的包空间
    scan-packages-ws: com.iplatform.tcp.util.ws
    # tcp长连接的端口
    port-tcp: 7878
    # websocket连接端口
    port-ws: 60001
    # websocket连接uri
    websocket-uri: wss://ctoms.chinapost.com.cn/ws
    # 通信线程数量
    boss-thread-num: 2
    # 业务处理线程数量
    worker-thread-num: 4
    # 启用自定义连接管理器,如果true则业务需要提供自定义实现(配置),2023-07-18
    custom-connection-manager: false
    # 是否开启集群模式,连接管理器会默认加载负载支持,2023-09-26
    load-balance-enabled: false
    # 长连接主机(标记)信息,可以是任意字符串,只要集群中每个服务配置不同即可。
    # 在集群模式中,每个连接要加上主机标识,以便别人推送消息时,知道在哪个主机上推送。2023-09-19
    connection-host: topic_master
  # 推送模块配置,2023-04-25
  push:
    # 短信推送者名称:mock_sms_push(模拟短信),alidy_sms_push(阿里大鱼短信)
    sms-push-name: alidy_sms_push
    # 短信验证码模板ID,根据实际情况(每个平台可能不同)
    sms-template-code: SMS_285145505
    # 对于一般业务提醒(不包括:邮件、短信),推送的方式。推送者ID组合
    # && 表示并列,|| 表示或(只要一个成功就OK)
    # 2023-04-26,可用的 id = 'tcp', 'web_socket', 'wx', 'system'
    #    message-type: tcp && web_socket && wx && system
    # 如果是或者方式,需要把最可靠的放前面,因为系统会找到第一个发送成功即可。
    message-type: system || tcp || web_socket || wx
    # 邮件通知发送信息配置,2023-04-26
    mail-server: smtp.126.com
    mail-from: hnzzzhsl@126.com
    mail-password:
  # 聊天相关配置,2023-07-07
  #  chat:
  #    # 是否启用机器人答复,2023-07-12
  #    robot-enabled: true
  #    # 机器人具体实现对象,2023-07-12
  #    robot-class: com.iplatform.chat.support.TestRobot
  #    # 是否开启mongo存储,如果false则默认使用数据库表
  #    mongo-enabled: true
  #    mongo:
  #      ip: 116.198.40.76
  #      port: 27017
  #      # 聊天数据库名称,默认:chat
  #      database: czt_ops_test
  #      user-name: czt_ops_test
  #      password: czt_ops_test
  #      max-size: 10
  #      min-size: 2
  #      # 连接最大空闲时间,20分钟
  #      max-idle-time-seconds: 1200
  #      # 连接等待最大时间,10秒
  #      max-wait-time-seconds: 10
  # RestTemplate连接池配置,2023-08-18
  rest:
    # 连接保持活动时间,默认:600秒
    keep-alive-duration-seconds: 600
    # 空闲连接数量,默认:200个
    max-idle-connections: 200
    # 连接超时,默认:2秒
    connect-timeout-seconds: 2
    # 读超时,默认3秒
    read-timeout-seconds: 3
    # 写超时,默认3秒
    write-timeout-seconds: 3
  # 集群支持相关配置,2023-09-29
  lb:
    # 本机服务节点ID,每个主机一个唯一数字编号,从1开始
    server-id: 8
  # 2024-02-29 api监控相关
  api:
    # 2024-02-29 是否打开接口时间统计记录,如果:true 则会记录接口调用时间,写入表:s_api_time
    time-enabled: false
  # 网院统一认证配置选项,2023-10-23
  unified:
    # 是否开启测试模式,测试模式用于本地测试,无法连接网院认证接口
    test-mode: false
    # 网院提供的认证地址,关闭测试模式后,该选项生效
    remote-url: http://10.5.6.105:8001/uaa/person/current
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# RocketMQ队列配置,2023-09-26
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
rocketmq:
  # 服务地址,多个用逗号分开
  #  name-server: 116.198.40.76:9876
  name-server: 116.198.39.83:9898
  producer:
    # 发送消息超时时间,默认3000
    send-message-timeout: 3000
    # 生产者组
    group: producer_group
    # 发送消息失败重试次数,默认2
    retryTimesWhenSendFailed: 2
    # 异步消息重试此处,默认2
    retryTimesWhenSendAsyncFailed: 2
    # 消息最大长度,默认1024 * 1024 * 4(默认4M)
    # 1024 * 128 = 128K
    maxMessageSize: 131072
    # 压缩消息阈值,默认4k(1024 * 4)
    compressMessageBodyThreshold: 4096
    # 是否在内部发送失败时重试另一个broker,默认false
    retryNextServer: false
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# 集中培训业务器配置,2023-10-26
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
ctoms:
  # 调度器配置,2023-10-26
  scheduler:
    # 是否允许集群模式启用,
    # true:表示集群部署,则业务打包发布中不包含调度模块(ctoms-scheduler-server),调度模块需要独立打包;
    # false:表示单机部署,调度模块会被自动打包(作为一个类库)发布;
    cluster-enabled: false
    # 发布类型,
    # single:业务单应用打包,直接能运行调度任务
    # scheduler:调度任务包(独立打包支持集群)
    deploy-type: single
#  push-url: "https://push.cpoc.cn/sendv2" #互联网
  push-url: "http://10.5.6.99:9001/sendv2" #综合网
  link-app-url: "https://ctoms.chinapost.com.cn/ctoms-h5/#/?" #移动跳转
  link-pc-url: "http://websocket:8083/tcp/"
#  push-url: "http://push.cpoc.cn:9001/sendv2" #综合网
iplatform-base-tcp/src/main/resources/application-test.yml
New file
@@ -0,0 +1,485 @@
spring:
  application:
    name: deploy
  datasource:
    # 是否显示dao中打印的SQL语句
    show-sql: true
    ##    driver-class-name: sunje.goldilocks.jdbc.GoldilocksDriver
    # 注意:MySQL服务端,需要调整两个参数,否则服务端会主动断开连接
    # wait_timeout: 超过改时间(秒)服务端主动断开
    # interactive_timeout: 客户端工具交互超过这个时间(秒)会端口,表现为navicat
    username: tbase
    password: ENC(SDQnluhnbTXI2XBdvnpBMw==)
    url: jdbc:postgresql://116.198.40.76:30004/ctoms-beifen?useUnicode=true&characterEncoding=UTF-8&useSSL=false&stringtype=unspecified
    #    username: root
    #    password: Bjjmy_2020
    #    url: jdbc:mysql://116.198.40.76:3306/iplatform?useUnicode=true&characterEncoding=UTF-8&useSSL=false
    # 达梦数据库
    #    driver-class-name: dm.jdbc.driver.DmDriver
    #    username: iplatform
    #    password: iplatform
    #    url: jdbc:dm://116.198.39.83:5236?schema=iplatform
    #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    # 使用 Hikari 连接池
    #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    #    type: com.walker.jdbc.ds.DefaultDataSource
    #    hikari:
    #      minimum-idle: 5
    #      idle-timeout: 600000
    #      pool-name: databasePool_walker
    #      # 连接最大超时时间
    #      connection-timeout: 30000
    #      # 连接池最大数量
    #      maximum-pool-size: 10
    #      # 控制池中连接最大生存期
    #      # max-lifetime: 70000
    #      # 此属性控制测试连接是否活跃的最长时间。此值必须小于 connectionTimeout
    #      validation-timeout: 10000
    #      #
    #      connection-test-query: select 1
    #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    # 使用 Druid 连接池
    #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    type: com.walker.jdbc.ds.MyDruidDataSource
    druid:
      initial-size: 5
      min-idle: 10
      max-active: 20
      # 配置获取连接等待超时的时间
      max-wait: 60000
      # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
      time-between-eviction-runs-millis: 60000
      # 配置一个连接在池中最小生存的时间,单位是毫秒 超过这个时间每次会回收默认3个连接
      min-evictable-idle-time-millis: 30000
      # 线上配置的mysql断开闲置连接时间为1小时,数据源配置回收时间为3分钟,以最后一次活跃时间开始算
      max-evictable-idle-time-millis: 180000
      validation-query: select 1
      test-while-idle: true
      test-on-borrow: false
      test-on-return: false
      pool-prepared-statements: true
      max-open-prepared-statements: 100
      use-global-data-source-stat: true
      webStatFilter:
        enabled: false
        statViewServlet:
          enabled: false
          # 设置白名单,不填则允许所有访问
        #          allow:
        #          url-pattern: /druid/*
        #          # 控制台管理用户名和密码
        #          login-username: ruoyi
        filter:
          stat:
            enabled: false
            # 慢SQL记录
            log-slow-sql: true
            slow-sql-millis: 1000
            merge-sql: true
          wall:
            config:
              multi-statement-allow: false
  redis:
    host: 116.198.40.76
    port: 6379
    password: ENC(VQ9j5YF08eKywBGZzMFq4g==)
    # 内网环境不认该参数,2023-11-10
    database: 2
  mvc:
    pathmatch:
      # 加该配置是因为 swagger3 启动报错,2023-02-23
      matching-strategy: ant_path_matcher
    hiddenmethod:
      filter:
        enabled: true # 加上该配置可以接收: application/x-www-form-urlencoded请求参数为对象。支付通知中使用。2023-02-26
  servlet:
    multipart:
      #  maxFileSize 是单个文件大小
      #  maxRequestSize是设置总上传的数据大小
      enabled: true
      max-file-size: 5MB
      max-request-size: 10MB
server:
  port: 8088
  servlet:
    context-path: /tcp
    session:
      timeout: 30m
logging:
  level:
    root: info
    org:
      springframework: info
    io.swagger: error
    com:
      walker: debug
      iplatform: debug
      ctoms: debug
    RocketmqRemoting: error # MQ打印该日志,目前没有找到解决资料。2023-09-27
  charset:
    # 控制台编码
    console: UTF-8
    # 输出文件编码
    file: UTF-8
  #  file: # logging.file.path 和 logging.file.name,只会有一个生效,配了path不要配name,配了name不要配path,只配path时name默认为spring.log,想路径和文件名同时生效可配置logging.file.name=d:/logs/mylog.log
  #    name: ${spring.application.name}.log #日志文件名
  #    path: logs  #日志存储路径
  #    max-history: 30 #保留多少天的日志
  #    max-size: 10MB
  #  pattern:
  #    rolling-file-name: ${LOG_FILE}.%d{yyyy-MM-dd}.%i.gz
  logback:
    rollingpolicy:
      # 单个文件最大为30MB,超过之后会打包成一个日志文件
      max-file-size: 10MB
      # 文件保存7天
      max-history: 1
      # 打包文件格式,默认: ${LOG_FILE}.%d{yyyy-MM-dd}.%i.gz,书写格式为:文件路径/文件名.%i.文件后缀,其中%i不可省去,否则无日志显示
      # 例如: 日期为:2023/07/13 ,则打包文件之后为: log/ota.2023-07-13.0.gz,0表示日志的第一部分,后续就是,1,2,3...
      # 如果是压缩包,里面会多一个名log/ota.2023-07-13.0的日志文件
      # 如下面的例子,打包之后为: log/2023-07/ota.2020-07-13.0.log,这是一个日志文件
      file-name-pattern: ${logging.file.path}%d{yyyy-MM}/iplatform-tcp.%d{yyyy-MM-dd}.%i.log
  file:
    name: ${logging.file.path}iplatform-tcp.log
    path: D:/log/
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# 平台配置支持功能
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
iplatform:
  # 平台缓存,是否启用redis缓存,默认使用基于内存缓存.
  # 注意:如果开启redis缓存,业务工程必须依赖(walker-support-redis)
  cache:
    redis-enabled: true
    # redis 缓存是否要重建,如果设置 true,则系统启动后会删除缓存,并执行默认重新加载方法。2023-08-26
    # 注意:该参数一般在测试阶段使用,正式环境不要使用!
    redis-rebuild: true
    # 机构用户是否很庞大,如果是则缓存中会关闭初始化加载,同时机构树也不会放入缓存,2023-07-17
    org-user-big: true
    # 单独加载机构
    org-alone-big: true
  # 相似度分析模块使用(已废弃)
  #  similarity:
  #    # 是否启用相似度模块,如果禁用则要确保注入时支持为空
  #    enabled: false
  #    milvus-host: 172.16.60.65
  #    milvus-port: 19530
  #    word-file: d:/dev_tools/ai/w2v_sogou_dim300_vocab.txt
  #    embedding-file: d:/dev_tools/ai/w2v_sogou_dim300.npy
  # 调度器模块,是否启用
  scheduler:
    enabled: false
    # 是否支持数据库存储任务(废弃该配置)
    database-enabled: false
  # 数据采集模块
  gather:
    enabled: false
  security:
    # 匿名可访问地址(开放地址)
    anonymous-list:
      - /login
      - /register
      - /captcha/*      # 验证码统一放开
      - /test/**        # 开发测试,仅开发板可用
      - /template/ctoms/**
      - /swagger-resources/** # swagger
      - /swagger-ui/**  # swagger
      - /v3/**          # swagger
      - /pay/notify/**  # 支付通知
      - /jmreport/**    # 积木报表
      - /file/**          # 登录用户可访问系统文件,2023-06-09
      - /third_party/unified/** # 系统第三方登录地址,前端获取网院token后,调用后台地址实现隐式登录,2023-10-23
      - /door/**
      - /oss/** # 测试使用,正式应配置到permit下,2023-12-13
    # 允许所有认证用户都可访问地址,请慎重否则权限都过大,一般API都应该设置在这里
    permit-list:
      #      - /permit          # 公用权限,2023-03-13,平台代码已经添加过了,这里不需要配置
      - /getInfo
      - /getRouters
      - /getMenus         # 新界面菜单,2023-05-12
      - /logout
      - /api/**
      - /dqyy/**              #档期预约
      - /pxjd/**              #培训资源
      - /tr/**                #教师
      - /base/**              #基地
      - /bjgl/class/**        #班级管理
      - /app/**               #app
      - /system/**            #平台系统
      - /report/**            #费用汇总
      - /settlement/**            #费用汇总记录
      - /resource/**            #资源
      - /evaluation/**        #教学评估
      - /stat/workload/**     #校内师资工作量
      - /bjgl/bus/**     #接送站
      - /all/base/statistics/**  #全网统计
    # 超级管理员密码,加密后的秘文
    supervisor-password: ENC(i2QGr3kEu49MbMrW7dR+Q7jiPn1WpeuIbCfemGkhy+aMTamfXxrmKyS6j4GqG2TDm/7MTF8Q/+xqQB5pvy6rqA==)
    # 是否允许配置跨域响应头, true 启用, false 不启用。2022-12-28
    # 在Gateway模式中,需要关闭跨域配置,因为网关也会配置。
    cors-enabled: true
    # 用户名密码方式登录,配置的验证码类型:code/sms/slide/jigsaw,2023-03-14
    login-captcha-user-pass: code
    # 手机验证码方式登录,配置的验证码类型:code/sms/slide/jigsaw,2023-03-14
    login-captcha-sms-code: sms
    # 是否允许后台用户(非App)登录手机端?2023-03-20
    allow-pc-user-access-app: true
    # 是否支持手机登录时(不存在手机号)直接注册?目前电商系统支持! 2023-06-26
    allow-mobile-login-reg: true
    # PC端token失效分钟,默认:120分钟,2023-03-28
    token-expire-web: 120
    # 移动端token失效分钟,2023-03-28
    token-expire-mobile: 120
    # 用户体系(用户名)都是手机号,2023-06-28
    # 一般系统PC端通常都不是手机号作为用户名,只有在互联网相关系统中会存在手机号作为登录账号
    user-name-is-phone: false
    # 2023-07-11 登录策略配置,列表中存在的策略都表示启用(不启用的删掉)
    login-strategy-list:
    #      - com.iplatform.base.support.strategy.WebOnceLoginStrategy ##同一账号只能登录一个ip
    #      - com.iplatform.base.support.strategy.MobileOnceLoginStrategy
    # 用户相关安全配置,2023-08-03
    user:
      # 用户修改密码等级,分四级:1-2-3-4,常量:PasswordUtils#
      pass-level: 1
      # 是否强制用户修改默认密码?
      pass-default-modify: false
  # 代码生成,2022-11-26
  gen:
    # 作者
    author: Mike
    # 默认生成包路径 system 需改成自己的模块名称 如 system monitor tool
    package-name: com.iplatform.model.po
    # 自动去除表前缀,默认是false
    auto-remove-pre: false
    # 表前缀(生成类名不会包含表前缀,多个用逗号分隔)
    table-prefix: s_
  log:
    # 是否打开登录日志,2023-01-05
    # 如果不打开,则设备登录的uuid更新操作也无法获得,用于记录每个登录用户的uuid(用户登录角色更新),2023-03-23
    login-enabled: true
    # 是否打开操作日志,2023-01-05
    operate-enabled: false
  # 验证码相关配置,2023-01-27
  captcha:
    # 图形验证码实现类, TextCaptchaProvider | DefaultCaptchaProvider
    image-captcha-class: com.iplatform.base.captcha.TextCaptchaProvider
    # 短信验证码实现类(配置废弃,2023-04-25)
    sms-captcha-class: com.iplatform.base.captcha.MockSmsCaptchaProvider
  # 平台文件存储配置,2023-02-15
  file:
    # FileStoreType = FileSystem 本地磁盘存储根路径
    file-root: d:/tmp/
    # 调用远程上传文件,是否按照本地文件处理,2023-07-03
    # 在测试过程中如果没有FTP等服务,可以设置为true临时存储本地
    remote-as-local: false
    # oss实现类型:aws_s3(亚马逊) | tx(腾讯) | ali(阿里) | qn(七牛),2023-12-13
    oss-type: aws_s3
    # oss访问前缀,如:http://localhost:8082/admin/oss/,2023-12-13
    oss-prefix: http://localhost:8082/admin/oss/
    oss-access-key: ENC(BTHETNjs5ddRjYrdMF9ltmw8DMIEYTuyXIgDOeLIHy0=)
    oss-secret-key: ENC(jROCWGpiPULZrU1wBZAsXvQQovvqnhoJKWpmYD/3nbkNGDikxfEvCXW5jn3QsYnG)
    # oss服务地址
    oss-endpoint: https://eos-beijing-1.cmecloud.cn
    # oss默认的桶名称
    oss-bucket-name: ctoms-file
    protocol: https
    ftp:
      ip: 116.198.40.76
      port: 22
      user-name: mysftp
      password:
      private-key:
      # ftp服务上面的存储根路径,只能是linux路径
      #      file-root: /files/
      file-root: /train/
  # 接口文档生成,swagger3,2023-02-23
  swagger:
    enable: false
    # 包路径,暂时不用
    base-package: com.iplatform.base.controller
    title: 云原生微服务平台
    description: 一个快速web开发框架
  # TCP通信配置,2024-01-23
  tcp:
    # 是否开启引擎
    enabled: true
    # 是否打开心跳
    open-heart-beat: true
    # 心跳时间(秒),2023-08-28
    heart-beat-seconds: 60
    # 是否显示详细日志
    show-log: false
    # 请求队列类型:memory 基于内存
    queue-type: memory
    # tcp方式要扫描的请求对象的包空间
    scan-packages-tcp: com.walker.tcp,com.iplatform.tcp
    # websocket方式要扫描的请求对象的包空间
    scan-packages-ws: com.iplatform.tcp.util.ws
    # tcp长连接的端口
    port-tcp: 7878
    # websocket连接端口
    port-ws: 60001
    # websocket连接uri
    websocket-uri: wss://jmy.jinmingyuan.com/wss
    # 通信线程数量
    boss-thread-num: 2
    # 业务处理线程数量
    worker-thread-num: 4
    # 启用自定义连接管理器,如果true则业务需要提供自定义实现(配置),2023-07-18
    custom-connection-manager: false
    # 是否开启集群模式,连接管理器会默认加载负载支持,2023-09-26
    load-balance-enabled: false
    # 长连接主机(标记)信息,可以是任意字符串,只要集群中每个服务配置不同即可。
    # 在集群模式中,每个连接要加上主机标识,以便别人推送消息时,知道在哪个主机上推送。2023-09-19
    connection-host: topic_master
  # 推送模块配置,2023-04-25
  push:
    # 短信推送者名称:mock_sms_push(模拟短信),alidy_sms_push(阿里大鱼短信)
    sms-push-name: alidy_sms_push
    # 短信验证码模板ID,根据实际情况(每个平台可能不同)
    sms-template-code: SMS_285145505
    # 对于一般业务提醒(不包括:邮件、短信),推送的方式。推送者ID组合
    # && 表示并列,|| 表示或(只要一个成功就OK)
    # 2023-04-26,可用的 id = 'tcp', 'web_socket', 'wx', 'system'
    #    message-type: tcp && web_socket && wx && system
    # 如果是或者方式,需要把最可靠的放前面,因为系统会找到第一个发送成功即可。
    message-type: system || tcp || web_socket || wx
    # 邮件通知发送信息配置,2023-04-26
    mail-server: smtp.126.com
    mail-from: hnzzzhsl@126.com
    mail-password:
  # 聊天相关配置,2023-07-07
  #  chat:
  #    # 是否启用机器人答复,2023-07-12
  #    robot-enabled: true
  #    # 机器人具体实现对象,2023-07-12
  #    robot-class: com.iplatform.chat.support.TestRobot
  #    # 是否开启mongo存储,如果false则默认使用数据库表
  #    mongo-enabled: true
  #    mongo:
  #      ip: 116.198.40.76
  #      port: 27017
  #      # 聊天数据库名称,默认:chat
  #      database: czt_ops_test
  #      user-name: czt_ops_test
  #      password: czt_ops_test
  #      max-size: 10
  #      min-size: 2
  #      # 连接最大空闲时间,20分钟
  #      max-idle-time-seconds: 1200
  #      # 连接等待最大时间,10秒
  #      max-wait-time-seconds: 10
  # RestTemplate连接池配置,2023-08-18
  rest:
    # 连接保持活动时间,默认:600秒
    keep-alive-duration-seconds: 600
    # 空闲连接数量,默认:200个
    max-idle-connections: 200
    # 连接超时,默认:2秒
    connect-timeout-seconds: 2
    # 读超时,默认3秒
    read-timeout-seconds: 3
    # 写超时,默认3秒
    write-timeout-seconds: 3
  # 集群支持相关配置,2023-09-29
  lb:
    # 本机服务节点ID,每个主机一个唯一数字编号,从1开始
    server-id: 1
  # 2024-02-29 api监控相关
  api:
    # 2024-02-29 是否打开接口时间统计记录,如果:true 则会记录接口调用时间,写入表:s_api_time
    time-enabled: false
  # 网院统一认证配置选项,2023-10-23
  unified:
    # 是否开启测试模式,测试模式用于本地测试,无法连接网院认证接口
    test-mode: true
    # 网院提供的认证地址,关闭测试模式后,该选项生效
    remote-url: https://wangyuan.com/api/unified
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# RocketMQ队列配置,2023-09-26
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
rocketmq:
  # 服务地址,多个用逗号分开
  #  name-server: 116.198.40.76:9876
  name-server: 116.198.39.83:9898
  producer:
    # 发送消息超时时间,默认3000
    send-message-timeout: 3000
    # 生产者组
    group: producer_group
    # 发送消息失败重试次数,默认2
    retryTimesWhenSendFailed: 2
    # 异步消息重试此处,默认2
    retryTimesWhenSendAsyncFailed: 2
    # 消息最大长度,默认1024 * 1024 * 4(默认4M)
    # 1024 * 128 = 128K
    maxMessageSize: 131072
    # 压缩消息阈值,默认4k(1024 * 4)
    compressMessageBodyThreshold: 4096
    # 是否在内部发送失败时重试另一个broker,默认false
    retryNextServer: false
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# 集中培训业务器配置,2023-10-26
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
ctoms:
  # 调度器配置,2023-10-26
  scheduler:
    # 是否允许集群模式启用,
    # true:表示集群部署,则业务打包发布中不包含调度模块(ctoms-scheduler-server),调度模块需要独立打包;
    # false:表示单机部署,调度模块会被自动打包(作为一个类库)发布;
    cluster-enabled: false
    # 发布类型,
    # single:业务单应用打包,直接能运行调度任务
    # scheduler:调度任务包(独立打包支持集群)
    deploy-type: single
  # 消息推送请求地址
  push-url: "https://push.cpoc.cn/sendv2" #互联网
  link-app-url: "https://jmy.jinmingyuan.com/ctoms-h5/#/?" #移动跳转
  link-pc-url: "https://jmy.jinmingyuan.com/tcp/"
#  push-url: "http://10.5.6.99:9001/sendv2" #综合网
#  push-url: "http://push.cpoc.cn:9001/sendv2" #综合网
iplatform-base-tcp/src/main/resources/application.yml
New file
@@ -0,0 +1,7 @@
spring:
  profiles:
#    active: oracle
    active: dev
#    active: test
# 默认基地超级管理员角色key
baseRole: ROLE-BASE-ADMIN
iplatform-base-tcp/src/main/resources/wk_sn_lic.bin
New file
@@ -0,0 +1,2 @@
A’†/3öeº`DÇ^·»zž˜¼›    OÅZ¹ãyðÈ͒¼ˆ.4KUž[ÒǾïÈæÎ‘ãY$qñZ_h½º^+EΎ‚*’ ¿æÁ>nœu4GÑ-#
ö(¬ï|6ÐUqå¢gt0Æ    b÷ Ï\Ô:?Sœè}œže_ -ԐÕEÀÀЙ
iplatform-base-tcp/src/test/java/com/ctoms/AppTest.java
New file
@@ -0,0 +1,16 @@
package com.ctoms;
import com.ctoms.tcp.WebsocketClientTest;
/**
 * Unit test for simple App.
 */
public class AppTest
{
    public static void main( String[] args )
    {
        System.out.println( "Hello World!" );
        WebsocketClientTest test = new WebsocketClientTest();
        test.createBatch();
    }
}
iplatform-base-tcp/src/test/java/com/ctoms/tcp/DemoLoginResponse.java
New file
@@ -0,0 +1,51 @@
package com.ctoms.tcp;
public class DemoLoginResponse {
    /**
     *
     */
    private static final long serialVersionUID = 1167352207355638142L;
//    @Override
//    protected void translateProperties(JSONObject result) {
//        result.put("status", status);
//    }
    @Override
    public String toString(){
        return new StringBuilder("[protocol=").append(this.getProtocol())
                .append(", name=").append(this.getUid())
                .append(", status=").append(this.status)
                .append("]").toString();
    }
    public String getUid() {
        return uid;
    }
    public void setUid(String uid) {
        this.uid = uid;
    }
    private String protocol;
    public String getProtocol() {
        return protocol;
    }
    public void setProtocol(String protocol) {
        this.protocol = protocol;
    }
    public int getStatus() {
        return status;
    }
    public void setStatus(int status) {
        this.status = status;
    }
    private int status = 0;
    private String uid;
}
iplatform-base-tcp/src/test/java/com/ctoms/tcp/DemoWebsocketClient.java
New file
@@ -0,0 +1,81 @@
package com.ctoms.tcp;
import com.walker.infrastructure.utils.JsonUtils;
import org.java_websocket.client.WebSocketClient;
import org.java_websocket.handshake.ServerHandshake;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.net.URI;
import java.util.HashMap;
import java.util.Map;
public class DemoWebsocketClient extends WebSocketClient {
    protected final transient Logger logger = LoggerFactory.getLogger(this.getClass());
    public DemoWebsocketClient(URI serverUri) {
        super(serverUri);
    }
    @Override
    public void onOpen(ServerHandshake serverHandshake) {
//        logger.info("ws 连接成功");
        String data = null;
        Map<String, String> map = new HashMap<>(4);
        map.put("protocol", "login");
        map.put("uid", this.uid);
        try {
            data = JsonUtils.objectToJsonString(map);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        this.send(data);
        logger.info("客户端发送登录请求,name = {}", this.uid);
    }
    @Override
    public void onMessage(String s) {
        if(s.indexOf("heartbeat") >= 0){
            return;
        }
        logger.info("ws 收到消息:{}", s);
        try {
            DemoLoginResponse response = JsonUtils.jsonStringToObject(s, DemoLoginResponse.class);
            if(response.getProtocol().equals("login")){
                logger.info("websocket 登录成功:{}", response.getUid());
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
    @Override
    public void onClose(int i, String s, boolean b) {
        logger.warn("连接被关闭:code={}, reason={}, remote={}", i, s, b);
    }
    @Override
    public void onError(Exception e) {
        logger.error("连接错误:" + e.getMessage(), e);
    }
    public String getUid() {
        return uid;
    }
    public void setUid(String uid) {
        this.uid = uid;
    }
    public String getUserName() {
        return userName;
    }
    public void setUserName(String userName) {
        this.userName = userName;
    }
    private String uid;
    private String userName;
}
iplatform-base-tcp/src/test/java/com/ctoms/tcp/WebsocketClientTest.java
New file
@@ -0,0 +1,101 @@
package com.ctoms.tcp;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.net.URI;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
public class WebsocketClientTest {
    protected final transient Logger logger = LoggerFactory.getLogger(this.getClass());
//    private static final URI wsUri = URI.create("ws://127.0.0.1:60000/websocket");
//    private static final URI wsUri = URI.create("ws://116.198.40.76:60000/websocket");
//    private static final URI wsUri = URI.create("ws://10.8.4.35:60035/websocket");
//    private static final URI wsUri = URI.create("ws://www.shikeying.com:60001/websocket");
//    private static final URI wsUri = URI.create("ws://ctoms.chinapost.com.cn/ws/websocket");
//    private static final URI wsUri = URI.create("ws://localhost:60000");
//    private static final URI wsUri = URI.create("ws://116.198.40.76:60001");
    private static final URI wsUri = URI.create("wss://jmy.jinmingyuan.com/wss");
    private static Map<String, DemoWebsocketClient> cacheClient = new ConcurrentHashMap<>(32770);
    private ExecutorService executorService = Executors.newFixedThreadPool(4);
//    private static int currentSize = 20000;
    private static int currentSize = 1;
    private static final int TOTAL_LINES = 2;
//    private static final int TOTAL_LINES = 16;
//    private static final int BATCH_SIZE = 8;
    private static final int BATCH_SIZE = 2;
    public void createBatch(){
        logger.info(".........开始测试通信连接.......... TOTAL_LINES = {}, url = {}", TOTAL_LINES, wsUri);
        while(currentSize < TOTAL_LINES){
            try {
                this.executorService.execute(new Runnable() {
                    @Override
                    public void run() {
                        int count = BATCH_SIZE;
                        for(int i=0; i<count; i++){
                            String uri = "mike" + (currentSize);
                            DemoWebsocketClient client = createOneClient(wsUri, uri);
                            logger.info("创建了一个客户端: " + uri);
                            cacheClient.put(uri, client);
                            currentSize ++;
                        }
                    }
                });
            } catch (Exception ex){
                ex.printStackTrace();
            } finally {
                try {
                    logger.info("currentSize = {}", currentSize);
                    TimeUnit.SECONDS.sleep(3);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        }
        logger.info("已累计创建连接:{} 个", currentSize);
        try {
            TimeUnit.SECONDS.sleep(10);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
//        logger.info("准备关闭所有链接对象...");
//        for(DemoWebsocketClient client : cacheClient.values()){
//            client.close();
//        }
//        logger.info("执行了关闭操作,所有连接稍后会完全关闭");
    }
    private DemoWebsocketClient createOneClient(URI uri, String uid){
        DemoWebsocketClient client = new DemoWebsocketClient(uri);
        client.setUid(uid);
        client.setConnectionLostTimeout(0);
        try {
            client.connectBlocking();
        } catch (InterruptedException e) {
            throw new RuntimeException("创建client错误, uid=" + uid + ", " + e.getMessage(), e);
        }
        return client;
    }
//    public void createOneClient(){
//        DemoWebsocketClient client = new DemoWebsocketClient(wsUri);
//        client.setUid("mike");
//        try {
//            client.connectBlocking();
//        } catch (InterruptedException e) {
//            throw new RuntimeException(e);
//        }
//        WaitConsoleInput.waitInput();
//    }
}
iplatform-base-tcp/src/test/java/com/ctoms/tcp/WebsocketDemo.java
New file
@@ -0,0 +1,22 @@
package com.ctoms.tcp;
import com.iplatform.tcp.util.ws.WebDataResponse;
import com.walker.infrastructure.utils.JsonUtils;
import com.walker.infrastructure.utils.NumberGenerator;
import org.junit.Test;
public class WebsocketDemo {
    @Test
    public void sendDataFormat(){
        WebDataResponse msg = new WebDataResponse();
        msg.setMessageId(NumberGenerator.getLongSequenceId());
        msg.setName("shikeying");    // 指定用户发送,该id与浏览器端注册的id保持一致。
        msg.setData("你好,这是推送给浏览器的内容,一般是JSON格式。");
        try {
            System.out.println(JsonUtils.objectToJsonString(msg));
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}
iplatform-base/doc/iplatform_base.sql
New file
@@ -0,0 +1,35 @@
--
ALTER TABLE s_user_core
    ADD UNIQUE INDEX inx_usr_login (user_name) USING BTREE ;
ALTER TABLE s_role_user
    ADD INDEX inx_ru_uid (user_id) USING BTREE ;
ALTER TABLE s_config
    ADD INDEX inx_sc_key (config_key) USING BTREE ;
-- 用户核心表添加字段 2023-03-06
ALTER TABLE s_user_core
    ADD COLUMN wx_open_id  varchar(64) NULL AFTER remark;
ALTER TABLE s_user_core
    ADD COLUMN wx_union_id  varchar(64) NULL AFTER wx_open_id;
ALTER TABLE s_user_core
    ADD COLUMN ding_user_id  varchar(64) NULL AFTER wx_union_id;
ALTER TABLE s_user_core
    ADD COLUMN bind_client_id  varchar(64) NULL AFTER ding_user_id;
ALTER TABLE s_user_core
    ADD COLUMN bind_wechat  int NOT NULL DEFAULT 0 COMMENT '是否绑定微信:1_是, 0_否。' AFTER bind_client_id;
ALTER TABLE s_user_core
    ADD COLUMN modify_pwd  int NOT NULL DEFAULT 0 COMMENT '是否修改密码:0_否,1_是' AFTER bind_wechat;
ALTER TABLE s_user_core
    ADD COLUMN bind_mobile  int NOT NULL DEFAULT 0 COMMENT '是否绑定手机:1_是, 0_否。' AFTER modify_pwd;
ALTER TABLE s_user_core
    ADD COLUMN bind_mail  int NOT NULL DEFAULT 0 COMMENT '是否绑定邮箱:1_是, 0_否。' AFTER bind_mobile;
ALTER TABLE s_user_core
    ADD COLUMN profile_id  bigint NOT NULL DEFAULT 0 COMMENT '关联的档案id,0表示未关联' AFTER bind_mail;
-- 数据字典
ALTER TABLE s_dict_data
    ADD COLUMN parent_id  bigint NOT NULL DEFAULT 0 COMMENT '父记录id' AFTER remark;
--
iplatform-base/pom.xml
New file
@@ -0,0 +1,277 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>low-consum-manage</artifactId>
        <groupId>com.consum</groupId>
        <version>1.0.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.iplatform</groupId>
    <artifactId>iplatform-base</artifactId>
    <name>iplatform-base</name>
    <packaging>jar</packaging>
    <version>3.2.0</version>
    <properties>
    </properties>
    <dependencies>
        <!-- 平台实体接口对象, 2022/09/19 -->
        <dependency>
            <groupId>com.iplatform</groupId>
            <artifactId>iplatform-model-pojo</artifactId>
            <version>3.2.0</version>
        </dependency>
        <!-- 核心模块:提供非常基础服务(不连接数据库), 2022/09/19 -->
        <dependency>
            <groupId>com.iplatform</groupId>
            <artifactId>iplatform-core</artifactId>
            <version>3.2.0</version>
        </dependency>
        <dependency>
            <groupId>com.walkersoft</groupId>
            <artifactId>walker-infrastructure</artifactId>
            <version>${walker-infrastructure.version}</version>
            <systemPath>${project.basedir}/src/main/resources/lib/walker-infrastructure-3.2.0.jar</systemPath>
            <scope>system</scope>
        </dependency>
        <dependency>
            <groupId>com.walkersoft</groupId>
            <artifactId>walker-web</artifactId>
            <version>3.2.0</version>
            <systemPath>${project.basedir}/src/main/resources/lib/walker-web-3.2.0.jar</systemPath>
            <scope>system</scope>
        </dependency>
        <dependency>
            <groupId>com.walkersoft</groupId>
            <artifactId>walker-licence</artifactId>
            <version>3.2.0</version>
            <systemPath>${project.basedir}/src/main/resources/lib/walker-licence-3.2.0.jar</systemPath>
            <scope>system</scope>
        </dependency>
        <!-- 提供数据库支持, 2022/09/19 -->
        <dependency>
            <groupId>com.walkersoft</groupId>
            <artifactId>walker-jdbc</artifactId>
            <version>${walker-jdbc.version}</version>
            <systemPath>${project.basedir}/src/main/resources/lib/walker-jdbc-3.2.0.jar</systemPath>
            <scope>system</scope>
        </dependency>
        <!-- 提供Redis支持,由业务子工程决定是否依赖, 2022/09/20 -->
        <dependency>
            <groupId>com.walkersoft</groupId>
            <artifactId>walker-support-redis</artifactId>
            <version>3.2.0</version>
            <systemPath>${project.basedir}/src/main/resources/lib/walker-support-redis-3.2.0.jar</systemPath>
            <scope>system</scope>
            <exclusions>
                <exclusion>
                    <groupId>com.walkersoft</groupId>
                    <artifactId>walker-cache</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>com.walkersoft</groupId>
            <artifactId>walker-connector</artifactId>
            <version>${walker-connector.version}</version>
            <systemPath>${project.basedir}/src/main/resources/lib/walker-connector-3.2.0.jar</systemPath>
            <scope>system</scope>
        </dependency>
        <dependency>
            <groupId>com.walkersoft</groupId>
            <artifactId>walker-db-meta</artifactId>
            <version>${walker-db-meta.version}</version>
            <systemPath>${project.basedir}/src/main/resources/lib/walker-db-meta-3.2.0.jar</systemPath>
            <scope>system</scope>
        </dependency>
        <dependency>
            <groupId>com.walkersoft</groupId>
            <artifactId>walker-web-security</artifactId>
            <version>${walker-web-security.version}</version>
            <systemPath>${project.basedir}/src/main/resources/lib/walker-web-security-3.2.0.jar</systemPath>
            <scope>system</scope>
        </dependency>
        <!-- 基础模块提供web支持, 2022/10/31 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-starter</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>org.springframework</groupId>
                    <artifactId>spring-beans</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>org.springframework</groupId>
                    <artifactId>spring-core</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-starter-logging</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <!-- 简单的验证码实现(若依), 2022/11/06 -->
        <dependency>
            <groupId>com.walkersoft</groupId>
            <artifactId>walker-support-kaptcha</artifactId>
            <version>3.2.0</version>
            <systemPath>${project.basedir}/src/main/resources/lib/walker-support-kaptcha-3.2.0.jar</systemPath>
            <scope>system</scope>
        </dependency>
        <!--
        csv数据导入支持: univocity实现, 2023/02/01
        excel导入,目前这两种先放一起,2023/02/06
        -->
        <dependency>
            <groupId>com.walkersoft</groupId>
            <artifactId>walker-di-support-univocity</artifactId>
            <version>3.2.0</version>
            <systemPath>${project.basedir}/src/main/resources/lib/walker-di-support-univocity-3.2.0.jar</systemPath>
            <scope>system</scope>
        </dependency>
        <dependency>
            <groupId>com.walkersoft</groupId>
            <artifactId>walker-security</artifactId>
            <version>3.2.0</version>
            <systemPath>${project.basedir}/src/main/resources/lib/walker-security-3.2.0.jar</systemPath>
            <scope>system</scope>
        </dependency>
        <dependency>
            <groupId>com.walkersoft</groupId>
            <artifactId>walker-push-wx-public</artifactId>
            <version>3.2.0</version>
            <systemPath>${project.basedir}/src/main/resources/lib/walker-push-wx-public-3.2.0.jar</systemPath>
            <scope>system</scope>
        </dependency>
        <!-- 亚马逊S3 OSS 服务配置,2023-12-12 -->
        <dependency>
            <groupId>com.amazonaws</groupId>
            <artifactId>aws-java-sdk-s3</artifactId>
            <version>1.11.126</version>
            <scope>system</scope>
            <systemPath>${project.basedir}/src/main/resources/lib/aws-java-sdk-s3-1.11.126-with-dependencies.jar</systemPath>
        </dependency>
        <!-- 引入apache common-io, AbstractController 下载文件使用。2022-11-28 -->
        <dependency>
            <groupId>commons-io</groupId>
            <artifactId>commons-io</artifactId>
        </dependency>
        <!-- easyExcel底层依赖poi -->
        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi</artifactId>
            <version>${poi.version}</version>
            <exclusions>
                <exclusion>
                    <groupId>org.slf4j</groupId>
                    <artifactId>slf4j-api</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>org.projectlombok</groupId>
                    <artifactId>lombok</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>commons-codec</groupId>
                    <artifactId>commons-codec</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi-ooxml</artifactId>
            <version>${poi.version}</version>
        </dependency>
        <!-- jwt token: 2022/10/10 -->
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt-api</artifactId>
            <version>0.11.5</version>
        </dependency>
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt-impl</artifactId>
            <version>0.11.5</version>
        </dependency>
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt-jackson</artifactId>
            <version>0.11.5</version>
        </dependency>
        <!-- 获取浏览器信息, WebAgent依赖该对象用于解析Excel: 2023/01/05 -->
        <dependency>
            <groupId>com.univocity</groupId>
            <artifactId>univocity-parsers</artifactId>
        </dependency>
        <!-- RestTemplate使用: 2023/08/16 -->
        <dependency>
            <groupId>com.squareup.okhttp3</groupId>
            <artifactId>okhttp</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <scope>provided</scope>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>3.2.11</version>
                <configuration>
                    <!-- 2023-11-06 该参数指定了依赖的本地类库,不引入则报错:不能指向本地jar文件 -->
                    <arguments>
                        <argument>${project.basedir}/src/main/resources/lib</argument>
                    </arguments>
                    <!-- 2023-11-06 该参数指定,打包忽略 Main 函数。-->
                    <skip>true</skip>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>
iplatform-base/src/main/java/com/iplatform/base/AbstractController.java
New file
@@ -0,0 +1,360 @@
package com.iplatform.base;
import com.iplatform.base.callback.PlatformCallbackPostProcessor;
import com.iplatform.core.BeanContextAware;
import com.walker.db.page.ListPageContext;
import com.walker.db.page.PageSearch;
import com.walker.infrastructure.ApplicationRuntimeException;
import com.walker.infrastructure.arguments.ArgumentsManager;
import com.walker.infrastructure.arguments.Variable;
import com.walker.infrastructure.utils.DateUtils;
import com.walker.infrastructure.utils.FileUtils;
import com.walker.infrastructure.utils.StringUtils;
import com.walker.web.ResponseValue;
import com.walker.web.util.ServletUtils;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.util.List;
public abstract class AbstractController implements InitializingBean {
    protected final transient Logger logger = LoggerFactory.getLogger(this.getClass());
    private static String contextPath;
    private static final Object lock = new Object();
    /**
     * 返回第三方对接管理器对象。
     * @return
     * @date 2023-07-03
     */
    protected ThirdPartyManager getThirdPartyManager(){
        return BeanContextAware.getBeanByType(ThirdPartyManager.class);
    }
    /**
     * 返回服务端调用地址前缀,如:<br>
     * http://localhost:8080/demo
     * @return
     * @date 2023-01-06
     */
    protected String getServerDomain(){
        HttpServletRequest request = this.getRequest();
        return ServletUtils.getServerDomain(request);
    }
    /**
     * 返回给定的回调实现对象。
     * @param clazz 定义的回调接口,如: PlatformUserCallback.class
     * @return
     * @param <T>
     * @date 2023-01-05
     * @date 2023-01-28 该方法只能获取单个类型Callback,对于存在多种实现的请使用方法:
     * {@linkplain PlatformCallbackPostProcessor#getCallbackMultipleBean(Class)}
     */
    protected <T> T getPlatformCallback(Class<T> clazz){
        return PlatformCallbackPostProcessor.getCallbackObject(clazz);
    }
    /**
     * 返回系统配置可变参数对象。
     * @param key 参数 key
     * @return
     * @date 2022-11-29
     */
    protected Variable getArgumentVariable(String key){
        Variable v =  this.getArgumentManager().getVariable(key);
        if(v == null){
            throw new IllegalArgumentException("可变配置参数不存在: " + key);
        }
        return v;
    }
    protected ArgumentsManager getArgumentManager(){
        return BeanContextAware.getBeanByType(ArgumentsManager.class);
    }
    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    //~ 新添加的方法,2022-11-16
    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    /**
     * 下载简单的文件,通常文件不大。
     * @param data 文件字节码内容
     * @param fileName 文件名称,如: demo.zip
     * @throws IOException
     * @date 2022-11-28
     */
    protected void downloadSimpleFile(byte[] data, String fileName) throws IOException{
        HttpServletResponse response = this.getResponse();
        response.reset();
        response.addHeader("Access-Control-Allow-Origin", "*");
        response.addHeader("Access-Control-Expose-Headers", "Content-Disposition");
        response.setHeader("Content-Disposition", "attachment; filename=\"" + URLEncoder.encode(fileName) + "\"");
        response.addHeader("Content-Length", "" + data.length);
        response.setContentType("application/octet-stream; charset=UTF-8");
        IOUtils.write(data, response.getOutputStream());
    }
    /**
     * 从前端获得分页查询条件,写入线程参数中。
     * @author 时克英
     * @date 2022-11-16
     * @date 2023-01-28 该方法废弃,由拦截器根据特征'/list'自动执行分页准备调用。
     */
    @Deprecated
    protected PageSearch preparePageSearch(){
        PageSearch pageSearch = new PageSearch();
        Integer pn = this.getIntParameter(PageSearch.PAGE_NUM);
        if(pn != null){
            pageSearch.setPageIndex(pn.intValue());
        }
        Integer pageSize = this.getIntParameter(PageSearch.PAGE_SIZE);
        if(pageSize != null){
            pageSearch.setPageSize(pageSize.intValue());
        }
        pageSearch.setOrderByColumn(this.getParameter(PageSearch.ORDER_BY_COLUMN));
        pageSearch.setOrderAsc(this.getParameter(PageSearch.IS_ASC));
//        pageSearch.setReasonable(this.getParameter(PageSearch));
        ListPageContext.setPageSearch(pageSearch);
        return pageSearch;
    }
    /**
     * 把一个日期或时间字符串,转换成数值。
     * <pre>
     *     1.2022-11-16 --> 20221116000000
     *     2.2022-11-16 12:01:30 --> 20221116120130
     * </pre>
     * @param dateTime 给定时间或日期字符串
     * @param isTime 是否时间,否表示只有日期
     * @return
     */
    protected long getParamsDateTime(String dateTime, boolean isTime){
        if(StringUtils.isEmpty(dateTime)){
            return -1;
        }
        if(isTime){
            return DateUtils.getDateTimeNumber(dateTime);
        } else {
            return DateUtils.toLongDateTime(dateTime);
        }
    }
    /**
     * 包装返回带分页的表格集合,加上total属性(适配前端)
     * @param data
     * @param total
     * @return
     * @param <T>
     * @date 2022-11-19
     * @date 2023-05-16 从2.3.0之后废弃,因为使用了新的完整前端界面,返回的业务数据是一个整体,不再随意增加:ResponseValue属性。
     */
    @Deprecated
    protected <T> ResponseValue<List<?>> acquireTablePage(List<T> data, long total){
        ResponseValue<List<?>> rv = ResponseValue.success(data);
        rv.setTotal(total);
        return rv;
    }
    //~~~~~~~~~~~~~~~~~~~~~~~~~~ end ~~~~~~~~~~~~~~~~~~~~~~
    protected HttpServletRequest getRequest(){
//        ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
//        HttpServletRequest request = servletRequestAttributes.getRequest();
//        return request;
        return ServletUtils.getRequest();
    }
    protected HttpServletResponse getResponse(){
//        return ((ServletRequestAttributes)RequestContextHolder.getRequestAttributes()).getResponse();
        return ServletUtils.getResponse();
    }
    /**
     * 得到WEB应用上下文路径,如:/web
     * @return
     */
    protected String getContextPath(){
        if(contextPath == null){
            synchronized (lock) {
                HttpServletRequest request = getRequest();
                if(request == null) {
                    throw new Error("request not found: getRequest().");
                }
                contextPath = request.getContextPath();
            }
        }
        return contextPath;
    }
    protected String getParameter(String name){
        return getRequest().getParameter(name);
    }
    protected String getParameterUTF8(String name) throws UnsupportedEncodingException {
        String value = this.getParameter(name);
        return value == null ? null : URLDecoder.decode(value, "UTF-8");
    }
    protected String[] getParameterValues(String name) {
        return this.getRequest().getParameterValues(name);
    }
    protected Integer getIntParameter(String name) {
        String value = this.getParameter(name);
        return value == null ? null : Integer.valueOf(value);
    }
    protected Long getLongParameter(String name) {
        String value = this.getParameter(name);
        return value == null ? null : Long.valueOf(value);
    }
    protected Float getFloatParameter(String name) {
        String value = this.getParameter(name);
        return value == null ? null : Float.valueOf(value);
    }
    protected Double getDoubleParameter(String name) {
        String value = this.getParameter(name);
        return value == null ? null : Double.valueOf(value);
    }
    /**
     * @param name
     * @return Attribute value
     */
    protected Object getAttribute(String name) {
        return this.getRequest().getAttribute(name);
    }
    /**
     * @param name
     * @param value
     */
    protected void setAttribute(String name, Object value) {
        this.getRequest().setAttribute(name, value);
    }
    protected void setDefaultContentType() {
        this.getResponse().setContentType("text/html; charset=utf-8");
    }
    private void ajaxOutPut(ResponseFormat format, Object outputString) throws IOException {
        HttpServletResponse response = this.getResponse();
        response.setCharacterEncoding(StringUtils.DEFAULT_CHARSET_UTF8);
        response.setContentType(format.getValue());
        this.print(response, outputString == null ? StringUtils.EMPTY_STRING : outputString.toString());
    }
    protected void print(HttpServletResponse response, String str) throws IOException {
//        ServletOutputStream outputStream = this.getResponse().getOutputStream();
        PrintWriter pw = response.getWriter();
        pw.print(str);
    }
    protected void ajaxOutPutText(Object outputString) throws IOException {
        this.ajaxOutPut(ResponseFormat.TextPlain, outputString);
    }
    protected void ajaxOutPutJson(Object outputString) throws IOException {
        this.ajaxOutPut(ResponseFormat.ApplicationJson, outputString);
    }
    protected void ajaxOutPutXml(Object outputString) throws IOException {
        this.ajaxOutPut(ResponseFormat.TextXml, outputString);
    }
    protected void ajaxOutPutHtml(Object outputString) throws IOException {
        setDefaultContentType();
        this.print(this.getResponse(), outputString == null ? StringUtils.EMPTY_STRING : outputString.toString());
    }
    protected void ajaxOutputFileStream(String contentType, String filePath){
        ajaxOutputFileStream(contentType, new File(filePath));
    }
    protected void ajaxOutputFileStream(String contentType, File file){
        HttpServletResponse response = this.getResponse();
        response.setCharacterEncoding(StringUtils.DEFAULT_CHARSET_UTF8);
        response.setContentType(contentType);
        byte[] content = FileUtils.getFileBytes(file);
        if(content == null) {
            return;
        }
        OutputStream out = null;
        try {
            out = response.getOutputStream();
            out.write(content);
            out.flush();
        } catch (IOException e) {
            throw new ApplicationRuntimeException("输出服务器文件流出现异常!", e);
        } finally {
            if(out != null){
                try {
                    out.close();
                } catch (IOException e) {}
            }
        }
    }
    public enum ResponseFormat{
        ApplicationJson{
        },
        ApplicationXml{
        },
        TextXml{
        },
        TextPlain{
        };
//        ImagePng{
//            public String getValue(){
//                return Constants.IMAGE_PNG;
//            }
//        },
//        ImageJpeg{
//            public String getValue(){
//                return Constants.IMAGE_JPEG;
//            }
//        },
//        ImageGif{
//            public String getValue(){
//                return Constants.IMAGE_GIF;
//            }
//        };
        public String getValue(){
            throw new AbstractMethodError();
        }
    }
    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    //~ 为老的freemarker界面预留的方法,2022-11-08
    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    /**
     * 前端页面调用的JS方面名称
     */
    protected static final String DEFAULT_JS_NAME = "reload";
    /**
     * 前端使用的分页对象引用名称,默认值
     */
    protected static final String DEFAULT_PAGER_VIEW_NAME = "pagerView";
}
iplatform-base/src/main/java/com/iplatform/base/AbstractFileOperateSpiController.java
New file
@@ -0,0 +1,339 @@
package com.iplatform.base;
import com.iplatform.core.BeanContextAware;
import com.walker.file.FileInfo;
import com.walker.file.FileStoreType;
import com.walker.infrastructure.utils.FileCopyUtils;
import com.walker.infrastructure.utils.FileUtils;
import com.walker.infrastructure.utils.StringUtils;
import org.springframework.web.multipart.MultipartFile;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import java.util.Set;
public abstract class AbstractFileOperateSpiController extends AbstractSecurityController{
    protected FileOperateSpi acquireFileOperateSpi(){
        return BeanContextAware.getBeanByType(FileOperateSpi.class);
    }
    /**
     * 清除给定文件地址的CDN前缀。如:https://qnyun.com/oss/12345678
     * <pre>
     *     去掉前缀后,只剩下"12345678"
     * </pre>
     * @param path 给定的文件资源地址
     * @return
     * @date 2023-05-17
     */
    protected String clearCdnPrefix(String path){
        return this.acquireFileOperateSpi().clearCdnPrefix(path);
    }
    /**
     * 返回存储文件的前缀,根据平台配置参数:本地、FTP或OSS远程存储不同而返回不同前缀。
     * @return
     * @date 2023-06-10
     */
    protected String getCdnUrl(){
        return this.acquireFileOperateSpi().getCdnUrl();
    }
    /**
     * 根据文件信息,加载文件二进制内容。
     * @param fileInfo
     * @return
     * @date 2023-03-17
     */
    protected byte[] getLocalFileData(FileInfo fileInfo){
        String localRoot = this.acquireFileOperateSpi().getFileRootConfig();
        String absolutePath = localRoot + fileInfo.getUrl();
        try {
            return FileCopyUtils.copyToByteArray(new File(absolutePath));
        } catch (IOException e) {
            throw new RuntimeException("文件加载异常,可能不存在:" + absolutePath, e);
        }
    }
    /**
     * 根据文件id编号,获取文件基本信息。
     * @param id
     * @return
     * @date 2023-03-17
     */
    protected FileInfo getFileInfo(String id){
        if(StringUtils.isEmpty(id)){
            logger.warn("id为空,无法获得文件信息");
            return null;
        }
        return this.acquireFileOperateSpi().getFileInfo(Long.parseLong(id));
    }
    protected List<FileInfo> getFileInfoList(List<String> ids){
        if(StringUtils.isEmptyList(ids)){
            throw new IllegalArgumentException("ids is required!");
        }
        return this.acquireFileOperateSpi().getFileInfoList(ids);
    }
    /**
     * 下载oss文件
     * @param id 文件唯一编号
     * @return
     * @date 2023-12-13
     */
    protected byte[] getOssFileData(String id){
        return this.acquireFileOperateSpi().downloadOssFile(id);
    }
    protected FileInfo uploadFileToLocal(InputStream inputStream
            , String fileName, String groupId, long fileSize, Integer businessType, String owner) throws Exception{
        return this.acquireFileOperateSpi().uploadFileToLocal(inputStream, fileName, groupId, fileSize, businessType, owner);
    }
    protected FileInfo[] uploadFileToLocal(InputStream[] inputStream
            , String[] fileName, String groupId, long[] fileSize, Integer businessType, String owner) throws Exception{
        return this.acquireFileOperateSpi().uploadFileToLocal(inputStream, fileName, groupId, fileSize, businessType, owner);
    }
    /**
     * 上传文件到本地,业务控制器直接调用该方法。
     * @param multipartFile
     * @param groupId 分组ID,可选
     * @param businessType 业务类型,可选,请参考电商模块(PID)
     * @param owner 数据归属:平台为-1,其他为顶级机构ID(商户ID)
     * @param fileContentType 前端传入的文件上传类型:uploadf 表示文件,其他为图片
     * @return
     * @throws Exception 上传失败抛出异常
     * @date 2023-06-09
     */
    protected FileInfo uploadFileToLocal(MultipartFile multipartFile
            , String groupId, Integer businessType, String owner, String fileContentType) throws Exception{
        this.checkMultipartFile(multipartFile);
        long fileSize = multipartFile.getSize();
        String fileName = multipartFile.getOriginalFilename().toLowerCase();
        if(fileName.length() > 99){
            fileName = fileName.substring(fileName.length()-99);
        }
        String fileExt = this.checkFile(fileName, fileSize, fileContentType);
        logger.debug("上传本地文件扩展名:" + fileExt);
        return this.uploadFileToLocal(multipartFile.getInputStream(), fileName, groupId, fileSize, businessType, owner);
    }
    /**
     * 上传多个文件
     * @param multipartFile
     * @param groupId
     * @param businessType
     * @param owner
     * @param fileContentType
     * @return
     * @throws Exception
     * @date 2023-08-01
     */
    protected FileInfo[] uploadFileToLocal(MultipartFile[] multipartFile
            , String groupId, Integer businessType, String owner, String fileContentType) throws Exception{
        if(multipartFile == null || multipartFile.length == 0){
            throw new IllegalArgumentException("multipartFile数组为空");
        }
        int size = multipartFile.length;
        InputStream[] inputStream = new InputStream[size];
        String[] fileName = new String[size];
        long[] fileSize = new long[size];
        for(int i=0; i<size; i++){
            inputStream[i] = multipartFile[i].getInputStream();
            fileName[i] = multipartFile[i].getOriginalFilename().toLowerCase();
            fileSize[i] = multipartFile[i].getSize();
        }
        return this.uploadFileToLocal(inputStream, fileName, groupId, fileSize, businessType, owner);
    }
    protected FileInfo uploadFileToRemote(InputStream inputStream
            , String fileName, String groupId, long fileSize, Integer businessType, String owner) throws Exception{
//        String remoteFileStoreType = this.getArgumentVariable(ArgumentsConstants.CONFIG_UPLOAD_TYPE).getStringValue();
//        if(StringUtils.isEmpty(remoteFileStoreType)){
//            throw new PlatformRuntimeException("平台未配置任何远程存储类别:" + ArgumentsConstants.CONFIG_UPLOAD_TYPE);
//        }
        // 2023-07-03,如果配置远程上传为本地
        if(this.acquireFileOperateSpi().isRemoteAsLocal()){
            return this.acquireFileOperateSpi().uploadFileToLocal(inputStream, fileName, groupId, fileSize, businessType, owner);
        }
//        FileStoreType fileStoreType = FileStoreType.getType(remoteFileStoreType);
        FileStoreType fileStoreType = FileStoreType.OssAws;
        if(fileStoreType == FileStoreType.Ftp){
            return this.acquireFileOperateSpi().uploadFileToFtp(inputStream, fileName, groupId, fileSize, businessType, owner);
        } else {
            return this.acquireFileOperateSpi().uploadFileToOss(inputStream, fileName, groupId, fileSize, businessType, owner, fileStoreType);
        }
    }
    protected FileInfo[] uploadFileToRemote(InputStream[] inputStream
            , String[] fileName, String groupId, long[] fileSize, Integer businessType, String owner) throws Exception{
//        String remoteFileStoreType = this.getArgumentVariable(ArgumentsConstants.CONFIG_UPLOAD_TYPE).getStringValue();
//        if(StringUtils.isEmpty(remoteFileStoreType)){
//            throw new PlatformRuntimeException("平台未配置任何远程存储类别:" + ArgumentsConstants.CONFIG_UPLOAD_TYPE);
//        }
        // 2023-07-03,如果配置远程上传为本地
        if(this.acquireFileOperateSpi().isRemoteAsLocal()){
            return this.acquireFileOperateSpi().uploadFileToLocal(inputStream, fileName, groupId, fileSize, businessType, owner);
        }
//        FileStoreType fileStoreType = FileStoreType.getType(remoteFileStoreType);
        FileStoreType fileStoreType = FileStoreType.OssAws;
        if(fileStoreType == FileStoreType.Ftp){
            return this.acquireFileOperateSpi().uploadFileToFtp(inputStream, fileName, groupId, fileSize, businessType, owner);
        } else {
            return this.acquireFileOperateSpi().uploadFileToOss(inputStream, fileName, groupId, fileSize, businessType, owner, fileStoreType);
        }
    }
    protected FileInfo uploadFileToRemote(MultipartFile multipartFile
            , String groupId, Integer businessType, String owner, String fileContentType) throws Exception{
        this.checkMultipartFile(multipartFile);
        long fileSize = multipartFile.getSize();
        String fileName = multipartFile.getOriginalFilename().toLowerCase();
        String fileExt = this.checkFile(fileName, fileSize, fileContentType);
        logger.debug("上传远程文件扩展名:" + fileExt);
        return this.uploadFileToRemote(multipartFile.getInputStream(), fileName, groupId, fileSize, businessType, owner);
    }
    protected FileInfo[] uploadFileToRemote(MultipartFile[] multipartFile
            , String groupId, Integer businessType, String owner, String fileContentType) throws Exception{
        int size = multipartFile.length;
        InputStream[] inputStream = new InputStream[size];
        String[] fileName = new String[size];
        long[] fileSize = new long[size];
        for(int i=0; i<size; i++){
            inputStream[i] = multipartFile[i].getInputStream();
            fileName[i] = multipartFile[i].getOriginalFilename().toLowerCase();
            fileSize[i] = multipartFile[i].getSize();
        }
        return this.uploadFileToRemote(inputStream, fileName, groupId, fileSize, businessType, owner);
    }
    private void checkMultipartFile(MultipartFile multipartFile){
        if(multipartFile == null){
            throw new PlatformRuntimeException("上传文件不存在");
        }
        long fileSize = multipartFile.getSize();
        if(fileSize <= 0){
            throw new PlatformRuntimeException("上传文件大小错误:" + fileSize);
        }
    }
    private String checkFile(String fileName, long fileSize, String fileContentType){
        String fileExt = FileUtils.getFileExt(fileName);
        if(StringUtils.isEmpty(fileExt)){
            logger.error("错误文件信息:{}" ,fileName);
            throw new PlatformRuntimeException("不存在文件扩展名,无法上传");
        }
        // 平台配置可支持的文件后缀类型
        String extArrayConfig = null;
        if(fileContentType.equals(Constants.UPLOAD_AFTER_FILE_KEYWORD)){
            extArrayConfig = this.getArgumentVariable(ArgumentsConstants.UPLOAD_FILE_EXT_STR_CONFIG_KEY).getStringValue();
        } else {
            extArrayConfig = this.getArgumentVariable(ArgumentsConstants.UPLOAD_IMAGE_EXT_STR_CONFIG_KEY).getStringValue();
        }
        // 如果存在配置后缀限制,就要检查匹配
        if(StringUtils.isNotEmpty(extArrayConfig)){
            Set<String> extensionList = StringUtils.commaDelimitedListToSet(extArrayConfig);
            if(extensionList == null || extensionList.size() == 0){
                throw new PlatformRuntimeException("上传文件类型,只能是:" + extArrayConfig);
            }
            if(!extensionList.contains(fileExt)){
                throw new PlatformRuntimeException("上传文件类型,只能是:" + extArrayConfig);
            }
        }
        // 文件大小限制
        long fileSizeConfig = 0;
        if(fileContentType.equals(Constants.UPLOAD_AFTER_FILE_KEYWORD)){
            fileSizeConfig = this.getArgumentVariable(ArgumentsConstants.UPLOAD_FILE_MAX_SIZE_CONFIG_KEY).getLongValue();
        } else {
            fileSizeConfig = this.getArgumentVariable(ArgumentsConstants.UPLOAD_IMAGE_MAX_SIZE_CONFIG_KEY).getLongValue();
        }
        // 配置的大小单位:M,这里换成字节
        fileSizeConfig = fileSizeConfig * 1024 * 1024;
        if(fileSize > fileSizeConfig){
            throw new PlatformRuntimeException("上传文件大小:" + fileSize + ",超出限制:" + fileSizeConfig);
        }
        return fileExt;
    }
    /**
     * 上传文件到服务器本地磁盘。
     * @param inputStream 文件流,系统使用完会自动关闭
     * @param fileName 文件名,如: demo.txt
     * @param groupId 业务ID,可选
     * @return
     * @throws Exception
     * @date 2023-03-16
     */
    @Deprecated
    protected FileInfo uploadFileToLocal(InputStream inputStream, String fileName, String groupId, long fileSize) throws Exception{
        return this.acquireFileOperateSpi().uploadFileToSystem(inputStream, fileName, groupId, fileSize);
    }
    /**
     * 上传文件到远程服务。
     * @param inputStream 文件流,系统使用完会自动关闭
     * @param fileName 文件名,如: demo.txt
     * @param groupId 业务ID,可选
     * @return
     * @throws Exception
     * @date 2023-02-15
     */
    @Deprecated
    protected FileInfo uploadFileToRemote(InputStream inputStream, String fileName, String groupId, long fileSize) throws Exception{
        if(inputStream == null){
            throw new IllegalArgumentException("上传ftp文件流为空!");
        }
        return this.acquireFileOperateSpi().uploadFileToFtp(inputStream, fileName, groupId,fileSize);
    }
    protected FileInfo uploadFileToRemote(String absoluteFilePath, Integer businessType, String owner) throws Exception{
        File uploadFile = this.checkAbsoluteFilePath(absoluteFilePath);
        BufferedInputStream inputStream = new BufferedInputStream(new FileInputStream(uploadFile));
        return this.uploadFileToRemote(inputStream, uploadFile.getName(), null, uploadFile.getTotalSpace(), businessType, owner);
    }
    /**
     * 上传文件到远程服务目录中。
     * @param absoluteFilePath
     * @return 返回上传文件基本信息
     * @throws Exception
     * @date 2023-02-15
     */
    @Deprecated
    protected FileInfo uploadFileToRemote(String absoluteFilePath) throws Exception{
//        if(StringUtils.isEmpty(absoluteFilePath)){
//            throw new IllegalArgumentException("上传文件全路径不存在!");
//        }
//        File uploadFile = new File(absoluteFilePath);
//        if(!uploadFile.exists()){
//            logger.warn("上传文件不存在,file=" + absoluteFilePath);
//            return null;
//        }
        File uploadFile = this.checkAbsoluteFilePath(absoluteFilePath);
        BufferedInputStream inputStream = new BufferedInputStream(new FileInputStream(uploadFile));
        return this.acquireFileOperateSpi().uploadFileToFtp(inputStream, FileUtils.getFileNameWithoutPath(absoluteFilePath), null, uploadFile.getTotalSpace());
    }
    private File checkAbsoluteFilePath(String absoluteFilePath) throws Exception{
        if(StringUtils.isEmpty(absoluteFilePath)){
            throw new IllegalArgumentException("上传文件全路径不存在!");
        }
        File uploadFile = new File(absoluteFilePath.toLowerCase());
        if(!uploadFile.exists()){
            throw new IllegalArgumentException("上传文件不存在, file=" + absoluteFilePath);
        }
        return uploadFile;
    }
}
iplatform-base/src/main/java/com/iplatform/base/AbstractSecurityController.java
New file
@@ -0,0 +1,129 @@
package com.iplatform.base;
import com.iplatform.base.config.SecurityUserProperties;
import com.iplatform.core.BeanContextAware;
import com.iplatform.model.po.S_user_core;
import com.walker.infrastructure.utils.PasswordUtils;
import com.walker.web.UserPrincipal;
import com.walker.web.UserType;
import java.util.List;
/**
 * 获得当前登录用户认证信息。<p></p>
 * <pre>
 * 1.如果你的控制器必须在用户认证之后才能操作,就必须继承该对象,请参考: {@linkplain AbstractSecurityController}
 *
 * 2.如果你的控制器没有权限控制,则只需要集成<code>AbstractController</code>即可,
 * 请参考: {@linkplain AbstractController}
 * </pre>
 * @author 时克英
 * @date 2022-11-11
 */
public abstract class AbstractSecurityController extends AbstractController{
    /**
     * 验证密码是否符合平台政策。
     //     * @param encryptPassword 前端修改的密码(密文),RSA加密,后台要解密的
     * @param originPassword 原始明文密码
     * @return 返回错误提示,返回空表示成功
     * @date 2023-08-05
     */
    protected String validatePasswordRule(String originPassword){
//        String originPassword = PlatformRSAUtils.getRsaDecryptValue(encryptPassword, PlatformRSAUtils.PRIK);
        String error = PasswordUtils.filterText(originPassword);
        if(error != null){
            return error;
        }
        int passLevelConfig = this.acquireSecurityUserProperties().getPassLevel();
        if(!PasswordUtils.validateComplex(originPassword, passLevelConfig)){
            return "密码级别过低,请输入:大小写字母、数字以及至少一种特殊符号";
        }
        return null;
    }
    protected SecurityUserProperties acquireSecurityUserProperties(){
        return BeanContextAware.getBeanByType(SecurityUserProperties.class);
    }
    /**
     * 以流程角色登录获取权限,activiti7专用,后续会废弃。
     * @date 2023-03-21
     */
    @Deprecated
    protected void loginAsWorkflowRole(){
        this.acquireSecuritySpi().loginAsWorkflowRole();
    }
    /**
     * 判断当前登录用户是否超级管理员。
     * @return
     * @date 2022-11-27
     */
    protected boolean isSupervisor(){
        S_user_core user_core = this.getCurrentUser();
        if(user_core == null){
            throw new IllegalStateException("获取当前用户错误,未找到");
        }
        return user_core.getUser_type() == UserType.TYPE_SUPER;
    }
    /**
     * 返回当前用户具有的角色集合,注意:这里面会有权限使用的特定角色,如: <p> ROLE_SUPERVISOR, ROLE_USER, ROLE_ADMIN 等。</p>
     * <pre>
     *     其他的是系统角色管理的ID,因此数据库通过角色集合查询菜单并不影响结果。
     * </pre>
     * @return
     * @date 2022-11-27
     */
    public List<String> getCurrentUserRoleIdList(){
        return this.acquireSecuritySpi().getCurrentUserRoleIdList();
    }
    public UserPrincipal<S_user_core> getCurrentUserPrincipal(){
        return this.acquireSecuritySpi().getCurrentUserPrincipal();
    }
    /**
     * 返回用户指定菜单的权限标识(是否存在),如果不存在返回空。
     * @param menuId 对应功能菜单ID
     * @return
     * @date 2022-12-21
     */
    protected String getCurrentDataScope(String menuId){
        UserPrincipal<S_user_core> userPrincipal = this.getCurrentUserPrincipal();
        return userPrincipal.getDataScope(menuId);
    }
    public S_user_core getCurrentUser(){
        return this.acquireSecuritySpi().getCurrentUser();
    }
    public long getCurrentUserId(){
        return this.acquireSecuritySpi().getCurrentUserId();
    }
    /**
     * 返回加密后的密文
     * @param password 明文密码
     * @return
     * @date 2022-12-13
     */
    public String encryptPassword(String password){
        return this.acquireSecuritySpi().encryptPassword(password);
    }
    /**
     * 比较提供的明文密码是否与加密的密码相同。
     * @param rawPassword 明文原始密码
     * @param encodedPassword 加密后的密码
     * @return
     */
    public boolean matchesPassword(String rawPassword, String encodedPassword){
        return this.acquireSecuritySpi().matchesPassword(rawPassword, encodedPassword);
    }
    private SecuritySpi acquireSecuritySpi(){
        return BeanContextAware.getBeanByType(SecuritySpi.class);
    }
}
iplatform-base/src/main/java/com/iplatform/base/ArgumentsConstants.java
New file
@@ -0,0 +1,103 @@
package com.iplatform.base;
/**
 * 可变参数常量定义,变量的KEY常量对应数据库中的: config_key 字段。
 * @author 时克英
 * @date 2022-11-30
 */
public class ArgumentsConstants {
    public static final String KEY_SECURITY_CAPTCHA_ENABLED = "security.captcha.enabled";
    /**
     * 用户初始密码参数,明文密码。
     * @date 2022-12-13
     */
    public static final String KEY_SECURITY_PASSWORD_INIT = "security.password.init";
    /**
     * 安全:是否开启用户注册
     * @date 2023-07-28
     */
    public static final String KEY_SECURITY_ACCOUNT_REGISTER = "security.account.register";
    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    //~ 平台配置参数,以Tab分组方式展示的界面。2023-05-12
    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    /** 移动端域名 */
    public static final String CONFIG_KEY_SITE_URL = "site_url";
    /** 图片上传类型 1本地 2七牛云 3OSS 4COS, 默认本地
     * <p>支持系统自己的定义常量,参考:{@linkplain com.walker.file.FileStoreType}</p>
     * */
    public static final String CONFIG_UPLOAD_TYPE = "uploadType";
    /** 全局本地图片域名 */
    public static final String CONFIG_LOCAL_UPLOAD_URL = "localUploadUrl";
    /** 七牛云上传URL */
    public static final String CONFIG_QN_UPLOAD_URL = "qnUploadUrl";
    /** 阿里云上传URL */
    public static final String CONFIG_AL_UPLOAD_URL = "alUploadUrl";
    /** 腾讯云上传URL */
    public static final String CONFIG_TX_UPLOAD_URL = "txUploadUrl";
    /** 亚马逊S3上传URL */
    public static final String CONFIG_S3_UPLOAD_URL = "s3UploadUrl";
    /** FTP上传URL */
    public static final String CONFIG_FTP_UPLOAD_URL = "ftpUploadUrl";
    /** 版权-公司信息 */
    public static final String CONFIG_COPYRIGHT_COMPANY_INFO = "copyright_company_name";
    /** 版权-公司图片 */
    public static final String CONFIG_COPYRIGHT_COMPANY_IMAGE = "copyright_company_image";
    /** 图片上传,拓展名 */
    public static final String UPLOAD_IMAGE_EXT_STR_CONFIG_KEY = "image_ext_str";
    /** 图片上传,最大尺寸 */
    public static final String UPLOAD_IMAGE_MAX_SIZE_CONFIG_KEY = "image_max_size";
    /** 文件上传,拓展名 */
    public static final String UPLOAD_FILE_EXT_STR_CONFIG_KEY = "file_ext_str";
    /** 文件上传,最大尺寸 */
    public static final String UPLOAD_FILE_MAX_SIZE_CONFIG_KEY = "file_max_size";
    /** 移动端顶部logo */
    public static final String CONFIG_KEY_MOBILE_TOP_LOGO = "mobile_top_logo";
    /** 移动端登录 logo */
    public static final String CONFIG_KEY_MOBILE_LOGIN_LOGO = "mobile_login_logo";
    /** 商品分类页配置 */
    public static final String CONFIG_CATEGORY_CONFIG = "category_page_config";
    /** 是否隐藏一级分类 */
    public static final String CONFIG_IS_SHOW_CATEGORY = "is_show_category";
    /** 首页商品列表模板配置 */
    public static final String CONFIG_IS_PRODUCT_LIST_STYLE = "homePageSaleListStyle";
    /** 底部导航—是否自定义 */
    public static final String CONFIG_BOTTOM_NAVIGATION_IS_CUSTOM = "bottom_navigation_is_custom";
    /** 客服类型 */
    public static final String CONFIG_CONSUMER_TYPE = "consumer_type";
    /** 客户类型-H5 */
    public static final String CONSUMER_TYPE_H5 = "h5";
    /** 客户类型-热线 */
    public static final String CONSUMER_TYPE_HOTLINE = "hotline";
    /** 客服H5链接 */
    public static final String CONFIG_CONSUMER_H5_URL = "consumer_h5_url";
    /** 客服电话 */
    public static final String CONFIG_CONSUMER_HOTLINE = "consumer_hotline";
    /** 店铺街开关 */
    public static final String CONFIG_KEY_SHOP_STREET_SWITCH = "shop_street_switch";
    /** 客服电话 */
    public static final String CONFIG_CONSUMER_MESSAGE = "consumer_message";
    /** 客服邮箱 */
    public static final String CONFIG_CONSUMER_EMAIL = "consumer_email";
    /** 自定义表单开关:关闭 */
    public static final String CONFIG_FORM_SWITCH_CLOSE = "'0'";
    /** 自定义表单开关:开启 */
    public static final String CONFIG_FORM_SWITCH_OPEN = "'1'";
    /** 公共开关:0关闭 */
    public static final String COMMON_SWITCH_CLOSE = "0";
    /** 公共开关:1开启 */
    public static final String COMMON_SWITCH_OPEN = "1";
}
iplatform-base/src/main/java/com/iplatform/base/AsyncManager.java
New file
@@ -0,0 +1,67 @@
package com.iplatform.base;
import com.iplatform.core.BeanContextAware;
import com.iplatform.core.util.ThreadUtils;
import java.util.TimerTask;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
/**
 * 异步任务管理器
 *
 * @author Mike
 * @date 2023-01-05
 */
public class AsyncManager
{
    /**
     * 操作延迟10毫秒
     */
    private final int OPERATE_DELAY_TIME = 10;
    /**
     * 异步操作任务调度线程池
     */
    private ScheduledExecutorService executor = (ScheduledExecutorService)BeanContextAware.getBeanByName("scheduledExecutorService");
    /**
     * 单例模式
     */
    private AsyncManager(){}
    private static AsyncManager me = new AsyncManager();
    public static AsyncManager me()
    {
        return me;
    }
    /**
     * 执行任务
     *
     * @param task 任务
     */
    public void execute(TimerTask task)
    {
        executor.schedule(task, OPERATE_DELAY_TIME, TimeUnit.MILLISECONDS);
    }
    /**
     * 执行一个异步任务
     * @param task 任务定义
     * @param millSeconds 延时执行(毫秒)
     * @date 2023-08-17
     */
    public void execute(TimerTask task, long millSeconds){
        executor.schedule(task, millSeconds, TimeUnit.MILLISECONDS);
    }
    /**
     * 停止任务线程池
     */
    public void shutdown()
    {
        ThreadUtils.shutdownAndAwaitTermination(executor);
    }
}
iplatform-base/src/main/java/com/iplatform/base/CategoryCacheProvider.java
New file
@@ -0,0 +1,34 @@
package com.iplatform.base;
import com.iplatform.model.po.S_category;
import com.iplatform.model.vo.CategoryTreeVo;
import java.util.List;
/**
 * 分类缓存对象。
 * @author 时克英
 * @date 2023-05-17
 */
public interface CategoryCacheProvider {
    List<CategoryTreeVo> getTree(Integer type, Integer status, String name, List<Integer> categoryIdList, int owner);
    /**
     * 返回分类树结构(目前只有两级)
     * @param type 归属类别:1 产品分类,2 附件分类,3 文章分类, 4 设置分类, 5 菜单分类, 6 配置分类, 7 秒杀配置
     * @param status 状态:0正常,1失效
     * @param name 名称(目前未知)
     * @param owner 归属(平台或商户)
     * @return
     */
    List<CategoryTreeVo> getListTree(Integer type, Integer status, String name, int owner);
    S_category get(int id);
    void save(S_category category);
    void update(S_category category);
    void remove(int id);
}
iplatform-base/src/main/java/com/iplatform/base/Constants.java
New file
@@ -0,0 +1,214 @@
package com.iplatform.base;
public class Constants {
    /**
     * 超级管理员默认的登录名称。
     * @author 时克英
     * @date 2022-10-31
     */
    public static final String SUPERVISOR_NAME_DEFAULT = "supervisor";
    /**
     * 超级管理员ID = 0
     */
    public static final long SUPERVISOR_ID = 0L;
    /**
     * 超级管理员姓名
     */
    public static final String SUPERVISOR_NAME_ZH = "主宰账号";
    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    /**
     * 用户缓存token前缀。
     * @date 2022-11-01
     */
    public static final String USER_ONLINE_PREFIX = "login_token:";
    /**
     * 验证码 redis key
     * @date 2022-11-07
     */
    public static final String CAPTCHA_CODE_PREFIX = "captcha_codes:";
    /**
     * 当组织机构表没有记录时,默认显示机构名称。
     * @date 2022-12-01
     */
    public static final String DEFAULT_ORG_NAME = "默认单位";
    /**
     * 数据权限前缀。
     * @date 2022-12-21
     */
    public static final String DATA_SCOPE_NAME = "data_scope,";
    /**
     * 前端过来的提交参数,如果必须提供则需要检查,返回非法提示。
     * @date 2023-06-01
     */
    public static final String ERROR_ARGUMENT = "参数错误";
    /**
     * id查询校验不存在,返回非法提示。
     * @date 2023-06-01
     */
    public static final String ERROR_NOT_EXIST = "信息不存在,请刷新后重试";
    /**
     * 校验班级结班,返回非法提示。
     * @date 2023-06-01
     */
    public static final String ERROR_CLASS_IS_END = "班级已结班,无法操作";
    /**
     * 平台附件上传,映射的浏览路径。
     * @date 2023-06-09
     */
    public static final String FILE_CONTEXT_PATH_MAPPING = "/file/**";
    /**
     * 平台附件上传,本地文件包含的路径,用于从路径中判断是否URL地址。
     * @date 2023-06-10
     */
    public static final String FILE_CONTEXT_PATH = "/file/";
    /**
     * 设置错误日志,内容最大长度,默认:255
     * @date 2023-08-13
     */
    public static final int LOG_ERROR_MAX_SIZE = 255;
    /** 数据状态:正常 */
    public static final int STATUS_NORMAL = 0;
    /** 数据状态:禁用 */
    public static final int STATUS_DISABLE = 1;
    /** 数据状态:删除 */
    public static final int STATUS_DELETE = 2;
    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    //~ 缓存名称常量,2023-01-04
    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    public static final String CACHE_NAME_MENU = "cache.base.menu";
    public static final String CACHE_NAME_CAPTCHA = "cache.base.captcha";
    public static final String CACHE_NAME_DEPT = "cache.base.dept";
    public static final String CACHE_NAME_HOST = "cache.system.host";
    public static final String CACHE_NAME_USER = "cache.base.user";
    public static final String CACHE_NAME_ONLINE_USER = "cache.base.user_online";
    public static final String CACHE_NAME_ARGUMENTS = "cache.base.arguments";
    public static final String CACHE_NAME_DICT = "cache.base.dict";
    public static final String CACHE_NAME_PUSH = "cache.base.push";
    public static final String CACHE_NAME_CATEGORY = "cache.base.category";
    public static final String CACHE_NAME_FORM = "cache.base.form";
    public static final String CACHE_NAME_GROUP = "cache.base.group";
    public static final String CACHE_NAME_USER_LOGIN = "cache.base.user_login";
    public static final String CACHE_NAME_WECHAT = "cache.base.wechat";
    public static final String CACHE_NAME_NOTIFICATION_TEMPLATE = "cache.notify.template";
    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    //~~ TCP 模块核心常量,2023-04-17
    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    public static final String QUEUE_TYPE_MEMORY = "memory";
    /**
     * 推送范围定义常量:data 普通单个,broadcast 广播。
     * @date 2023-04-21
     */
    public static final String PUSH_SCOPE_DATA = "data";
    public static final String PUSH_SCOPE_BROAD_CAST = "broadcast";
    /**
     * 模拟短信推送者ID
     * @date 2023-04-25
     */
    public static final String PUSH_SMS_ID_MOCK = "mock_sms_push";
    /**
     * 推送平台提醒时,from字段默认值
     * @date 2023-04-25
     */
    public static final String PUSH_FROM_DEFAULT = "platform";
    public static final String PUSH_OPTION_TYPE_DEFAULT = "no_type";
    /**
     * 消息通知,存放临时缓存前缀
     * @date 2023-04-27
     */
    public static final String PUSH_CACHE_PREFIX = "notification:";
    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    //~~ 导入导出相关,2023-05-08
    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    /**
     * 生成导入模板文件前缀。
     * @date 2023-05-08
     */
    public static final String TEMPLATE_IMPORT_PREFIX = "template_";
    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    //~~ 电商基础,2023-05-15
    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    /**
     * 数据归属:平台,> 0 表示商户ID
     */
    public static final int OWNER_PLATFORM = -1;
    /**
     * 系统基础分类的type类型。<p></p>
     * 类型,1 产品分类,2 附件分类,3 文章分类, 4 设置分类, 5 菜单分类, 6 配置分类, 7 秒杀配置
     * @date 2023-05-18
     */
    public static final int CATEGORY_TYPE_CONFIG = 6;
    public static final int CATEGORY_TYPE_ATTACH = 2;
    /** 文件导出下载拦截关键字 */
    public static final String DOWNLOAD_FILE_KEYWORD = "downloadf";
    /** 文件前端上传后下载关键字 */
    public static final String UPLOAD_AFTER_FILE_KEYWORD = "uploadf";
    public static final String UPLOAD_AFTER_IMAGE_KEYWORD = "uploadi";
    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    //~~ 系统分组配置项,2023-06-23
    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    /** 首页banner滚动图 */
    public static final Integer GROUP_DATA_ID_INDEX_BANNER = 1;
    /** 首页金刚区 */
    public static final Integer GROUP_DATA_ID_INDEX_MENU = 2;
    /** 热门搜索 */
    public static final Integer GROUP_DATA_ID_INDEX_KEYWORDS = 3;
    /** 移动端_充值套餐设置 */
    public static final Integer GROUP_DATA_ID_RECHARGE_PACKAGE = 4;
    /** 个人中心菜单 */
    public static final Integer GROUP_DATA_ID_USER_CENTER_MENU = 5;
    /** 个人中心轮播图 */
    public static final Integer GROUP_DATA_ID_USER_CENTER_BANNER = 7;
    /** 分销推广海报Banner */
    public static final Integer GROUP_DATA_ID_SPREAD_BANNER_LIST = 10;
    /** 移动端_订单状态_图 */
    public static final Integer GROUP_DATA_ID_ORDER_STATUS_IMAGE = 13;
    /** 移动端_底部导航 */
    public static final Integer GROUP_DATA_ID_BOTTOM_NAVIGATION = 14;
    /**
     * 分组配置数据中,图片关键词名字。
     * @date 2023-06-24
     */
    public static final String GROUP_DATA_IMAGE = "pic";
    /** 公共开关:0关闭 */
    public static final String COMMON_SWITCH_CLOSE = "0";
    /** 公共开关:1开启 */
    public static final String COMMON_SWITCH_OPEN = "1";
    /** 自定义表单开关:关闭 */
    public static final String CONFIG_FORM_SWITCH_CLOSE = "'0'";
    /** 自定义表单开关:开启 */
    public static final String CONFIG_FORM_SWITCH_OPEN = "'1'";
}
iplatform-base/src/main/java/com/iplatform/base/DefaultUserPrincipal.java
New file
@@ -0,0 +1,148 @@
package com.iplatform.base;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.iplatform.model.po.S_user_core;
import com.walker.web.DataStatus;
import com.walker.web.LoginType;
import com.walker.web.UserType;
import com.walker.web.principal.AbstractUserPrincipal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
 * 平台默认的用户凭证对象,主要用于登录认证、权限拦截判断使用。
 * 业务模块不会使用该对象。
 * @author 时克英
 * @date 2022-10-31
 */
public class DefaultUserPrincipal extends AbstractUserPrincipal<S_user_core> {
    private long lastLoginTime = 0;
    private LoginType lastLoginType = LoginType.UserPassword;
    private List<String> roleIdList = new ArrayList<>(4);
    // 2022-12-21 用户菜单对应的数据权限
    // key = menuId, value = dept_org(如:本单位)
    private Map<String, String> dataScopeMap = new HashMap<>();
    public DefaultUserPrincipal(){}
    public DefaultUserPrincipal(S_user_core user_core){
        if(user_core == null){
            throw new IllegalArgumentException("user info is required!");
        }
        this.setUserInfo(user_core);
        this.setId(String.valueOf(user_core.getId()));
        this.setUserName(user_core.getUser_name());
        this.setPassword(user_core.getPassword());
    }
    @JsonIgnore
    @Override
    public boolean isEnabled() {
        return this.getUserInfo().getStatus() == DataStatus.CONST_NORMAL;
    }
    @JsonIgnore
    @Override
    public boolean isTokenExpired(String token) {
        // 这里暂未实现,集成后腰修改
        return false;
    }
    @JsonIgnore
    @Override
    public boolean isAccountLocked() {
        // 这里暂未实现,集成后腰修改
        return false;
    }
    @JsonIgnore
    @Override
    public boolean validateMd5Password(String encryption) {
        return false;
    }
    @Override
    public long getLastLoginTime() {
        return this.lastLoginTime;
    }
    @Override
    public LoginType getLastLoginType() {
        return this.lastLoginType;
    }
    public void setLastLoginTime(long loginTime){
        this.lastLoginTime = loginTime;
    }
    public void setLastLoginType(LoginType loginType){
        this.lastLoginType = loginType;
    }
    /**
     * 返回该用户拥有的角色ID集合。
     * @return
     */
    public List<String> getRoleIdList() {
        return roleIdList;
    }
    public void setRoleIdList(List<String> roleIdList) {
        this.roleIdList = roleIdList;
    }
    @JsonIgnore
    @Override
    public boolean isSupervisor() {
        return this.getUserInfo().getUser_type().intValue() == UserType.TYPE_SUPER;
    }
    /**
     * 添加一条数据权限
     * @param menuId 菜单ID,对应一个功能
     * @param dataScopeValue 数据权限标识
     * @date 2022-12-21
     */
    @JsonIgnore
    public void addDataScope(String menuId, String dataScopeValue){
        this.dataScopeMap.put(menuId, dataScopeValue);
    }
    public void setDataScopeMap(Map<String, String> dataScopeMap){
        this.dataScopeMap = dataScopeMap;
    }
    /**
     * 根据菜单ID,查询是否具有特定的数据权限。
     * @param menuId
     * @return
     * @date 2022-12-21
     */
    @JsonIgnore
    @Override
    public String getDataScope(String menuId){
        return this.dataScopeMap.get(menuId);
    }
    public Map<String, String> getDataScopeMap(){
        return this.dataScopeMap;
    }
    /**
     * 该方法测试redis序列化使用,暂时不用。
     * @param user_core
     * @date 2022-11-15
     */
    @Deprecated
    public void setup(S_user_core user_core){
        this.setUserInfo(user_core);
        this.setId(String.valueOf(user_core.getId()));
        this.setUserName(user_core.getUser_name());
        this.setPassword(user_core.getPassword());
    }
}
iplatform-base/src/main/java/com/iplatform/base/DeptCacheProvider.java
New file
@@ -0,0 +1,47 @@
package com.iplatform.base;
import com.iplatform.model.po.S_dept;
import java.util.List;
/**
 * 组织机构缓存提供者使用规范。<p></p>
 * 因为存在多种实现,如: Redis 或者 内存,因此需要抽象该接口。
 * @author 时克英
 * @date 2022-12-03
 */
public interface DeptCacheProvider {
    /**
     * 设置是否允许缓存机构的子机构关系,<br>
     * 在机构数量非常庞大的时候,即便 Redis 也不太适合存储该关系,因为通常界面会展示较多数据,<br>
     * 此时可能退化成数据库查询,或者通过其他 NoSQL 数据库实现。
     * @param allow
     * @date 2022-12-03
     */
    void setAllowCacheChildren(boolean allow);
    S_dept getDept(long deptId);
    void updateDept(S_dept s_dept);
    void removeDept(long deptId);
    void putDept(S_dept s_dept);
    /**
     * 返回给定机构,下一级子机构ID集合。
     * @param deptId 给定当前机构ID
     * @return
     * @date 2022-12-03
     */
    List<String> getChildrenDeptIdOneLevel(long deptId);
    /**
     * 返回给定机构,下一级子机构对象集合。
     * @param deptId 给定当前机构ID
     * @return
     * @date 2022-12-03
     */
    List<S_dept> getChildrenDeptOneLevel(long deptId);
}
iplatform-base/src/main/java/com/iplatform/base/FileOperateSpi.java
New file
@@ -0,0 +1,82 @@
package com.iplatform.base;
import com.walker.file.FileInfo;
import com.walker.file.FileStoreType;
import java.io.InputStream;
import java.util.List;
/**
 * 文件操作提供者定义,由第三方实现文件管理功能,包括:上传、下载、获取文件信息等。
 * @author 时克英
 * @date 2023-02-15
 */
public interface FileOperateSpi {
    byte[] downloadOssFile(String id);
    /**
     * 清除给定文件地址的CDN前缀。如:https://qnyun.com/oss/12345678
     * <pre>
     *     去掉前缀后,只剩下"12345678"
     * </pre>
     * @param path 给定的文件资源地址
     * @return
     * @date 2023-05-17
     */
    String clearCdnPrefix(String path);
    /**
     * 获取上传文件的CDN地址,根据使用的不同第三方服务从配置中查找。
     * @return
     * @date 2023-05-17
     */
    String getCdnUrl();
    FileInfo uploadFileToLocal(InputStream inputStream
            , String fileName, String groupId, long fileSize, Integer businessType, String owner) throws Exception;
    FileInfo[] uploadFileToLocal(InputStream[] inputStream
            , String[] fileName, String groupId, long[] fileSize, Integer businessType, String owner) throws Exception;
    FileInfo uploadFileToFtp(InputStream inputStream
            , String fileName, String groupId, long fileSize, Integer businessType, String owner) throws Exception;
    FileInfo[] uploadFileToFtp(InputStream[] inputStream
            , String[] fileName, String groupId, long[] fileSize, Integer businessType, String owner) throws Exception;
    FileInfo uploadFileToOss(InputStream inputStream
            , String fileName, String groupId, long fileSize, Integer businessType, String owner, FileStoreType ossType) throws Exception;
    FileInfo[] uploadFileToOss(InputStream[] inputStream
            , String[] fileName, String groupId, long[] fileSize, Integer businessType, String owner, FileStoreType ossType) throws Exception;
    @Deprecated
    FileInfo uploadFileToSystem(InputStream inputStream, String fileName, String groupId, long fileSize) throws Exception;
    @Deprecated
    FileInfo uploadFileToFtp(InputStream inputStream, String fileName, String groupId, long fileSize) throws Exception;
    FileInfo getFileInfo(long id);
    List<FileInfo> getFileInfoList(List<String> ids);
    /**
     * 返回本地文件系统存储根目录,如: d:/tmp/
     * @return
     * @date 2023-02-15
     */
    String getFileRootConfig();
    /**
     * 调用远程上传文件,是否按照本地方式处理。
     * <pre>
     *     1)该选项为了适应没有FTP等服务的情况,特别是在开发过程,如果设置为:true 则调用远程上传方法后文件仍然保存在本地。
     *     2)如果生产环境存在合适的文件存储服务,则需要设置为:false
     * </pre>
     * @return
     * @date 2023-07-03
     */
    boolean isRemoteAsLocal();
    FileStoreType getOssFileStoreType();
    String getOssPrefix();
}
iplatform-base/src/main/java/com/iplatform/base/LocalDatabaseMetaEngine.java
New file
@@ -0,0 +1,100 @@
package com.iplatform.base;
import com.walker.connector.LocalAddress;
import com.walker.db.DatabaseType;
import com.walker.db.TableInfo;
import com.walker.db.page.GenericPager;
import com.walker.dbmeta.DatabaseMetaEngine;
import com.walker.dbmeta.FieldInfo;
import com.walker.dbmeta.support.DamengMetaEngine;
import com.walker.dbmeta.support.MySQLMetaEngine;
import com.walker.dbmeta.support.OracleMetaEngine;
import com.walker.dbmeta.support.PostgresMetaEngine;
import com.walker.dbmeta.support.SqlserverMetaEngine;
import com.walker.security.SystemLogMan;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
 * 定义本地数据库元数据引擎对象。<p></p>
 * 用于查询数据库中表和字段记录。
 * @author 时克英
 * @date 2022-11-22
 */
public class LocalDatabaseMetaEngine {
    private DatabaseMetaEngine engine;
    private LocalAddress localAddress;
    public void setLocalAddress(LocalAddress localAddress) {
        if(localAddress == null){
            throw new IllegalArgumentException("LocalAddress 必须提供");
        }
        this.localAddress = localAddress;
        SystemLogMan.getInstance().checkMan();
    }
    public void setDatabaseType(DatabaseType databaseType){
        if(databaseType == DatabaseType.DERBY){
            throw new UnsupportedOperationException("不支持该数据库:" + databaseType.name());
        } else if(databaseType == DatabaseType.MYSQL){
            engine = new MySQLMetaEngine();
        } else if(databaseType == DatabaseType.ORACLE){
            engine = new OracleMetaEngine();
        } else if(databaseType == DatabaseType.POSTGRES){
            engine = new PostgresMetaEngine();
        } else if(databaseType == DatabaseType.SQLSERVER){
            engine = new SqlserverMetaEngine();
        } else if(databaseType == DatabaseType.DAMENG){
            // 2023-03-03
            engine = new DamengMetaEngine();
        } else {
            throw new IllegalArgumentException("unsupported database type: " + databaseType.name());
        }
        System.out.println("创建:" + engine.getClass().getName());
    }
    public List<FieldInfo> getFieldsObject(String tableName){
        return engine.getFieldsObject(this.localAddress, tableName);
    }
    public List<String> getTableNamesByLike(String tableNameLike){
        return engine.getTableNamesByLike(this.localAddress, tableNameLike);
    }
    /**
     * 分页返回表信息集合。当前页信息通过对象: ListPageContext.getCurrentPageIndex()方法获得。
     * @param tableNameLike 表名模糊查询,如果为空字符串则查询所有。
     * @return
     * @date 2022-11-22
     */
    public GenericPager<TableInfo> queryPageTableNamesByLike(String tableNameLike
//            , int pageIndex, int pageSize
    ){
        return this.engine.queryPageTableNamesByLike(this.localAddress, tableNameLike);
    }
    /**
     * 返回指定表元数据信息。
     * @param tableName 表名称
     * @return
     * @date 2022-11-26
     */
    public TableInfo queryOneTableInfo(String tableName){
        List<String> tableList = new ArrayList<>(2);
        tableList.add(tableName);
        Map<String, TableInfo> map = this.engine.getTableRows(this.localAddress, tableList);
        if(map == null || map.size() == 0){
            return null;
        }
        return map.get(tableName);
    }
}
iplatform-base/src/main/java/com/iplatform/base/NotificationTemplateCache.java
New file
@@ -0,0 +1,19 @@
package com.iplatform.base;
import com.iplatform.model.vo.NotificationTemplateVo;
/**
 * 通知提醒,模板配置缓存定义。
 * @author 时克英
 * @date 2023-08-25
 */
public interface NotificationTemplateCache {
    NotificationTemplateVo get(String mark);
    void save(NotificationTemplateVo category);
    void update(NotificationTemplateVo category);
    void remove(String mark);
}
iplatform-base/src/main/java/com/iplatform/base/NotifyConstants.java
New file
@@ -0,0 +1,22 @@
package com.iplatform.base;
/**
 * 移植老程序代码,后续逐渐废弃使用。
 * @date 2023-08-25
 */
public class NotifyConstants {
    /** 消息开关状态-不存在 */
    public static int SWITCH_NOT_EXIST = 0;
    /** 消息开关状态-开启 */
    public static int SWITCH_OPEN = 1;
    /** 消息开关状态-关闭 */
    public static int SWITCH_COLSE = 2;
    /** 详情类型——公众号模板消息 */
    public static String DETAIL_TYPE_WECHAT = "wechat";
    /** 详情类型——小程序模板消息 */
    public static String DETAIL_TYPE_ROUTINE = "routine";
    /** 详情类型——短信 */
    public static String DETAIL_TYPE_SMS = "sms";
}
iplatform-base/src/main/java/com/iplatform/base/PlatformAdapterController.java
New file
@@ -0,0 +1,189 @@
package com.iplatform.base;
import com.iplatform.base.pojo.form.FormData;
import com.iplatform.base.pojo.form.FormDataItem;
import com.iplatform.base.service.GroupServiceImpl;
import com.iplatform.core.BeanContextAware;
import com.iplatform.model.po.S_group_data;
import com.iplatform.model.vo.SystemGroupVo;
import com.walker.infrastructure.utils.JsonUtils;
import com.walker.infrastructure.utils.StringUtils;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
/**
 * 新界面,重构平台定义的基础控制器。
 * @author 时克英
 * @date 2023-05-15
 */
public abstract class PlatformAdapterController extends SystemController{
    protected SystemGroupCache getSystemGroupCache(){
        return BeanContextAware.getBeanByType(SystemGroupCache.class);
    }
    private GroupServiceImpl getGroupService(){
        return BeanContextAware.getBeanByType(GroupServiceImpl.class);
    }
    /**
     * 根据分组项目ID,返回转换后的对象。
     * @param groupDataId
     * @param clazz
     * @param cdnUrl
     * @return
     * @param <T>
     * @date 2023-09-11
     */
    protected <T> T acquireGroupDataNormal(int groupDataId, Class<T> clazz, String cdnUrl){
        S_group_data groupData = this.getGroupService().queryGroupData(groupDataId);
        if(groupData == null || groupData.getStatus().intValue() == 0){
            return null;
        }
        try {
            FormData formData = JsonUtils.jsonStringToObject(groupData.getValue(), FormData.class);
            if(StringUtils.isEmptyList(formData.getFields())){
                return null;
            }
            HashMap<String, Object> map = new HashMap<>();
            for(FormDataItem item : formData.getFields()){
                if(item.getName().equals(Constants.GROUP_DATA_IMAGE)){
                    map.put(item.getName(), cdnUrl + item.getValue());
                } else {
                    map.put(item.getName(), item.getValue());
                }
            }
            map.put("id", groupData.getId());
            String json = JsonUtils.objectToJsonString(map);
            return JsonUtils.jsonStringToObject(json, clazz);
        } catch (Exception ex){
            throw new PlatformRuntimeException("S_group_data.value转FormData错误:" + ex.getMessage(), ex);
        }
    }
    /**
     * 返回分组包含的动态添加项列表集合。
     * <pre>
     *     1) 目前,在充值功能中使用,显示充值套餐列表。
     * </pre>
     * @param groupId 分组ID
     * @param status 是否可用
     * @param clazz 转换结果对象
     * @param cdnUrl 图片前缀
     * @return
     * @param <T>
     * @date 2023-09-11
     */
    protected <T> List<T> acquireGroupDataList(int groupId, boolean status, Class<T> clazz, String cdnUrl){
        SystemGroupVo vo = this.getSystemGroupCache().get(groupId);
        if(vo == null){
            throw new IllegalStateException("缓存中未找到分组记录,groupId = " + groupId);
        }
        List<S_group_data> groupDataList = vo.getGroupDataList();
        if(StringUtils.isEmptyList(groupDataList)){
            return null;
        }
        List<T> arrayList = new ArrayList<>();
        try{
            FormData formData = null;
            for(S_group_data data : groupDataList){
                formData = JsonUtils.jsonStringToObject(data.getValue(), FormData.class);
                if(StringUtils.isEmptyList(formData.getFields())){
                    continue;
                }
                if(status && data.getStatus().intValue() == 0){
                    continue;
                }
                HashMap<String, Object> map = new HashMap<>();
                T t;
                for(FormDataItem item : formData.getFields()){
                    if(item.getName().equals(Constants.GROUP_DATA_IMAGE)){
                        map.put(item.getName(), cdnUrl + item.getValue());
                    } else {
                        map.put(item.getName(), item.getValue());
                    }
                }
                map.put("id", data.getId());
                String json = JsonUtils.objectToJsonString(map);
                t = JsonUtils.jsonStringToObject(json, clazz);
                arrayList.add(t);
            }
            return arrayList;
        } catch (Exception ex){
            throw new PlatformRuntimeException("S_group_data.value转FormData错误:" + ex.getMessage(), ex);
        }
    }
    /**
     * 根据分组ID,返回分组包含的数据项配置集合信息。例如:
     * <pre>
     *     1)配置的APP首页底部导航。
     * </pre>
     * @param groupId 分组ID
     * @return
     * @date 2023-06-23
     */
    protected List<HashMap<String, Object>> acquireGroupDataConfigList(int groupId, String cdnUrl){
        SystemGroupVo vo = this.getSystemGroupCache().get(groupId);
        if(vo == null){
            throw new IllegalStateException("缓存中未找到分组记录,groupId = " + groupId);
        }
        List<S_group_data> groupDataList = vo.getGroupDataList();
        if(StringUtils.isEmptyList(groupDataList)){
            return new ArrayList<>(1);
        }
        List<HashMap<String, Object>> arrayList = new ArrayList<>();
        try {
            FormData formData = null;
            for (S_group_data data : groupDataList) {
                formData = JsonUtils.jsonStringToObject(data.getValue(), FormData.class);
                if(StringUtils.isEmptyList(formData.getFields())){
                    continue;
                }
                HashMap<String, Object> map = new HashMap<>();
                for(FormDataItem item : formData.getFields()){
                    if(item.getName().equals(Constants.GROUP_DATA_IMAGE)){
                        map.put(item.getName(), cdnUrl + item.getValue());
                    } else {
                        map.put(item.getName(), item.getValue());
                    }
                }
                map.put("id", data.getId());
                arrayList.add(map);
            }
            return arrayList;
        } catch (Exception ex){
            throw new PlatformRuntimeException("S_group_data.value转FormData错误:" + ex.getMessage(), ex);
        }
    }
    /**
     * 返回用户归属值,如果是平台为'-1',如果为租户(商户)则为定义的商户ID。
     * <pre>
     *     注意要点:
     *     1)因为系统框架中用户是长整形(Long),但商户中是(int)因此在实际添加商户数据时,需要按照序列来计算,这样只需要整形即可。
     * </pre>
     * @return
     * @date 2023-05-15
     */
//    protected int getOwner(){
//        S_user_core user = this.getCurrentUser();
//        int userType = user.getUser_type();
//        if(userType == UserType.TYPE_SUPER_MERCHANT || userType == UserType.TYPE_MERCHANT_ADMIN){
//            return user.getMer_id().intValue();
////            throw new UnsupportedOperationException("对于其他租户(商户)归属应为:mer_id");
//        }
//        return Constants.OWNER_PLATFORM;
//    }
}
iplatform-base/src/main/java/com/iplatform/base/PlatformLoginCallback.java
New file
@@ -0,0 +1,61 @@
package com.iplatform.base;
import com.iplatform.model.po.S_user_core;
import com.walker.infrastructure.ApplicationCallback;
import com.walker.web.CaptchaProvider;
import com.walker.web.CaptchaResult;
import com.walker.web.TokenException;
import com.walker.web.UserPrincipal;
/**
 * 平台登录回调接口定义,便于支持多种用户(或设备)使用相同方式登录系统。
 * @author 时克英
 * @date 2023-01-26
 */
public interface PlatformLoginCallback extends ApplicationCallback {
    /**
     * 是否检测验证码,该方法意思是:在提交登录时,是否要验证。
     * <pre>
     *     1)例如:在PC端会存在验证码。
     *     2)在APP端,通过短信登录时,不会有验证码(只会在生成短信验证码时才有验证操作)
     * </pre>
     * @return
     */
    boolean isValidateCaptcha();
    /**
     * 根据用户登录ID,返回用户登录基本信息。<p></p>
     *
     * @param loginId
     * @return object[0] = S_user_core user, object[1] = List<String> roleIdList
     */
    Object[] queryLoginUser(String loginId);
    /**
     * 校验密码回调方法。
     * @param userPrincipal
     * @return
     */
    boolean validatePassword(UserPrincipal<S_user_core> userPrincipal);
    /**
     * 注销回调方法,当用户退出时触发调用。
     * @param token 客户端提交的token
     */
    void onLogout(String token) throws TokenException;
    /**
     * 返回登录回调配置的'验证码提供者'
     * @return
     * @date 2023-03-14
     */
    CaptchaProvider<CaptchaResult> getCaptchaProvider();
    /**
     * 设置登录回调配置的'验证码提供者',每个登录回调对象都需要配置一个。
     * @param captchaProvider
     * @date 2023-03-14
     */
    void setCaptchaProvider(CaptchaProvider<CaptchaResult> captchaProvider);
}
iplatform-base/src/main/java/com/iplatform/base/PlatformRuntimeException.java
New file
@@ -0,0 +1,19 @@
package com.iplatform.base;
import com.walker.web.WebRuntimeException;
/**
 * 平台最基础的运行异常定义。
 * @author 时克英
 * @date 2023-05-20
 */
public class PlatformRuntimeException extends WebRuntimeException {
    public PlatformRuntimeException(String msg) {
        super(msg);
    }
    public PlatformRuntimeException(String msg, Throwable caution){
        super(msg, caution);
    }
}
iplatform-base/src/main/java/com/iplatform/base/PlatformUserCallback.java
New file
@@ -0,0 +1,34 @@
package com.iplatform.base;
import com.iplatform.model.po.S_user_core;
import com.walker.infrastructure.ApplicationCallback;
/**
 * 用户操作回调接口定义。
 * <pre>
 *     1)业务可以配置实现自己的用户操作回调动作,如:当系统添加用户时,业务要初始设置用户默认积分等。
 *     2)实现该接口后,直接配置为<code>Sping Bean</code> 即可。
 *     3)注意:该方法会在数据库事务中调用,因此业务尽量在回调中只操作数据库,降低时间操作过长风险。
 * </pre>
 * @date 2023-01-05
 */
public interface PlatformUserCallback extends ApplicationCallback {
    /**
     * 当系统创建新用户时,回调该接口
     * @param user
     */
    public void onCreateNewUser(S_user_core user);
    /**
     * 当系统更新用户时,回调该接口
     * @param user
     */
    public void onUpdateUser(S_user_core user);
    /**
     * 当系统删除用户时,回调该接口
     * @param userId
     */
    public void onDeleteUser(long userId);
}
iplatform-base/src/main/java/com/iplatform/base/PushCacheProvider.java
New file
@@ -0,0 +1,13 @@
package com.iplatform.base;
/**
 * 推送提醒,缓存定义。存储临时并行通知的消息,2分钟内失效。
 * @author 时克英
 * @date 2023-04-27
 */
public interface PushCacheProvider {
    void put(String key, String value);
    String get(String key);
}
iplatform-base/src/main/java/com/iplatform/base/PushController.java
New file
@@ -0,0 +1,106 @@
package com.iplatform.base;
import com.iplatform.base.push.DefaultPushManager;
import com.iplatform.base.util.NotificationUtils;
import com.walker.infrastructure.utils.DateUtils;
import com.walker.infrastructure.utils.NumberGenerator;
import com.walker.infrastructure.utils.StringUtils;
import com.walker.push.Notification;
import com.walker.push.NotificationChannel;
import com.walker.push.util.PushUtils;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
public abstract class PushController extends SystemController{
    /**
     * 发送短信验证码,登录与否都可调用。
     * @param code 发送的验证码
     * @param userIdOrMobile 用户ID 或者 手机号
     * @date 2023-04-26
     */
    protected void pushSmsValidateCode(String code, String userIdOrMobile){
        String creator = this.getCurrentUserPrincipal() == null? "creator" : this.getCurrentUserPrincipal().getId();
        ((DefaultPushManager)this.getPushManager()).pushSmsValidateCode(code, userIdOrMobile, creator);
    }
    /**
     * 推送短信内容,登录与否都可调用。
     * @param templateCode 短信模板代码
     * @param param 参数
     * @param userIdOrMobile 用户ID 或者 手机号
     * @date 2023-04-26
     */
    protected void pushSmsNotification(String templateCode, Map<String, String> param, String userIdOrMobile){
        String creator = this.getCurrentUserPrincipal() == null? "creator" : this.getCurrentUserPrincipal().getId();
        Notification notification = NotificationUtils
                .acquireSmsNotification(templateCode, param, null, userIdOrMobile, creator);
        this.getPushManager().pushSms(notification);
    }
    /**
     * 推送邮件通知,该方法无论登录都可调用。
     * <p>不登录的话,userIdOrMail 必须是邮件地址</p>
     * @param title 标题
     * @param content 内容
     * @param userIdOrMail 用户ID(或邮件地址)
     * @date 2023-04-26
     */
    protected void pushMailNotification(String title, String content, String userIdOrMail){
        if(StringUtils.isEmpty(title)
                || StringUtils.isEmpty(content)
                || StringUtils.isEmpty(userIdOrMail)){
            throw new IllegalArgumentException("推送邮件失败:参数为空!");
        }
        String creator = this.getCurrentUserPrincipal() == null? "creator" : this.getCurrentUserPrincipal().getId();
        Notification notification = PushUtils.acquireEmailNotificationOne(title, content
                , ((DefaultPushManager)this.getPushManager()).getMailFrom(), userIdOrMail, creator);
        this.getPushManager().push(notification, null);
    }
    /**
     * 推送平台普通消息,不包括:邮件和短信。
     * <p>这些通知会保存到数据库中,一般为操作业务的各种通知,如:待办事项等。</p>
     * @param pushData 业务传递的数据
     * @date 2023-04-26
     */
    protected void pushMessageNotification(PushData pushData){
        if(pushData == null){
            throw new IllegalArgumentException("pushData is null!");
        }
        if(StringUtils.isEmpty(pushData.getBusinessType())
                || StringUtils.isEmpty(pushData.getBusinessId())
                || StringUtils.isEmpty(pushData.getUserId())){
            throw new IllegalArgumentException("推送业务消息缺少条件:businessType, businessId, userId");
        }
        String creator = this.getCurrentUserPrincipal() == null? "creator" : this.getCurrentUserPrincipal().getId();
        Notification notification = new Notification();
        notification.setCreator(creator);
        notification.setCreateTime(DateUtils.getDateTimeNumber());
        notification.setFrom(Constants.PUSH_FROM_DEFAULT);
        notification.setTitle(pushData.getTitle());
        notification.setOptionId(pushData.getBusinessId());
        notification.setOptionType(pushData.getBusinessType());
        notification.setContent(pushData.toJson());
        notification.setId(NumberGenerator.getLongSequenceId());
        List<String> receiverList = new ArrayList<>(2);
        receiverList.add(pushData.getUserId());
        notification.setReceiverList(receiverList);
        // 根据平台配置实用通道
        List<NotificationChannel> channelList = new ArrayList<>(4);
        for(String index : this.getPushManager().getMessageChannelNames()){
            channelList.add(NotificationChannel.getType(index));
        }
        notification.setChannelList(channelList);
        // 是否并行
        notification.setParallel(this.getPushManager().isMessageParallel());
        this.getPushManager().push(notification, null);
    }
}
iplatform-base/src/main/java/com/iplatform/base/PushData.java
New file
@@ -0,0 +1,99 @@
package com.iplatform.base;
import com.walker.infrastructure.utils.JsonUtils;
import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;
/**
 * 平台定义的业务推送(消息)对象。<p>该对象在业务层使用。</p>
 * @author 时克英
 * @date 2023-04-25
 */
public class PushData implements Serializable {
    @Override
    public String toString(){
        return new StringBuilder("[userId=").append(this.userId)
                .append(", bizId=").append(this.businessId)
                .append(", type=").append(this.businessType)
                .append(", schema=").append(this.schema)
                .append(", title=").append(this.title)
                .append("]").toString();
    }
    public String toJson(){
        try {
            return JsonUtils.objectToJsonString(this);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
    public void addParameter(String key, Object value){
        if(this.parameter == null){
            this.parameter = new HashMap<>(2);
        }
        this.parameter.put(key, value);
    }
    public String getUserId() {
        return userId;
    }
    public void setUserId(String userId) {
        this.userId = userId;
    }
    public String getBusinessId() {
        return businessId;
    }
    public void setBusinessId(String businessId) {
        this.businessId = businessId;
    }
    public String getBusinessType() {
        return businessType;
    }
    public void setBusinessType(String businessType) {
        this.businessType = businessType;
    }
    public String getSchema() {
        return schema;
    }
    public void setSchema(String schema) {
        this.schema = schema;
    }
    public String getTitle() {
        return title;
    }
    public void setTitle(String title) {
        this.title = title;
    }
    public Map<String, Object> getParameter() {
        return parameter;
    }
    public void setParameter(Map<String, Object> parameter) {
        this.parameter = parameter;
    }
    private String userId;
    private String businessId;
    private String businessType;
    private String schema;
    private String title;
    private Map<String, Object> parameter = null;
    public static final String SCHEMA_HTTP = "https://";
    public static final String SCHEMA_APP = "app://";
    public static final String SCHEMA_SYSTEM = "system://";
}
iplatform-base/src/main/java/com/iplatform/base/RootConfigBean.java
New file
@@ -0,0 +1,83 @@
package com.iplatform.base;
import com.iplatform.base.cache.DictCacheProvider;
import com.iplatform.base.callback.PlatformCallbackPostProcessor;
import com.iplatform.base.di.PlatformDataImportEngine;
import com.walker.cache.CacheProvider;
import com.walker.push.PushManager;
import com.walker.web.CaptchaProvider;
import com.walker.web.CaptchaResult;
import java.util.List;
/**
 * 系统业务bean配置总管理,用于测试在 SystemController 初始化后自动组装一些通用缓存对象。<p></p>
 * 测试通过!
 * @author 时克英
 * @date 2023-03-10
 */
public class RootConfigBean {
    /**
     * 触发自动设置各种公共 Bean 到系统控制器中,不用在创建时手动注入,也不需要依赖 spring的特定注解。
     * @date 2023-03-10
     */
    public void startup(){
        List<SystemController> systemControllerList = this.callbackPostProcessor.getSystemControllerList();
        if(systemControllerList != null){
            for(SystemController controller : systemControllerList){
                controller.setDictCacheProvider(this.dictCacheProvider);
                controller.setDeptCacheProvider(this.deptCacheProvider);
                controller.setUserCacheProvider(this.userCacheProvider);
                controller.setPlatformDataImportEngine(this.platformDataImportEngine);
                controller.setPushManager(this.pushManager);
                controller.setSmsCaptchaProvider(this.smsCaptchaProvider);
                controller.setCaptchaCacheProvider(this.captchaCacheProvider);
            }
            System.out.println("............... | RootConfigBean: startup()");
        }
    }
    public void setCallbackPostProcessor(PlatformCallbackPostProcessor callbackPostProcessor) {
        this.callbackPostProcessor = callbackPostProcessor;
    }
    public void setDeptCacheProvider(DeptCacheProvider deptCacheProvider) {
        this.deptCacheProvider = deptCacheProvider;
    }
    public void setUserCacheProvider(UserCacheProvider userCacheProvider) {
        this.userCacheProvider = userCacheProvider;
    }
    public void setPlatformDataImportEngine(PlatformDataImportEngine platformDataImportEngine) {
        this.platformDataImportEngine = platformDataImportEngine;
    }
    public void setDictCacheProvider(DictCacheProvider dictCacheProvider) {
        this.dictCacheProvider = dictCacheProvider;
    }
    public void setPushManager(PushManager pushManager) {
        this.pushManager = pushManager;
    }
    public void setSmsCaptchaProvider(CaptchaProvider<CaptchaResult> smsCaptchaProvider) {
        this.smsCaptchaProvider = smsCaptchaProvider;
    }
    public void setCaptchaCacheProvider(CacheProvider<String> captchaCacheProvider) {
        this.captchaCacheProvider = captchaCacheProvider;
    }
    private CaptchaProvider<CaptchaResult> smsCaptchaProvider;
    private CacheProvider<String> captchaCacheProvider;
    private PushManager pushManager;
    private PlatformCallbackPostProcessor callbackPostProcessor = null;
    private DeptCacheProvider deptCacheProvider;
    private UserCacheProvider userCacheProvider;
    private PlatformDataImportEngine platformDataImportEngine;
    private DictCacheProvider dictCacheProvider;
}
iplatform-base/src/main/java/com/iplatform/base/SecurityConstants.java
New file
@@ -0,0 +1,38 @@
package com.iplatform.base;
public class SecurityConstants {
    public static final String ROLE_SUPER_ADMIN = "ROLE_SUPERVISOR";
    public static final String ROLE_ADMIN = "ROLE_ADMIN";
    public static final String ROLE_USER = "ROLE_USER";
    /**
     * 顶级机构特有的权限角色,例如:商户独立角色(第三方)
     * @date 2023-06-07
     */
    public static final String ROLE_MERCHANT = "ROLE_MERCHANT";
//    /**
//     * activiti7工作流强制使用的角色
//     * @date 2023-03-21
//     */
//    public static final String ROLE_ACTIVITI_USER = "ROLE_ACTIVITI_USER";
    /**
     * 工作流访问路径前缀。
     * @date 2023-03-21
     */
    public static final String URL_WORKFLOW_PREFIX = "/wf/";
    /**
     * 返回电商前端用户基本信息,用户KEY名称。
     * @date 2023-05-12
     */
    public static final String KEY_USER_INFO = "userInfo";
    /**
     * 移动端用户信息,关键字定义。
     * @date 2023-06-30
     */
    public static final String KEY_USER_INFO_APP = "userInfoApp";
}
iplatform-base/src/main/java/com/iplatform/base/SecuritySpi.java
New file
@@ -0,0 +1,81 @@
package com.iplatform.base;
import com.iplatform.base.exception.LoginException;
import com.iplatform.base.pojo.RequestLogin;
import com.iplatform.model.po.S_user_core;
import com.walker.web.UserPrincipal;
import java.util.List;
import java.util.Map;
/**
 * 安全提供者接口定义。<br>
 * 通过该接口使得 iplatform-base-security 模块可以解耦。
 * @author 时克英
 * @date 2022-11-11
 */
public interface SecuritySpi {
    /**
     * 返回当前登录用户具有的角色ID集合。
     * @return
     */
    List<String> getCurrentUserRoleIdList();
    /**
     * 返回当前登录用户凭证信息
     * @return
     */
    UserPrincipal<S_user_core> getCurrentUserPrincipal();
    /**
     * 返回当前登录用户对象
     * @return
     */
    S_user_core getCurrentUser();
    /**
     * 返回当前登录用户ID
     * @return
     */
    long getCurrentUserId();
    /**
     * 加密给定的明文密码
     * @param password
     * @return 返回密文密码
     */
    String encryptPassword(String password);
    /**
     * 比较给定的密码是否与加密相等。
     * @param rawPassword 原始明文密码
     * @param encodedPassword 加密的密码
     * @return
     */
    boolean matchesPassword(String rawPassword, String encodedPassword);
    /**
     * 是否允许移动端,在登录时手机号不存在直接注册?
     * @return
     * @date 2023-06-28
     */
    boolean isAllowMobileLoginRegister();
    /**
     * 用户登录系统过程方法,抽取该方法可以在自定义登录中灵活使用。
     * @param requestLogin
     * @return
     * @throws LoginException 登录异常抛出提示错误
     * @date 2023-06-28
     */
    Map<String, Object> login(RequestLogin requestLogin) throws LoginException;
    /**
     * 以流程角色登录获取权限,activiti7专用,后续会废弃。
     * @author 时克英
     * @date 2023-03-21
     */
    @Deprecated
    void loginAsWorkflowRole();
}
iplatform-base/src/main/java/com/iplatform/base/SystemController.java
New file
@@ -0,0 +1,551 @@
package com.iplatform.base;
import com.iplatform.base.cache.DictCacheProvider;
import com.iplatform.base.di.PlatformDataImportEngine;
import com.iplatform.base.di.TemplateInfo;
import com.iplatform.base.service.DeptServiceImpl;
import com.iplatform.base.service.LogServiceImpl;
import com.iplatform.base.service.RoleServiceImpl;
import com.iplatform.base.service.UserServiceImpl;
import com.iplatform.base.support.strategy.LoginStrategyManager;
import com.iplatform.base.util.MenuUtils;
import com.iplatform.core.BeanContextAware;
import com.iplatform.model.po.S_dept;
import com.iplatform.model.po.S_dict_data;
import com.iplatform.model.po.S_oper_log;
import com.iplatform.model.po.S_user_core;
import com.iplatform.model.po.S_user_login;
import com.walker.cache.CacheProvider;
import com.walker.infrastructure.utils.DateUtils;
import com.walker.infrastructure.utils.FileCopyUtils;
import com.walker.infrastructure.utils.NumberGenerator;
import com.walker.infrastructure.utils.PhoneNumberUtils;
import com.walker.infrastructure.utils.StringUtils;
import com.walker.push.PushManager;
import com.walker.web.CaptchaProvider;
import com.walker.web.CaptchaResult;
import com.walker.web.OrgType;
import com.walker.web.ResponseCode;
import com.walker.web.UserOnlineProvider;
import com.walker.web.UserPrincipal;
import com.walker.web.UserType;
import com.walker.web.WebAgentService;
import com.walker.web.WebUserAgent;
import com.walker.web.log.BusinessType;
import com.walker.web.log.OperateUser;
import com.walker.web.util.IdUtils;
import com.walker.web.util.ServletUtils;
import jakarta.servlet.http.HttpServletRequest;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.TimerTask;
/**
 * 系统控制器对象,业务所有 <code>Controller</code> 对象,都要继承该对象。
 * @author 时克英
 * @date 2022-12-01
 */
public abstract class SystemController extends AbstractFileOperateSpiController{
    /**
     * 更新给定用户登录缓存中的角色集合内容,roleIds。
     * <pre>
     *     1) 当用户变更角色后,可以直接刷新界面(重新请求用户信息)获取新的对应菜单权限。
     * </pre>
     * @param userId 用户ID
     * @author 时克英
     * @date 2023-12-28
     */
    protected void updateLoginUserRoleListInCache(Long userId){
        S_user_core userCore = this.getUser(userId);
        if(userCore == null){
            throw new PlatformRuntimeException("缓存中未查找到用户,这不正常,userId = " + userId, null);
        }
        S_user_login userLogin = this.getLoginStrategyManager().getUserLogin(userCore.getUser_name());
        if(userLogin == null){
            if(this.logger.isDebugEnabled()){
                this.logger.debug("用户:{}不存在登录缓存,无需更新角色(roleId)缓存,userName={}", userId, userCore.getUser_name());
            }
            return;
        }
        UserPrincipal<?> existUserPrincipal = BeanContextAware.getBeanByType(UserOnlineProvider.class).getUserPrincipal(userLogin.getUuid());
        if (existUserPrincipal==null) {
            return;
        }
        List<String> existRoleIdList = existUserPrincipal.getRoleIdList();
//        List<String> roleIdList = this.getUserService().queryUserRoleIdList(userId);
        List<String> roleIdList = BeanContextAware.getBeanByType(RoleServiceImpl.class).queryUserRoleIdList(userId);
        if(this.logger.isDebugEnabled()){
            this.logger.debug("给定用户:{},包含的角色:{}", userId, roleIdList);
        }
        if(StringUtils.isEmptyList(roleIdList)){
            roleIdList = new ArrayList<>(4);
        }
        // 把原来用户已有的系统级权限角色重新写入,否则用户会重新让登录
        if(existRoleIdList != null){
            for(String securityRoleName : existRoleIdList){
                if(securityRoleName.equals(SecurityConstants.ROLE_USER)
                        || securityRoleName.equals(SecurityConstants.ROLE_ADMIN)
                        || securityRoleName.equals(SecurityConstants.ROLE_SUPER_ADMIN)
                        || securityRoleName.equals(SecurityConstants.ROLE_MERCHANT)){
                    roleIdList.add(securityRoleName);
                }
            }
        }
        DefaultUserPrincipal userPrincipal = new DefaultUserPrincipal(userCore);
        userPrincipal.setRoleIdList(roleIdList);
        // 更新缓存的登录信息
        BeanContextAware.getBeanByType(UserOnlineProvider.class).cacheUserPrincipal(userLogin.getUuid(), userPrincipal, VariableConstants.DEFAULT_TOKEN_EXPIRED_MINUTES);
    }
    protected UserLoginCache getUserLoginCache(){
        return BeanContextAware.getBeanByType(UserLoginCache.class);
    }
    /**
     * 获得通知提醒模板配置缓存。
     * @return
     * @date 2023-08-25
     */
    protected NotificationTemplateCache getNotificationTemplateCache(){
        return BeanContextAware.getBeanByType(NotificationTemplateCache.class);
    }
    /**
     * 写入成功操作日志
     * @param userName 当前用户名,可以是登录ID或名字,可选
     * @param operateUser 操作用户类型
     * @param businessType 业务类型
     * @param title 标题描述,可选
     * @param input 输入参数,可选
     * @param output 输出参数,可选
     * @date 2023-08-13
     */
    protected void systemLogSuccess(String userName, OperateUser operateUser, BusinessType businessType
            , String title, String input, String output){
        S_oper_log s_oper_log = this.acquireSystemLog(userName, operateUser, businessType, title, input, output);
        AsyncManager.me().execute(new TimerTask() {
            @Override
            public void run() {
                BeanContextAware.getBeanByType(LogServiceImpl.class).execInsertOperateLog(s_oper_log);
            }
        });
    }
    /**
     * 写入错误日志
     * @param userName 当前用户名,可以是登录ID或名字,可选
     * @param operateUser 操作用户类型,可选
     * @param businessType 业务类型,可选
     * @param title 标题描述,可选
     * @param input 输入参数,可选
     * @param error 错误内容,必填
     * @date 2023-08-13
     */
    protected void systemLogError(String userName, OperateUser operateUser, BusinessType businessType, String title, String input, String error){
        if(StringUtils.isEmpty(error)){
            logger.warn("调用写入错误日志,但未提供错误内容,日志将不会保存:systemLogError --> error is null!");
            return;
        }
        S_oper_log s_oper_log = this.acquireSystemLog(userName, operateUser, businessType, title, input, null);
        s_oper_log.setStatus(ResponseCode.ERROR.getCode());
        s_oper_log.setError_msg(StringUtils.substring(error, 0, Constants.LOG_ERROR_MAX_SIZE));
        AsyncManager.me().execute(new TimerTask() {
            @Override
            public void run() {
                BeanContextAware.getBeanByType(LogServiceImpl.class).execInsertOperateLog(s_oper_log);
            }
        });
    }
    private S_oper_log acquireSystemLog(String userName, OperateUser operateUser, BusinessType businessType
            , String title, String input, String output){
        if(operateUser == null){
            operateUser = OperateUser.Manage;
        }
        if(businessType == null){
            businessType = BusinessType.Other;
        }
        WebUserAgent agent = this.getCurrentWebUserAgent();
        S_oper_log s_oper_log = new S_oper_log();
        s_oper_log.setOper_id(NumberGenerator.getLongSequenceNumber());
        s_oper_log.setOper_time(DateUtils.getDateTimeNumber());
        s_oper_log.setStatus(ResponseCode.SUCCESS.getCode());
        s_oper_log.setOper_name(userName);
        s_oper_log.setOperate_user(operateUser.getIndex());
        s_oper_log.setBusiness_type(businessType.getIndex());
        s_oper_log.setOper_ip(agent.getIp());
        s_oper_log.setOper_location(agent.getLocation());
        s_oper_log.setRequest_method(agent.getMethod());
        s_oper_log.setOper_url(agent.getUrl());
        s_oper_log.setTitle(title);
        if(StringUtils.isNotEmpty(input)){
            s_oper_log.setOper_param(input);
        }
        if(StringUtils.isNotEmpty(output)){
            s_oper_log.setJson_result(output);
        }
        return s_oper_log;
    }
    /**
     * 返回登录策略管理器对象
     * @return
     * @date 2023-08-05
     */
    protected LoginStrategyManager getLoginStrategyManager(){
        return BeanContextAware.getBeanByType(LoginStrategyManager.class);
    }
    /**
     * 返回当前web浏览器代理对象。
     * @return
     * @date 2023-07-25
     */
    protected WebUserAgent getCurrentWebUserAgent(){
        HttpServletRequest request = this.getRequest();
        return this.getWebAgentService().getWebUserAgent(request.getHeader("User-Agent"), request);
    }
    protected WebAgentService getWebAgentService(){
        return BeanContextAware.getBeanByType(WebAgentService.class);
    }
    /**
     * 判断短信验证码是否正确。
     * @param code 验证码
     * @param uuid 请求验证码标识
     * @return
     * @date 2023-08-07
     */
    protected boolean validateSmsCode(String code, String uuid){
        CaptchaResult captchaResult = new CaptchaResult();
        captchaResult.setCode(code);
        captchaResult.setUuid(uuid);
        return this.smsCaptchaProvider.validateCaptcha(captchaResult);
    }
    /**
     * 发送短信验证码。
     * @param phoneNumber 手机号
     * @return 返回前端需要的uuid,提交登录使用
     * @throws PlatformRuntimeException
     * @date 2023-06-28
     */
    protected Map<String, Object> sendSmsCodeValidation(String phoneNumber){
        if(!PhoneNumberUtils.isCellPhoneNumber(phoneNumber)){
            throw new PlatformRuntimeException("手机号码格式错误:" + phoneNumber);
        }
        String uuid = IdUtils.simpleUUID();
        Map<String, Object> data = new HashMap<>(4);
        data.put("uuid", uuid);
        String verifyKey = Constants.CAPTCHA_CODE_PREFIX + uuid;
        CaptchaResult captchaResult = this.smsCaptchaProvider.generateCaptcha(phoneNumber);
        if(captchaResult == null){
//            return ResponseValue.error("短信验证码生成错误, null");
            throw new PlatformRuntimeException("短信验证码生成错误, null");
        }
        // 写入验证码 key 和 code 到缓存中
        this.captchaCacheProvider.putCacheData(verifyKey, captchaResult.getCode(), 120);
        if(this.logger.isDebugEnabled()){
            this.logger.debug("生成短信验证码:{}, uuid:{}", captchaResult.getCode(), uuid);
        }
        data.put("message", "短信验证码已发送");
        return data;
    }
    /**
     * 返回用户归属值,如果是平台为'-1',如果为租户(商户)则为定义的商户ID。
     * <pre>
     *     注意要点:
     *     1)因为系统框架中用户是长整形(Long),但商户中是(int)因此在实际添加商户数据时,需要按照序列来计算,这样只需要整形即可。
     * </pre>
     * @return
     * @date 2023-06-05
     */
    protected long getOwner(){
        S_user_core user = this.getCurrentUser();
        int userType = user.getUser_type();
        if(userType == UserType.TYPE_SUPER){
            return Constants.OWNER_PLATFORM;
        }
        // 其他人需要根据机构中"menu_type"字段区分是平台机构,还是业务独立机构
        S_dept dept = this.getDept(user.getOrg_id());
        if(dept.getMenu_type().intValue() == MenuUtils.MENU_SCOPE_PLATFORM){
            return Constants.OWNER_PLATFORM;
        }
        // 机构ID就是业务创建的独立编号(如:商户id),这里商户是int,但系统机构中仍然使用bigint
        return dept.getId();
    }
    /**
     * 返回当前用户所在顶级单位,使用的菜单范围:平台0,商户(顶级独立单位菜单范围)4,后续可能会有其他值。
     * @return
     * @date 2023-06-01
     */
    protected int getCurrentOrgMenuScope(){
        long orgId = this.getCurrentUser().getOrg_id();
        if(orgId == Constants.SUPERVISOR_ID){
            // 超级管理员,返回平台菜单范围
            return MenuUtils.MENU_SCOPE_PLATFORM;
        }
         return this.getDeptCacheProvider().getDept(orgId).getMenu_type();
    }
    /**
     * 根据字典数据code,返回字典项名称。
     * @param dictCode 字典项编码(主键)
     * @return
     * @date 2023-03-26
     */
    protected String getDictName(long dictCode){
        S_dict_data dict_data = this.getDictCacheProvider().getCacheData(String.valueOf(dictCode));
        if(dict_data == null){
            throw new IllegalStateException("缓存中未找到字典名称,dict_code = " + dictCode);
        }
        return dict_data.getDict_label();
    }
    /**
     * 返回给定机构的名字。
     * @param deptId 机构ID
     * @return
     * @date 2023-03-23
     */
    protected String getDeptName(long deptId){
        S_dept dept = this.getDept(deptId);
        return dept == null ? "None":dept.getDept_name();
    }
    /**
     * 返回给定id的部门对象
     * @param deptId 部门ID
     * @return
     * @author 时克英
     * @date 2023-03-23
     */
    protected S_dept getDept(long deptId){
        return this.getDeptCacheProvider().getDept(deptId);
    }
    /**
     * 从缓存中返回给定的用户
     * @param userId 用户id
     * @return
     * @date 2023-03-22
     */
    public S_user_core getUser(long userId){
        return this.getUserCacheProvider().getUser(userId);
    }
    /**
     * 下载数据导入模板,该模板由系统自动生成。<br>
     * 业务需要提供数据库对应的'表名称',如:s_user_core
     * @param tableName 表名
     * @date 2023-03-18
     */
    protected void downloadLocalImportTemplate(String tableName){
        if(StringUtils.isEmpty(tableName)){
            throw new IllegalArgumentException("请提供要导入数据的'表名称'!");
        }
        // 本地文件存储根路径,如: d:/temp/
        String templatePath = this.acquireFileOperateSpi().getFileRootConfig();
        // 生成的模板文件名称,如:demo.xlsx
        String templateFileName = Constants.TEMPLATE_IMPORT_PREFIX + tableName + "_" + DateUtils.getDateTimeSecondForShow() + ".xlsx";
        TemplateInfo templateInfo = new TemplateInfo();
        // 2023-05-07 设置模板生成具体路径。
        templateInfo.setTemplatePath(templatePath + templateFileName);
        templateInfo.setTableName(tableName);
        logger.debug("templateInfo = {}", templateInfo);
        File templateFile = this.getDataImportEngine().generateTemplate(templateInfo);
        try {
            this.downloadSimpleFile(FileCopyUtils.copyToByteArray(templateFile), templateFileName);
        } catch (IOException e) {
            logger.error("下载模板错误:" + e.getMessage() + ", tableName=" + tableName, e);
            ServletUtils.renderString(getResponse(), "下载模板错误:" + e.getMessage() + ", tableName=" + tableName);
        }
    }
    /**
     * 返回数据字典缓存对象。<p></p>
     * 注意:这种写法后续要改掉,统一由平台自动注入,无需单独设置。
     * @return
     * @date 2023-03-10
     */
    protected DictCacheProvider getDictCacheProvider(){
//        if(this.dictCacheProvider == null){
//            this.dictCacheProvider = BeanContextAware.getBeanByType(DictCacheProvider.class);
//        }
        return this.dictCacheProvider;
    }
    /**
     * 返回数据导入引擎实现对象,用来完成 Excel 导入功能。
     * @return
     * @date 2023-02-07
     */
    protected PlatformDataImportEngine getDataImportEngine(){
//        return BeanContextAware.getBeanByType(PlatformDataImportEngine.class);
        return this.platformDataImportEngine;
    }
    public UserCacheProvider getUserCacheProvider() {
//        if(this.userCacheProvider == null){
//            this.userCacheProvider = BeanContextAware.getBeanByType(UserCacheProvider.class);
//        }
        return userCacheProvider;
    }
    public DeptCacheProvider getDeptCacheProvider() {
//        if(this.deptCacheProvider == null){
//            this.deptCacheProvider = BeanContextAware.getBeanByType(DeptCacheProvider.class);
//        }
        return deptCacheProvider;
    }
    /**
     * 返回给定用户的顶级机构ID。
     * @param userId
     * @return
     * @date 2022-12-15
     */
    protected long getUserRootOrgId(long userId){
        this.checkUserCacheProvider();
        S_user_core user_core = this.userCacheProvider.getUser(userId);
        if(user_core == null){
            throw new IllegalStateException("缓存中未找到用户: " + userId);
        }
        return user_core.getOrg_id();
    }
    /**
     * 根据机构(部门等非顶级ID)返回顶级机构ID。
     * @param deptId
     * @return
     * @date 2022-12-12
     */
    protected long getRootOrgIdByDept(long deptId){
        if(this.deptCacheProvider == null){
            throw new IllegalArgumentException("请先设置controller对象: deptCacheProvider");
        }
        S_dept dept = this.deptCacheProvider.getDept(deptId);
        if(dept == null){
            throw new IllegalStateException("缓存中未找到机构: " + deptId);
        }
        return dept.getOrg_id();
    }
    /**
     * 返回当前登录用户可选择的根机构列表,即:第一级机构,通常是集团公司等。<p></p>
     * 这些也称为多租户,系统提供多单位(独立)维护,不同独立单位由各自管理员自行维护。
     * <pre>
     *     1.超级管理员可以看到所有独立机构列表
     *     2.单位用户只能看到本单位(一个机构)
     * </pre>
     * @return
     * @date 2022-12-01
     */
    protected List<S_dept> getOrgListScope(){
        DeptServiceImpl deptService = BeanContextAware.getBeanByType(DeptServiceImpl.class);
        S_user_core currentUser = this.getCurrentUser();
        List<S_dept> list = null;
        if(currentUser.getUser_type().intValue() == UserType.TYPE_SUPER){
            list = deptService.queryRootOrgList(0);
        } else {
            list = deptService.queryRootOrgList(currentUser.getOrg_id().longValue());
        }
        if(StringUtils.isEmptyList(list)){
            // 不存在任何机构(数据库没有记录),需要创建默认机构
            list = new ArrayList<>(2);
            list.add(this.createDefaultOrg());
        }
        return list;
    }
    private void checkUserCacheProvider(){
        if(this.userCacheProvider == null){
            throw new IllegalStateException("UserCacheProvider 必须先设置到控制器中。");
        }
    }
    private S_dept createDefaultOrg(){
        S_dept root = new S_dept();
        root.setId(0L);
        root.setParent_id(0L);
        root.setOrg_type(OrgType.TYPE_ORG);
        root.setOrder_num(1);
        root.setDept_name(Constants.DEFAULT_ORG_NAME);
        return root;
    }
    public void setDeptCacheProvider(DeptCacheProvider deptCacheProvider) {
        this.deptCacheProvider = deptCacheProvider;
    }
    public void setUserCacheProvider(UserCacheProvider userCacheProvider) {
        this.userCacheProvider = userCacheProvider;
    }
    public void setPlatformDataImportEngine(PlatformDataImportEngine platformDataImportEngine) {
        this.platformDataImportEngine = platformDataImportEngine;
    }
    public void setDictCacheProvider(DictCacheProvider dictCacheProvider) {
        this.dictCacheProvider = dictCacheProvider;
    }
    public void setPushManager(PushManager pushManager) {
        this.pushManager = pushManager;
    }
    public void setSmsCaptchaProvider(CaptchaProvider<CaptchaResult> smsCaptchaProvider) {
        this.smsCaptchaProvider = smsCaptchaProvider;
    }
    public void setCaptchaCacheProvider(CacheProvider<String> captchaCacheProvider) {
        this.captchaCacheProvider = captchaCacheProvider;
    }
    public CaptchaProvider<CaptchaResult> getSmsCaptchaProvider() {
        return smsCaptchaProvider;
    }
    public CacheProvider<String> getCaptchaCacheProvider() {
        return captchaCacheProvider;
    }
    protected UserServiceImpl getUserService(){
        return BeanContextAware.getBeanByType(UserServiceImpl.class);
    }
    private CaptchaProvider<CaptchaResult> smsCaptchaProvider;
    private CacheProvider<String> captchaCacheProvider;
    /**
     * 返回推送管理器对象,只有在特殊情况下需要业务直接调用该对象。
     * @return
     * @date 2023-04-25
     */
    protected PushManager getPushManager() {
        return pushManager;
    }
    private PushManager pushManager;
    private DeptCacheProvider deptCacheProvider;
    private UserCacheProvider userCacheProvider;
    private PlatformDataImportEngine platformDataImportEngine;
    private DictCacheProvider dictCacheProvider;
    @Override
    public void afterPropertiesSet() throws Exception {}
}
iplatform-base/src/main/java/com/iplatform/base/SystemGroupCache.java
New file
@@ -0,0 +1,20 @@
package com.iplatform.base;
import com.iplatform.model.vo.SystemGroupVo;
/**
 * 系统分组数据缓存定义,目前主要电商模块使用。
 * <p>后续其他模块都可以作为标准化功能使用。</p>
 * @author 时克英
 * @date 2023-06-23
 */
public interface SystemGroupCache {
    SystemGroupVo get(int id);
    void save(SystemGroupVo category);
    void update(SystemGroupVo category);
    void remove(int id);
}
iplatform-base/src/main/java/com/iplatform/base/ThirdPartyAuthentication.java
New file
@@ -0,0 +1,38 @@
package com.iplatform.base;
import com.iplatform.base.exception.LoginException;
import com.iplatform.base.pojo.RequestLogin;
import java.util.Map;
/**
 * 第三方登录(集成)认证。
 * <pre>
 *     1)该接口主要用于第三方系统直接登录平台方式。
 *     2)第三方传递给平台相关参数,然后平台直接登录并生成token给前端。
 * </pre>
 * @date 2023-07-03
 */
public interface ThirdPartyAuthentication {
    /**
     * 调用第三方认证接口,依据参数获得用户详细信息(以便于转换成平台用户身份)
     * @param parameter 第三方提供的参数
     * @return 返回平台登录参数,请求统一登录地址完成(无感)登录
     * @throws LoginException 如果错误抛出异常
     * @date 2023-07-03
     */
    RequestLogin authenticate(Map<String, Object> parameter) throws LoginException;
    /**
     * 返回认证的唯一标识(或名称)
     * @return
     */
    String getName();
    /**
     * 返回认证对象的描述。
     * @return
     */
    String getDescription();
}
iplatform-base/src/main/java/com/iplatform/base/ThirdPartyManager.java
New file
@@ -0,0 +1,57 @@
package com.iplatform.base;
import com.walker.infrastructure.utils.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
 * 第三方管理器默认实现。
 * @author 时克英
 * @date 2023-07-03
 */
public class ThirdPartyManager {
    protected final transient Logger logger = LoggerFactory.getLogger(this.getClass());
    private final Map<String, ThirdPartyAuthentication> authenticationMap = new ConcurrentHashMap<>(8);
    /**
     * 注册一个第三方认证对象到平台。
     * <pre>
     *     1)其中,name属性通常是第三方请求登录路径中的后缀部分,如:/third_party/login/aaa
     *     2)平台支持多种第三方认证同时使用,只要在业务中注册即可。
     * </pre>
     * @param thirdPartyAuthentication
     * @date 2023-07-03
     */
    public void register(ThirdPartyAuthentication thirdPartyAuthentication){
        if(thirdPartyAuthentication == null){
            throw new IllegalArgumentException("注册第三方认证对象为空");
        }
        String key = thirdPartyAuthentication.getName();
        if(StringUtils.isEmpty(key)){
            throw new IllegalArgumentException("自定义第三方认证对象名称不存在:getName()");
        }
        if(this.authenticationMap.get(key) != null){
            throw new IllegalArgumentException("已经存在第三方认证定义:" + key);
        }
        this.authenticationMap.put(key, thirdPartyAuthentication);
        logger.info("注册'第三方认证对象' = " + key + ", " + thirdPartyAuthentication.getDescription());
    }
    /**
     * 返回一个第三方认证对象
     * @param key 认证对象的名称,参考:{@linkplain ThirdPartyAuthentication#getName()}
     * @return
     * @date 2023-07-03
     */
    public ThirdPartyAuthentication getThirdPartyAuthentication(String key){
        if(StringUtils.isEmpty(key)){
            throw new IllegalArgumentException("请提供第三方认证标识");
        }
        return this.authenticationMap.get(key);
    }
}
iplatform-base/src/main/java/com/iplatform/base/UserCacheProvider.java
New file
@@ -0,0 +1,27 @@
package com.iplatform.base;
import com.iplatform.model.po.S_user_core;
/**
 * 用户缓存统一抽象,针对 Redis 或 内存做不同实现。
 * @author 时克英
 * @date 2022-12-13
 */
public interface UserCacheProvider {
    /**
     * 根据登录id查找缓存用户,如果不存在需要先初始化加载数据库。
     * @param loginId
     * @return
     * @date 2024-03-06
     */
    S_user_core getUserByLoginId(String loginId);
    S_user_core getUser(long userId);
    void updateUser(S_user_core user_core);
    void removeUser(long userId);
    void putUser(S_user_core user_core);
}
iplatform-base/src/main/java/com/iplatform/base/UserLoginCache.java
New file
@@ -0,0 +1,20 @@
package com.iplatform.base;
import com.iplatform.model.po.S_user_login;
/**
 * 用户登录记录缓存定义。
 * <p>每个登录ID只会存在一条记录。</p>
 * @author 时克英
 * @date 2023-07-11
 */
public interface UserLoginCache {
    S_user_login getUserLogin(String loginId);
    void updateUserLogin(S_user_login user_login);
    void removeUserLogin(String loginId);
    void putUserLogin(S_user_login user_login);
}
iplatform-base/src/main/java/com/iplatform/base/VariableConstants.java
New file
@@ -0,0 +1,37 @@
package com.iplatform.base;
/**
 * 系统可配置的参数变量临时定义,等配置参数功能好了需要替换为动态参数: ArgumentsManager.
 * @date 2022-11-02
 */
@Deprecated
public class VariableConstants {
    /**
     * token签名密钥
     */
    public static final String TOKEN_SECRET = "Ha2rEvsXPWovJH2y760OZUlzUOAx1dSnqYB9x1NWHwkGk0SqLn9divY8Rho+/tFkCKXSkUDFtwVmw9TuMbchyzJmlOAoXyDLd27lAo+Byhlysm71/mCOoLfAQIDAQAB";
//    /**
//     * 是否打开验证码选项
//     */
//    public static final boolean CAPTCHA_ENABLED = true;
    /**
     * 创建token默认过期时间(分钟)<br>
     * @date 2023-12-29 修改保底时间,尽量大一些,避免业务配置了10天,但缓存却太短数据已经不存在了!
     */
    public static final long DEFAULT_TOKEN_EXPIRED_MINUTES = 60 * 24 * 30;
//    /**
//     * 缓存用户登录时间(秒)
//     */
//    public static final long DEFAULT_CACHE_USER_SECONDS = 3600;
    /**
     * 缓存管理中,默认能显示的缓存明细记录数量,因为在redis方式下,缓存数量可能太大而无法展示。
     * @date 2023-01-05
     */
    public static final long MAX_CACHE_SHOW = 256;
}
iplatform-base/src/main/java/com/iplatform/base/WechatBaseController.java
New file
@@ -0,0 +1,217 @@
package com.iplatform.base;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.iplatform.base.util.NotificationUtils;
import com.iplatform.base.util.RestTemplateUtils;
import com.iplatform.core.BeanContextAware;
import com.iplatform.model.vo.NotificationTemplateVo;
import com.iplatform.model.vo.WeChatAccessTokenVo;
import com.walker.infrastructure.arguments.Variable;
import com.walker.infrastructure.utils.StringUtils;
import com.walker.push.Notification;
import org.springframework.web.client.RestTemplate;
import java.text.MessageFormat;
import java.util.List;
public abstract class WechatBaseController extends PlatformAdapterController{
//    public void setRestTemplate(RestTemplate restTemplate) {
//        this.restTemplate = restTemplate;
//    }
//    private RestTemplate restTemplate;
    /**
     * 平台统一根据模板,发送提醒通知。
     * @param mark 通知标识,如:发货、订单等,从:sf_notification 表查询
     * @param variableList 业务数据,key,value
     * @param userId 用户ID
     * @date 2023-08-25
     */
    protected void pushNotificationWithTemplate(String mark, List<Variable> variableList, long userId){
        if(StringUtils.isEmpty(mark)){
            throw new IllegalArgumentException("必须输入通知模板标记:mark");
        }
        if(StringUtils.isEmptyList(variableList)){
            throw new IllegalArgumentException("必须输入推送消息业务数据:variableList");
        }
        NotificationTemplateVo templateVo = this.getNotificationTemplateCache().get(mark);
        if(templateVo == null){
            logger.error("通知模板缓存不存在,mark={}", mark);
            return;
        }
        // 1:存在微信公众号推送方式
        if(templateVo.getWechat()){
            String openId = this.getUser(userId).getWx_open_id();
            if(StringUtils.isEmpty(openId)){
                logger.error("用户openId为空,无法发送微信公众号通知, userId={}", userId);
                return;
            }
            String accessToken = this.acquirePublicAccessToken();
            if(StringUtils.isEmpty(accessToken)){
                logger.error("推送公众号消息失败,accessToken获取为空,mark={}, userId={}", mark, userId);
                return;
            }
            Notification notification = NotificationUtils.acquireWechatNotification(accessToken
                    , templateVo.getWechatTempId(), openId, variableList);
            this.getPushManager().push(notification, null);
        }
        // 2:存在短信推送
        if(templateVo.getSms()){
            String userPhone = this.getUser(userId).getPhonenumber();
            if(StringUtils.isEmpty(userPhone)){
                logger.error("用户手机号为空,无法发送短信通知, userId={}", userId);
                return;
            }
            Notification smsNotification = NotificationUtils.acquireSmsNotification(templateVo.getSmsTempId(), userPhone, variableList, "短信通知");
            this.getPushManager().push(smsNotification, null);
        }
        // 3:存在微信小程序推送方式
        if(templateVo.getRoutine()){
            throw new UnsupportedOperationException("未实现微信小程序消息通知");
        }
    }
    /**
     * 获取微信小程序(access_token),并设置缓存。
     * @return
     * @date 2023-09-16
     */
    protected String acquireMiniAccessToken(){
        String appId = this.getArgumentVariable(WechatConstants.WECHAT_MINI_APPID).getStringValue();
        if(StringUtils.isEmpty(appId)){
            throw new PlatformRuntimeException("请先配置微信参数:小程序appid");
        }
        String miniAccessToken = this.getWechatCache().getMiniAccessToken();
        if(StringUtils.isNotEmpty(miniAccessToken)){
            logger.debug("缓存中已有'access_token':{}", miniAccessToken);
            return miniAccessToken;
        }
        String secret = this.getArgumentVariable(WechatConstants.WECHAT_MINI_APPSECRET).getStringValue();
        if (StringUtils.isEmpty(secret)) {
            throw new PlatformRuntimeException("微信小程序secret未设置");
        }
//        String url = MessageFormat.format(WechatConstants.WECHAT_ACCESS_TOKEN_URL, appId, secret);
//        logger.debug("url = {}", url);
//        ObjectNode objectNode = RestTemplateUtils.getData(url, BeanContextAware.getBeanByType(RestTemplate.class));
//        if(objectNode == null){
//            throw new PlatformRuntimeException("微信平台接口异常,没任何数据返回!");
//        }
//        logger.debug(objectNode.toString());
        ObjectNode objectNode = this.acquireAccessTokenNode(appId, secret);
        WeChatAccessTokenVo accessTokenVo = null;
        try {
//            if (objectNode.has("errcode") && !objectNode.get("errcode").asText().equals("0")) {
//                if (objectNode.has("errmsg")) {
//                    // 保存到微信异常表
////                    wxExceptionDispose(data, StrUtil.format("微信获取accessToken异常,{}端", type));
//                    throw new PlatformRuntimeException("微信接口调用失败:" + objectNode.get("errcode") + objectNode.get("errmsg"));
//                }
//            }
            this.checkStatusError(objectNode);
            accessTokenVo = new WeChatAccessTokenVo();
            accessTokenVo.setAccessToken(objectNode.get("access_token").asText());
            accessTokenVo.setExpiresIn(objectNode.get("expires_in").asInt());
//            accessTokenVo = JsonUtils.jsonStringToObject(result, WeChatAccessTokenVo.class);
            this.getWechatCache().putMiniAccessToken(accessTokenVo.getAccessToken(), accessTokenVo.getExpiresIn().longValue() - 1800L);
            logger.info("调用一次微信远程接口获取'mini_accessToken',并缓存:{}", accessTokenVo.getAccessToken());
            return accessTokenVo.getAccessToken();
        } catch (Exception e) {
            throw new RuntimeException("json字符串转对象错误:" + objectNode, e);
        }
    }
    /**
     * 获取微信公众号(普通)access_token
     * @return
     * @date 2023-08-23
     */
    protected String acquirePublicAccessToken(
//            String appId, String type
    ){
        String appId = this.getArgumentVariable(WechatConstants.WECHAT_PUBLIC_APPID).getStringValue();
        if(StringUtils.isEmpty(appId)){
            throw new PlatformRuntimeException("请先配置微信参数:公众号appid");
        }
        String publicAccessToken = this.getWechatCache().getPublicAccessToken();
        if(StringUtils.isNotEmpty(publicAccessToken)){
            logger.debug("缓存中已有'access_token':{}", publicAccessToken);
            return publicAccessToken;
        }
        String secret = this.getArgumentVariable(WechatConstants.WECHAT_PUBLIC_APPSECRET).getStringValue();
        if (StringUtils.isEmpty(secret)) {
            throw new PlatformRuntimeException("微信公众号secret未设置");
        }
//        String url = MessageFormat.format(WechatConstants.WECHAT_ACCESS_TOKEN_URL, appId, secret);
//        logger.debug("url = {}", url);
//        ObjectNode objectNode = RestTemplateUtils.getData(url, BeanContextAware.getBeanByType(RestTemplate.class));
//        if(objectNode == null){
//            throw new PlatformRuntimeException("微信平台接口异常,没任何数据返回!");
//        }
//        logger.debug(objectNode.toString());
        ObjectNode objectNode = this.acquireAccessTokenNode(appId, secret);
        WeChatAccessTokenVo accessTokenVo = null;
        try {
//            if (objectNode.has("errcode") && !objectNode.get("errcode").asText().equals("0")) {
//                if (objectNode.has("errmsg")) {
//                    // 保存到微信异常表
////                    wxExceptionDispose(data, StrUtil.format("微信获取accessToken异常,{}端", type));
//                    throw new PlatformRuntimeException("微信接口调用失败:" + objectNode.get("errcode") + objectNode.get("errmsg"));
//                }
//            }
            this.checkStatusError(objectNode);
            accessTokenVo = new WeChatAccessTokenVo();
            accessTokenVo.setAccessToken(objectNode.get("access_token").asText());
            accessTokenVo.setExpiresIn(objectNode.get("expires_in").asInt());
//            accessTokenVo = JsonUtils.jsonStringToObject(result, WeChatAccessTokenVo.class);
            this.getWechatCache().putPublicAccessToken(accessTokenVo.getAccessToken(), accessTokenVo.getExpiresIn().longValue() - 1800L);
            logger.info("调用一次微信远程接口获取'public_accessToken',并缓存:{}", accessTokenVo.getAccessToken());
            return accessTokenVo.getAccessToken();
        } catch (Exception e) {
            throw new RuntimeException("json字符串转对象错误:" + objectNode, e);
        }
    }
    private ObjectNode acquireAccessTokenNode(String appId, String secret){
        String url = MessageFormat.format(WechatConstants.WECHAT_ACCESS_TOKEN_URL, appId, secret);
        logger.debug("url = {}", url);
        ObjectNode objectNode = RestTemplateUtils.getData(url, BeanContextAware.getBeanByType(RestTemplate.class));
        if(objectNode == null){
            throw new PlatformRuntimeException("微信平台接口异常,没任何数据返回!");
        }
        logger.debug(objectNode.toString());
        return objectNode;
    }
    private void checkStatusError(ObjectNode objectNode){
        //            ObjectNode objectNode = JsonUtils.jsonStringToObjectNode(result);
        if (objectNode.has("errcode") && !objectNode.get("errcode").asText().equals("0")) {
            if (objectNode.has("errmsg")) {
                // 保存到微信异常表
//                    wxExceptionDispose(data, StrUtil.format("微信获取accessToken异常,{}端", type));
                throw new PlatformRuntimeException("微信接口调用失败:" + objectNode.get("errcode") + objectNode.get("errmsg"));
            } else {
                throw new PlatformRuntimeException("微信接口调用失败:" + objectNode.get("errcode"));
            }
        }
    }
    /**
     * 返回微信基础配置缓存对象。
     * @return
     * @date 2023-08-23
     */
    protected WechatCacheProvider getWechatCache(){
        return BeanContextAware.getBeanByType(WechatCacheProvider.class);
    }
}
iplatform-base/src/main/java/com/iplatform/base/WechatCacheProvider.java
New file
@@ -0,0 +1,56 @@
package com.iplatform.base;
/**
 * 微信登录相关配置缓存,目前:ticket、普通 access_token
 * @author 时克英
 * @date 2023-08-23 从电商模块迁移到 base
 */
public interface WechatCacheProvider {
    /**
     * 获取小程序access_token,生成小程序二维码使用。
     * @return
     * @date 2023-09-16
     */
    String getMiniAccessToken();
    /**
     * 返回:JS-SDK的ticket,
     * @return
     */
    String getJsApiTicket();
    /**
     * 获取公共(公众号)accessToken
     * @return
     * @date 2023-07-16
     */
    String getPublicAccessToken();
    /**
     * 设置公共(公众号)accessToken,<p>缓存时间:accessTokenVo.getExpiresIn().longValue() - 1800L</p>
     * @param value
     * @date 2023-07-16
     */
    void putPublicAccessToken(String value, long expiredSeconds);
    /**
     * 缓存ticket,默认:7100 秒后过期。
     * @param value
     */
    void putJsApiTicket(String value);
    /**
     * 设置(小程序)accessToken,<p>缓存时间:accessTokenVo.getExpiresIn().longValue() - 1800L</p>
     * @param value
     * @param expiredSeconds
     * @date 2023-09-16
     */
    void putMiniAccessToken(String value, long expiredSeconds);
    /**
     * 删除(小程序)accessToken.
     * @date 2023-09-16
     */
    void removeMiniAccessToken();
}
iplatform-base/src/main/java/com/iplatform/base/WechatConstants.java
New file
@@ -0,0 +1,490 @@
package com.iplatform.base;
public class WechatConstants {
    /** 微信token类型-公众号 */
    public static final String WECHAT_TOKEN_TYPE_PUBLIC = "public";
    /** 微信token类型-小程序 */
    public static final String WECHAT_TOKEN_TYPE_MINI = "mini";
    /** 微信token类型-APP */
    public static final String WECHAT_TOKEN_TYPE_APP = "app";
    //-------------------------------------------微信系统配置------------------------------------------------------------
    /** 公众号appId key */
    public static final String WECHAT_PUBLIC_APPID = "wechat_appid";
    /** 公众号appSecret key */
    public static final String WECHAT_PUBLIC_APPSECRET = "wechat_appsecret";
    /** 小程序appId key */
    public static final String WECHAT_MINI_APPID = "routine_appid";
    /** 小程序appSecret key */
    public static final String WECHAT_MINI_APPSECRET = "routine_appsecret";
    /** 小程序 名称 */
    public static final String WECHAT_MINI_NAME = "routine_name";
    /** 公众号 支付商户号 */
    public static final String WECHAT_PAY_PUBLIC_MCHID = "pay_weixin_mchid";
    /** 公众号 支付key */
    public static final String WECHAT_PAY_PUBLIC_KEY = "pay_weixin_key";
    /** 公众号 支付证书地址 */
    public static final String WECHAT_PAY_PUBLIC_CERTIFICATE_PATH = "pay_weixin_certificate_path";
    /** 小程序 支付商户号 */
    public static final String WECHAT_PAY_MINI_MCHID = "pay_routine_mchid";
    /** 小程序 支付key */
    public static final String WECHAT_PAY_MINI_KEY = "pay_routine_key";
    /** 小程序 支付证书地址 */
    public static final String WECHAT_PAY_MINI_CERTIFICATE_PATH = "pay_routine_certificate_path";
    /** 微信app appId */
    public static final String WECHAT_APP_APPID = "wechat_app_appid";
    /** 微信app 商户号 */
    public static final String WECHAT_PAY_APP_MCHID = "pay_weixin_app_mchid";
    /** 微信app 支付key */
    public static final String WECHAT_PAY_APP_KEY = "pay_weixin_app_key";
    /** 微信app 支付证书地址 */
    public static final String WECHAT_PAY_APP_CERTIFICATE_PATH = "pay_weixin_app_certificate_path";
    //------------------------------------------------微信公众号------------------------------------------------
    //微信接口请求地址
    public static final String API_URL = "https://api.weixin.qq.com/";
    //获取token
    public static final String API_TOKEN_URI = "cgi-bin/token?grant_type=client_credential";
    // 微信token 过期时间,娶了一个中间值 4000 官方的7200不靠谱
    public static final Long API_TOKEN_EXPIRES = 3000L;
    //微信公众号菜单创建
    public static final String PUBLIC_API_MENU_CREATE_URI = "cgi-bin/menu/create";
    //微信公众号菜单获取
    public static final String PUBLIC_API_MENU_GET_URI = "cgi-bin/menu/get";
    //微信公众号菜单删除
    public static final String PUBLIC_API_MENU_DELETE_URI = "cgi-bin/menu/delete";
    //微信公众号,获取自定义菜单配置接口
    public static final String PUBLIC_API_MENU_SELF_SET_URI = "cgi-bin/get_current_selfmenu_info";
    //微信公众号,创建个性化菜单
    public static final String PUBLIC_API_MENU_ADD_CONDITIONAL_URI = "cgi-bin/menu/addconditional";
    //微信公众号,删除个性化菜单
    public static final String PUBLIC_API_MENU_DEL_CONDITIONAL_URI = "cgi-bin/menu/delconditional";
    //微信公众号,测试个性化菜单匹配结果
    public static final String PUBLIC_API_USER_INFO_URI = "cgi-bin/menu/trymatch";
    //获取公众号已创建的标签
    public static final String PUBLIC_API_TAG_LIST_URI = "cgi-bin/tags/get";
    //创建标签
    public static final String PUBLIC_API_TAG_CREATE_URI = "cgi-bin/tags/create";
    //编辑标签
    public static final String PUBLIC_API_TAG_UPDATE_URI = "cgi-bin/tags/update";
    //删除标签
    public static final String PUBLIC_API_TAG_DELETE_URI = "cgi-bin/tags/delete";
    //获取标签下粉丝列表
    public static final String PUBLIC_API_TAG_USER_GET_URI = "cgi-bin/user/tag/get";
    //批量为用户打标签
    public static final String PUBLIC_API_TAG_MEMBER_BATCH_URI = "cgi-bin/tags/members/batchtagging";
    //批量为用户取消标签
    public static final String PUBLIC_API_TAG_MEMBER_BATCH_UN_URI = "cgi-bin/tags/members/batchuntagging";
    //获取用户身上的标签列表
    public static final String PUBLIC_API_TAG_GET_ID_LIST_URI = "cgi-bin/tags/getidlist";
    //获取 JsApiTicket
    public static final String PUBLIC_API_JS_API_TICKET = "cgi-bin/ticket/getticket";
    //发送公众号模板消息
    public static final String PUBLIC_API_PUBLIC_TEMPLATE_MESSAGE_SEND = "cgi-bin/message/template/send";
    //获取设置的行业信息
    public static final String PUBLIC_API_TEMPLATE_MESSAGE_INDUSTRY = "cgi-bin/template/get_industry";
    //新增其他类型永久素材
    public static final String PUBLIC_API_MEDIA_UPLOAD = "cgi-bin/material/add_material";
    //获取永久素材
    public static final String PUBLIC_API_MEDIA_GET = "cgi-bin/material/get_material";
    //获取微信素材总数
    public static final String PUBLIC_API_MEDIA_COUNT = "cgi-bin/material/get_materialcount";
    //发送客服消息
    public static final String PUBLIC_API_KF_MESSAGE_SEND = "cgi-bin/message/custom/send";
    //------------------------------------------------微信小程序------------------------------------------------
    //小程序行业消息
    public static final String PUBLIC_API_PROGRAM_CATEGORY = "wxaapi/newtmpl/getcategory";
    //小程序公共模板库
    public static final String PUBLIC_API_PROGRAM_PUBLIC_TEMP = "wxaapi/newtmpl/getpubtemplatetitles";
    //小程序模板关键词列表
    public static final String PUBLIC_API_PROGRAM_PUBLIC_TEMP_KEYWORDS = "wxaapi/newtmpl/getpubtemplatekeywords";
    //添加小程序订阅消息
    public static final String PUBLIC_API_ADD_PROGRAM_TEMPLATE = "wxaapi/newtmpl/addtemplate";
    //删除小程序订阅消息
    public static final String PUBLIC_API_DELETE_PROGRAM_TEMPLATE = "wxaapi/newtmpl/deltemplate";
    //发送小程序模板消息
    public static final String PUBLIC_API_PROGRAM_TEMPLATE_MESSAGE_SEND = "cgi-bin/message/subscribe/send";
    //授权登录
    //获取临时code跳转地址
    public static final String WE_CHAT_AUTHORIZE_REDIRECT_URI_URL  = "/api/front/wechat/authorize/login";
    //获取openId
    public static final String WE_CHAT_AUTHORIZE_GET_OPEN_ID = "sns/oauth2/access_token";
    //获取小程序openId
    public static final String WE_CHAT_AUTHORIZE_PROGRAM_GET_OPEN_ID = "sns/jscode2session";
    //获取用户信息
    public static final String WE_CHAT_AUTHORIZE_GET_USER_INFO = "sns/userinfo";
    //生成二维码
    public static final String WE_CHAT_CREATE_QRCODE = "wxa/getwxacodeunlimit";
    //微信消息存储队列
    public static final String WE_CHAT_MESSAGE_SEND_KEY = "we_chat_message_send_list";
    //大家注意这里消息类型的定义,以 RESP 开头的表示返回的消息类型,以 REQ 表示微信服务器发来的消息类型
    /**
     * 返回消息类型:文本
     */
    public static final String WE_CHAT_MESSAGE_RESP_MESSAGE_TYPE_TEXT = "text";
    /**
     * 返回消息类型:音乐
     */
    public static final String WE_CHAT_MESSAGE_RESP_MESSAGE_TYPE_MUSIC = "music";
    /**
     * 返回消息类型:图文
     */
    public static final String WE_CHAT_MESSAGE_RESP_MESSAGE_TYPE_NEWS = "news";
    /**
     * 返回消息类型:图片
     */
    public static final String WE_CHAT_MESSAGE_RESP_MESSAGE_TYPE_IMAGE = "image";
    /**
     * 返回消息类型:语音
     */
    public static final String WE_CHAT_MESSAGE_RESP_MESSAGE_TYPE_VOICE = "voice";
    /**
     * 返回消息类型:视频
     */
    public static final String WE_CHAT_MESSAGE_RESP_MESSAGE_TYPE_VIDEO = "video";
    /**
     * 请求消息类型:文本
     */
    public static final String WE_CHAT_MESSAGE_REQ_MESSAGE_TYPE_TEXT = "text";
    /**
     * 请求消息类型:图片
     */
    public static final String WE_CHAT_MESSAGE_REQ_MESSAGE_TYPE_IMAGE = "image";
    /**
     * 请求消息类型:链接
     */
    public static final String WE_CHAT_MESSAGE_REQ_MESSAGE_TYPE_LINK = "link";
    /**
     * 请求消息类型:地理位置
     */
    public static final String WE_CHAT_MESSAGE_REQ_MESSAGE_TYPE_LOCATION = "location";
    /**
     * 请求消息类型:音频
     */
    public static final String WE_CHAT_MESSAGE_REQ_MESSAGE_TYPE_VOICE = "voice";
    /**
     * 请求消息类型:视频
     */
    public static final String WE_CHAT_MESSAGE_REQ_MESSAGE_TYPE_VIDEO = "video";
    /**
     * 请求消息类型:推送
     */
    public static final String WE_CHAT_MESSAGE_REQ_MESSAGE_TYPE_EVENT = "event";
    /**
     * 事件类型:subscribe(订阅)
     */
    public static final String WE_CHAT_MESSAGE_EVENT_TYPE_SUBSCRIBE = "subscribe";
    /**
     * 事件类型:unsubscribe(取消订阅)
     */
    public static final String WE_CHAT_MESSAGE_EVENT_TYPE_UNSUBSCRIBE = "unsubscribe";
    /**
     * 事件类型:CLICK(自定义菜单点击事件)
     */
    public static final String WE_CHAT_MESSAGE_EVENT_TYPE_CLICK = "click";
    /**
     * 事件类型:VIEW(自定义菜单 URl 视图)
     */
    public static final String WE_CHAT_MESSAGE_EVENT_TYPE_VIEW = "view";
    /**
     * 事件类型:LOCATION(上报地理位置事件)
     */
    public static final String WE_CHAT_MESSAGE_EVENT_TYPE_LOCATION = "LOCATION";
    /**
     * 事件类型:LOCATION(上报地理位置事件)
     */
    public static final String WE_CHAT_MESSAGE_EVENT_TYPE_SCAN = "SCAN";
    //无效关键字key
    public static final String WE_CHAT_MESSAGE_DEFAULT_CONTENT_KEY = "default";
    //Js sdk api 列表
    public static final String PUBLIC_API_JS_API_SDK_LIST = "editAddress,openAddress,updateTimelineShareData,updateAppMessageShareData,onMenuShareTimeline,onMenuShareAppMessage,onMenuShareQQ,onMenuShareWeibo,onMenuShareQZone,startRecord,stopRecord,onVoiceRecordEnd,playVoice,pauseVoice,stopVoice,onVoicePlayEnd,uploadVoice,downloadVoice,chooseImage,previewImage,uploadImage,downloadImage,translateVoice,getNetworkType,openLocation,getLocation,hideOptionMenu,showOptionMenu,hideMenuItems,showMenuItems,hideAllNonBaseMenuItem,showAllNonBaseMenuItem,closeWindow,scanQRCode,chooseWXPay,openProductSpecificView,addCard,chooseCard,openCard";
    //token
    public static final String REDIS_TOKEN_KEY = "wechat_token";
    public static final String REDIS_PROGRAM_TOKEN_KEY = "wechat_program_token";
    //tag
    public static final String REDIS_TAGS_LIST_KEY = "wechat_tags_list";
    //user tag
    public static final String REDIS_TAGS_LIST_USER_KEY = "wechat_tags_user_list";
    //微信菜单
    public static final String REDIS_PUBLIC_MENU_KEY = "wechat_public_menu_key";
    //微信自定义菜单
    public static final String REDIS_PUBLIC_MENU_SELF_KEY = "wechat_public_menu_self_key";
    //授权请求地址
    public static final String WE_CHAT_AUTHORIZE_URL = "https://open.weixin.qq.com/connect/oauth2/authorize?appid={$appId}&redirect_uri={$redirectUri}&response_type=code&scope=snsapi_base&state=#wechat_redirect";
    //-------------------------------------------微信支付------------------------------------------------------------
    //微信支付接口请求地址
    public static final String PAY_API_URL = "https://api.mch.weixin.qq.com/";
    public static final String PAY_API_URI = "pay/unifiedorder";
    public static final String PAY_NOTIFY_API_URI_WECHAT = "/api/admin/payment/callback/wechat";
    // 公共号退款
    public static final String PAY_REFUND_API_URI_WECHAT = "secapi/pay/refund";
    public static final String PAY_TYPE_JS = "JSAPI";
    public static final String PAY_TYPE_H5 = "MWEB";
    // --------------------------------------------------------------------------------------------------------
    // 微信部分
    // --------------------------------------------------------------------------------------------------------
    /** 获取accessToken的url */
    public static final String WECHAT_ACCESS_TOKEN_URL = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={0}&secret={1}";
    /** 开放平台获取accessToken的url */
    public static final String WECHAT_OAUTH2_ACCESS_TOKEN_URL = "https://api.weixin.qq.com/sns/oauth2/access_token?appid={0}&secret={1}&code={2}&grant_type=authorization_code";
    /** 开放平台获取用户的url */
    public static final String WECHAT_SNS_USERINFO_URL = "https://api.weixin.qq.com/sns/userinfo?access_token={0}&openid={1}&lang={2}";
    /** 公众号js-sdk获取ticket的url */
    public static final String WECHAT_PUBLIC_JS_TICKET_URL = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token={0}&type=jsapi";
    /** 公众号发送模板消息的url */
    public static final String WECHAT_PUBLIC_SEND_TEMPLATE_URL = "https://api.weixin.qq.com/cgi-bin/message/template/send?access_token={}";
    /** 公众号获取自定义菜单配置的url */
    public static final String WECHAT_PUBLIC_MENU_GET_URL = "https://api.weixin.qq.com/cgi-bin/menu/get?access_token={}";
    /** 公众号创建自定义菜单的url */
    public static final String WECHAT_PUBLIC_MENU_CREATE_URL = "https://api.weixin.qq.com/cgi-bin/menu/create?access_token={}";
    /** 公众号删除自定义菜单的url */
    public static final String WECHAT_PUBLIC_MENU_DELETE_URL = "https://api.weixin.qq.com/cgi-bin/menu/delete?access_token={}";
    /** 企业号上传其他类型永久素材的url */
    public static final String WECHAT_PUBLIC_QYAPI_ADD_MATERIAL_URL = "https://qyapi.weixin.qq.com/cgi-bin/material/add_material?type={}&access_token={}";
    /** 公众号获取模板列表(自己的) */
    public static final String WECHAT_PUBLIC_GET_ALL_PRIVATE_TEMPLATE_URL = "https://api.weixin.qq.com/cgi-bin/template/get_all_private_template?access_token={}";
    /** 公众号删除模板(自己的) */
    public static final String WECHAT_PUBLIC_DEL_PRIVATE_TEMPLATE_URL = "https://api.weixin.qq.com/cgi-bin/template/del_private_template?access_token={}";
    /** 公众号添加模板(自己的) */
    public static final String WECHAT_PUBLIC_API_ADD_TEMPLATE_URL = "https://api.weixin.qq.com/cgi-bin/template/api_add_template?access_token={}";
    /** 小程序登录凭证校验的url */
    public static final String WECHAT_MINI_SNS_AUTH_CODE2SESSION_URL = "https://api.weixin.qq.com/sns/jscode2session?appid={0}&secret={1}&js_code={2}&grant_type=authorization_code";
    /** 小程序生成小程序码的url */
    public static final String WECHAT_MINI_QRCODE_UNLIMITED_URL = "https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token={0}";
    /** 小程序发送订阅消息的url */
    public static final String WECHAT_MINI_SEND_SUBSCRIBE_URL = "https://api.weixin.qq.com/cgi-bin/message/subscribe/send?access_token={}";
    /** 小程序获取订阅列表(自己的) */
    public static final String WECHAT_MINI_GET_ALL_PRIVATE_TEMPLATE_URL = "https://api.weixin.qq.com/wxaapi/newtmpl/gettemplate?access_token={}";
    /** 小程序删除模板(自己的) */
    public static final String WECHAT_MINI_DEL_PRIVATE_TEMPLATE_URL = "https://api.weixin.qq.com/wxaapi/newtmpl/deltemplate?access_token={}";
    /** 小程序获取订阅模板(小程序的) */
    public static final String WECHAT_MINI_GET_TEMPLATE_URL = "https://api.weixin.qq.com/wxaapi/newtmpl/getpubtemplatekeywords?access_token={}&tid={}";
    /** 公众号添加模板(自己的) */
    public static final String WECHAT_MINI_API_ADD_TEMPLATE_URL = "https://api.weixin.qq.com/wxaapi/newtmpl/addtemplate?access_token={}";
    /** 小程序accessToken redis key */
    public static final String REDIS_WECAHT_MINI_ACCESS_TOKEN_KEY = "wechat_mini_accessToken";
    /** 公众号accessToken redis key */
    public static final String REDIS_WECAHT_PUBLIC_ACCESS_TOKEN_KEY = "wechat_public_accessToken";
    /** 公众号JsApiTicket redis key */
    public static final String REDIS_PUBLIC_JS_API_TICKET = "wechat_js_api_ticket";
    public static final Long REDIS_PUBLIC_JS_API_TICKET_EXPRESS = 7100L;
    /**
     * --------------------------------------------------------------------------------------------------------
     * 以下为视频号相关部分
     * --------------------------------------------------------------------------------------------------------
     */
    /*------------------------------------------ 申请接入接口 START ---------------------------------------*/
    public static final String WECHAT_SHOP_BASE_DOME = "https://api.weixin.qq.com";
    /* 申请接入申请 */
    public static final String WECHAT_SHOP_REGISTER_APPLY = WECHAT_SHOP_BASE_DOME.concat("/shop/register/apply?access_token={}");
    /* 获取接入状态 */
    public static final String WECHAT_SHOP_REGISTER_CHECK = WECHAT_SHOP_BASE_DOME.concat("/shop/register/check?access_token={}");
    /* 完成接入任务 */
    public static final String WECHAT_SHOP_REGISTER_FINISH_ACCESS = WECHAT_SHOP_BASE_DOME.concat("/shop/register/finish_access_info?access_token={}");
    /* 场景接入申请 在小程序上线之后 并且所有的api调用都通过了 才可以使用 调用申请成功之后就可以在视频号直播时选择线上商品了 */
    public static final String WECHAT_SHOP_REGISTER_APPLY_SCENE = WECHAT_SHOP_BASE_DOME.concat("/shop/register/apply_scene?access_token={}");
    /*------------------------------------------ 申请接入接口 END ---------------------------------------*/
    /*------------------------------------------ 接入商品前必须接口 START ---------------------------------------*/
    /** 获取商品类目 */
    public static final String WECHAT_SHOP_CAT_GET_URL = WECHAT_SHOP_BASE_DOME.concat("/shop/cat/get?access_token={}");
    /** 上传图片 **/
    public static final String WECHAT_SHOP_IMG_UPLOAD = WECHAT_SHOP_BASE_DOME.concat("/shop/img/upload?&access_token={}");
    /** 上传品牌信息 */
    public static final String WECHAT_SHOP_AUDIT_AUDIT_BRAND = WECHAT_SHOP_BASE_DOME.concat("/shop/audit/audit_brand?&access_token={}");
    /** 上传类目资质 */
    public static final String WECHAT_SHOP_AUDIT_AUDIT_CATEGORY = WECHAT_SHOP_BASE_DOME.concat("/shop/audit/audit_category?&access_token={}");
    /** 查询类目审核结果 */
    public static final String WECHAT_SHOP_AUDIT_RESULT= WECHAT_SHOP_BASE_DOME.concat("/shop/audit/result?access_token={}");
    /** 获取小程序提交过的入驻资质信息 */
    public static final String WECHAT_SHOP_AUDIT_GET_MINIAPP_CERTIFICATE= WECHAT_SHOP_BASE_DOME.concat("/shop/audit/get_miniapp_certificate?access_token={}");
    /*------------------------------------------ 接入商品前必须接口 END ---------------------------------------*/
    /*------------------------------------------ 商家入驻接口 START ---------------------------------------*/
    /** 获取类目列表 */
    public static final String WECHAT_SHOP_ACCOUNT_GET_CATEGORY_LIST = WECHAT_SHOP_BASE_DOME.concat("/shop/account/get_category_list?access_token={}");
    /** 获取品牌列表 */
    public static final String WECHAT_SHOP_ACCOUNT_GET_BRAND_LIST = WECHAT_SHOP_BASE_DOME.concat("/shop/account/get_brand_list?access_token={}");
    /** 更新商家信息 */
    public static final String WECHAT_SHOP_ACCOUNT_UPDATE_INFO = WECHAT_SHOP_BASE_DOME.concat("/shop/account/update_info?access_token={}");
    /** 获取商家信息 */
    public static final String WECHAT_SHOP_ACCOUNT_GET_INFO = WECHAT_SHOP_BASE_DOME.concat("/shop/account/get_info?access_token={}");
    /*------------------------------------------ 商家入驻接口 END ---------------------------------------*/
    /*------------------------------------------ SPU 接口 START ---------------------------------------*/
    /** 添加商品 */
    public static final String WECHAT_SHOP_SPU_ADD_URL = WECHAT_SHOP_BASE_DOME.concat("/shop/spu/add?access_token={}");
    /** 删除商品 */
    public static final String WECHAT_SHOP_SPU_DEL_URL = WECHAT_SHOP_BASE_DOME.concat("/shop/spu/del?access_token={}");
    /** 撤回商品审核 */
    public static final String WECHAT_SHOP_SPU_DEL_AUDIT_URL = WECHAT_SHOP_BASE_DOME.concat("/shop/spu/del_audit?access_token={}");
    /** 获取商品 */
    public static final String WECHAT_SHOP_SPU_GET_URL = WECHAT_SHOP_BASE_DOME.concat("/shop/spu/get?access_token={}");
    /** 获取商品列表 */
    public static final String WECHAT_SHOP_SPU_GET_LIST_URL = WECHAT_SHOP_BASE_DOME.concat("/shop/spu/get_list?access_token={}");
    /** 更新商品 */
    public static final String WECHAT_SHOP_SPU_UPDATE_URL = WECHAT_SHOP_BASE_DOME.concat("/shop/spu/update?access_token={}");
    /** 上架商品 */
    public static final String WECHAT_SHOP_SPU_LISTING_URL = WECHAT_SHOP_BASE_DOME.concat("/shop/spu/listing?access_token={}");
    /** 下架商品 */
    public static final String WECHAT_SHOP_SPU_DELISTING_URL = WECHAT_SHOP_BASE_DOME.concat("/shop/spu/delisting?access_token={}");
    /*------------------------------------------ SPU 接口 END ---------------------------------------*/
    /*------------------------------------------ 订单 接口 START ---------------------------------------*/
    /** 检查场景值是否在支付校验范围内 */
    public static final String WECHAT_SHOP_SCENE_CHECK_URL = WECHAT_SHOP_BASE_DOME.concat("/shop/scene/check?access_token={}");
    /** 生成订单并获取ticket */
    public static final String WECHAT_SHOP_ORDER_ADD_URL = WECHAT_SHOP_BASE_DOME.concat("/shop/order/add?access_token={}");
    /** 视频号忽略此接口 同步订单支付结果  */
    public static final String WECHAT_SHOP_ORDER_PAY_URL = WECHAT_SHOP_BASE_DOME.concat("/shop/order/pay?access_token={}");
    /** 生成支付参数   */
    public static final String WECHAT_SHOP_ORDER_PAYMENT_PARAMS_URL = WECHAT_SHOP_BASE_DOME.concat("/shop/order/getpaymentparams?access_token={}");
    /** 获取订单 */
    public static final String WECHAT_SHOP_ORDER_GET_URL = WECHAT_SHOP_BASE_DOME.concat("/shop/order/get?access_token={}");
    /** 按推广员获取订单 */
    public static final String WECHAT_SHOP_ORDER_GETBYFINDER_URL = WECHAT_SHOP_BASE_DOME.concat("/shop/order/get_list_by_finder?access_token={}");
    /** 按分享员获取订单 */
    public static final String WECHAT_SHOP_ORDER_GETBYSHARER_URL = WECHAT_SHOP_BASE_DOME.concat("/shop/order/get_list_by_sharer?access_token={}");
    /** 获取订单列表 */
    public static final String WECHAT_SHOP_ORDER_GET_LIST_URL = WECHAT_SHOP_BASE_DOME.concat("/shop/order/get_list?access_token={}");
    /*------------------------------------------ 订单 接口 END ---------------------------------------*/
    /*------------------------------------------ 物流 接口 START ---------------------------------------*/
    /** 获取快递公司列表 */
    public static final String WECHAT_SHOP_DELIVERY_GET_COMPANY_LIST_URL = WECHAT_SHOP_BASE_DOME.concat("/shop/delivery/get_company_list?access_token={}");
    /** 订单发货 */
    public static final String WECHAT_SHOP_DELIVERY_SEND_URL = WECHAT_SHOP_BASE_DOME.concat("/shop/delivery/send?access_token={}");
    /** 订单确认收货 */
    public static final String WECHAT_SHOP_DELIVERY_RECIEVE_URL = WECHAT_SHOP_BASE_DOME.concat("/shop/delivery/recieve?access_token={}");
    /*------------------------------------------ 物流 接口 END ---------------------------------------*/
    /*------------------------------------------ 售后 接口 START ---------------------------------------*/
    /** 创建售后 */
    public static final String WECHAT_SHOP_AFTERSALE_ADD_URL = WECHAT_SHOP_BASE_DOME.concat("/shop/aftersale/add?access_token={}");
    /** 获取售后 */
    public static final String WECHAT_SHOP_AFTERSALE_GET_URL = WECHAT_SHOP_BASE_DOME.concat("/shop/aftersale/get?access_token={}");
    /** 更新售后 */
    public static final String WECHAT_SHOP_AFTERSALE_UPDATE_URL = WECHAT_SHOP_BASE_DOME.concat("/shop/aftersale/update?access_token={}");
    /*------------------------------------------ 售后 接口 END ---------------------------------------*/
    /*------------------------------------------ 优惠券 接口 START 二期 ---------------------------------------*/
    /** 商家确认回调领券事件 */
    public static final String WECHAT_SHOP_COUPON_CONFIRM_URL = WECHAT_SHOP_BASE_DOME.concat("/shop/coupon/confirm?access_token={}");
    /** 添加优惠券 */
    public static final String WECHAT_SHOP_COUPON_ADD_URL = WECHAT_SHOP_BASE_DOME.concat("/shop/coupon/add?access_token={}");
    /** 获取优惠券信息 */
    public static final String WECHAT_SHOP_COUPON_GET_URL = WECHAT_SHOP_BASE_DOME.concat("/shop/coupon/get?access_token={}");
    /** 获取优惠券列表 */
    public static final String WECHAT_SHOP_COUPON_GET_LIST_URL = WECHAT_SHOP_BASE_DOME.concat("/shop/coupon/get_list?access_token={}");
    /** 更新优惠券信息 */
    public static final String WECHAT_SHOP_COUPON_UPDATE_URL = WECHAT_SHOP_BASE_DOME.concat("/shop/coupon/update?access_token={}");
    /** 更新优惠券状态 */
    public static final String WECHAT_SHOP_COUPON_UPDATE_STATUS_URL = WECHAT_SHOP_BASE_DOME.concat("/shop/coupon/update_status?access_token={}");
    /** 更新优惠券库存 */
    public static final String WECHAT_SHOP_COUPON_UPDATE_STOCK_URL = WECHAT_SHOP_BASE_DOME.concat("/shop/coupon/update_coupon_stock?access_token={}");
    /** 添加用户优惠券 */
    public static final String WECHAT_SHOP_COUPON_ADD_USER_COUPON_URL = WECHAT_SHOP_BASE_DOME.concat("/shop/coupon/add_user_coupon?access_token={}");
    /** 获取用户优惠券列表 */
    public static final String WECHAT_SHOP_COUPON_GET_USERCOUPON_LIST_URL = WECHAT_SHOP_BASE_DOME.concat("/shop/coupon/get_usercoupon_list?access_token={}");
    /** 更新用户优惠券 */
    public static final String WECHAT_SHOP_COUPON_UPDATE_USER_COUPON_URL = WECHAT_SHOP_BASE_DOME.concat("/shop/coupon/update_user_coupon?access_token={}");
    /** 更新用户优惠券状态 */
    public static final String WECHAT_SHOP_COUPON_UPDATE_USERCOUPON_STATUS_URL = WECHAT_SHOP_BASE_DOME.concat("/shop/coupon/update_usercoupon_status?access_token={}");
    /*------------------------------------------ 优惠券 接口 END 二期 ---------------------------------------*/
    /*------------------------------------------ 推广员 接口 START 二期 ---------------------------------------*/
    /** 获取推广员列表 */
    public static final String WECHAT_SHOP_COUPON_PROMOTER_LIST_URL = WECHAT_SHOP_BASE_DOME.concat("/shop/promoter/list?access_token={}");
    /*------------------------------------------ 推广员 接口 END 二期 ---------------------------------------*/
    /*------------------------------------------ 分销员 接口 START 二期 ---------------------------------------*/
    /** 绑定分享员 */
    public static final String WECHAT_SHOP_SHARER_BIND_URL = WECHAT_SHOP_BASE_DOME.concat("/shop/sharer/bind?access_token={}");
    /** 解绑分享员 */
    public static final String WECHAT_SHOP_SHARER_UNBIND_URL = WECHAT_SHOP_BASE_DOME.concat("/shop/sharer/unbind?access_token={}");
    /** 获取已经绑定的分享员列表*/
    public static final String WECHAT_SHOP_SHARER_GET_SHARER_LIST_URL = WECHAT_SHOP_BASE_DOME.concat("/shop/sharer/get_sharer_list?access_token={}");
    /** 获取分分享员 */
    public static final String WECHAT_SHOP_SHARER_SEARCH_SHARER_URL = WECHAT_SHOP_BASE_DOME.concat("/shop/sharer/search_sharer?access_token={}");
    /** 获取分享员的中带货数据 */
    public static final String WECHAT_SHOP_SHARER_GET_SHARER_DATA_SUMMARY_URL = WECHAT_SHOP_BASE_DOME.concat("/shop/sharer/get_sharer_data_summary?access_token={}");
    /** 获取分享员的直播间订单汇总 */
    public static final String WECHAT_SHOP_SHARER_GET_SHARER_LIVE_ORDER_LIST_URL = WECHAT_SHOP_BASE_DOME.concat("/shop/sharer/get_sharer_live_order_list?access_token={}");
    /** 获取分享员的直播间带货数据汇总 */
    public static final String WECHAT_SHOP_SHARER_GET_SHARER_LIVE_SUMMARY_LIST_URL = WECHAT_SHOP_BASE_DOME.concat("/shop/sharer/get_sharer_live_summary_list?access_token={}");
    /*------------------------------------------ 分销员 接口 END 二期 ---------------------------------------*/
    /** 自定义组件,商品类型 redis key */
    public static final String REDIS_WECHAT_SHOP_CAT_KEY = "wechat_shop_cat";
    /** 微信小程序回调,商品审核回调事件 */
    public static final String WECAHT_CALLBACK_EVENT_SPU_AUDIT = "open_product_spu_audit";
    /** 微信小程序回调,品牌审核回调事件 */
    public static final String WECAHT_CALLBACK_EVENT_BRAND_AUDIT = "open_product_brand_audit";
    /** 账户接入回调 */
    public static final String WECHAT_CALLBACK_EVENT_OPEN_PRODUCT_ACCOUNT_REGISTER = "open_product_account_register";
    /** 场景审核回调 */
    public static final String WECHAT_CALLBACK_EVENT_OPEN_PRODUCT_SCENE_GROUP_AUDIT = "![CDATA[open_product_scene_group_audit]]";
    /** 订单支付成功回调 */
    public static final String WECHAT_CALLBACK_EVENT_OPEN_PRODUCT_ORDER_PAY = "open_product_order_pay";
    /** 商品系统下架回调 */
    public static final String WECHAT_CALLBACK_EVENT_OPEN_PRODUCT_SPU_STATUS_UPDATE = "open_product_spu_status_update";
    /** 类目审核回调 */
    public static final String WECHAT_CALLBACK_EVENT_OPEN_PRODUCT_CATEGORY_AUDIT = "open_product_category_audit";
    /** 分享员绑定解绑通知 */
    public static final String WECHAT_CALLBACK_EVENT_MINIPROGRAM_SHARER_BIND_STATUS_CHANGE = "miniprogram_sharer_bind_status_change";
    /** 用户领券回调 */
    public static final String WECHAT_CALLBACK_EVENT_OPEN_PRODUCT_RECEIVE_COUPON = "open_product_receive_coupon";
    /** 素材上传图片最大值 */
    public static final Long OPEN_MEDIA_UPLOAD_IMAGE_MAX_SIZE = 1024 * 1024 * 10L;
    /** 素材上传图片后缀类型 */
    public static final String OPEN_MEDIA_UPLOAD_IMAGE_SUFFIX_NAME = "bmp,png,jpeg,jpg,gif";
    /** 素材上传语音最大值 */
    public static final Long OPEN_MEDIA_UPLOAD_VOICE_MAX_SIZE = 1024 * 1024 * 2L;
    /** 素材上传语音后缀类型 */
    public static final String OPEN_MEDIA_UPLOAD_VOICE_SUFFIX_NAME = "mp3,wma,wav,amr";
}
iplatform-base/src/main/java/com/iplatform/base/api/UserAndDeptApi.java
New file
@@ -0,0 +1,40 @@
package com.iplatform.base.api;
import com.iplatform.base.PlatformAdapterController;
import com.iplatform.base.service.UserDeptApiServiceImpl;
import com.iplatform.model.to.UserAndDeptTo;
import com.walker.web.ResponseValue;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
/**
 * 提供对外接口,实现对平台机构人员的操作。
 * @author 时克英
 * @date 2023-06-06
 */
@RestController
@RequestMapping("/api/platform/user_dept")
public class UserAndDeptApi extends PlatformAdapterController
//        implements UserAndDeptServiceApi
{
    private UserDeptApiServiceImpl userDeptApiService;
    @Autowired
    public UserAndDeptApi(UserDeptApiServiceImpl userDeptApiService){
        this.userDeptApiService = userDeptApiService;
    }
    @RequestMapping(value = "/create", method = RequestMethod.POST)
    public ResponseValue execInsertTopOrgAndAdmin(@RequestBody UserAndDeptTo userAndDeptTo) {
        long loginId = this.getCurrentUserId();
        userAndDeptTo.setCreateId(String.valueOf(loginId));
        if(this.logger.isDebugEnabled()){
            logger.debug(userAndDeptTo.toString());
        }
        return this.userDeptApiService.execInsertTopOrgAndAdmin(userAndDeptTo);
    }
}
iplatform-base/src/main/java/com/iplatform/base/cache/DictCacheProvider.java
New file
@@ -0,0 +1,277 @@
package com.iplatform.base.cache;
import com.iplatform.base.Constants;
import com.iplatform.base.service.CodeServiceImpl;
import com.iplatform.model.po.S_dict_data;
import com.iplatform.model.po.S_dict_type;
import com.walker.cache.tree.AbstractCacheTreeProvider;
import com.walker.cache.tree.CacheTreeNode;
import com.walker.cache.tree.DefaultCacheTreeNode;
import com.walker.infrastructure.utils.StringUtils;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
/**
 * 数据字典缓存定义,该缓存因为树结构复杂,并没有提供'Redis'方式实现。<br>
 * 不过一般字典数据变动很小,因此在集群环境中也是相对安全。<p></p>
 * 1) 注意:如果生产环境字典变动较大,而且是集群环境则必须实现'Redis'集中存储缓存对象。
 * @author 时克英
 * @date 2023-03-10
 */
public class DictCacheProvider extends AbstractCacheTreeProvider<S_dict_data> {
    private final SortComparator sort = new SortComparator();
    private final NodeComparator nodeSort = new NodeComparator();
    // 2023-04-15 添加字典类型与id映射记录,因为
    private final Map<String, String> dictTypeIdMap = new HashMap<>();
    /**
     * 根据字典类型,找出对应id,因为前端若依框架中,需要传类型,但系统使用id查询。
     * @param dictType
     * @return
     * @date 2023-04-15
     */
    public String getDictTypeId(String dictType){
        String id = this.dictTypeIdMap.get(dictType);
        if(id == null){
            throw new IllegalArgumentException("根据字典类型,未找到id:" + dictType);
        }
        return id;
    }
    /**
     * 返回代码集合对象,通常在数据库中可能会存储多个代码ID,可以通过此 方法获取代码集合。
     * @param codeIds 输入多个代码ID数组
     * @return
     */
    public List<CacheTreeNode> getCodeList(String[] codeIds){
        List<CacheTreeNode> result = new ArrayList<CacheTreeNode>(2*2);
        if(codeIds != null && codeIds.length > 0){
            for(String cid : codeIds){
                result.add(this.get(cid));
            }
        }
        return result;
    }
    /**
     * 返回给定代码表的子代码项,树结构
     * @param codeTableName 代码表ID
     * @return
     */
    public List<CacheTreeNode> getCodeChildrenList(String codeTableName){
        CacheTreeNode node = this.getOneRootNode(codeTableName);
        if(node == null){
            throw new IllegalArgumentException("not found node: " + codeTableName);
        }
        Collection<CacheTreeNode> list = node.getChildren();
        if(list != null){
            List<CacheTreeNode> result = new ArrayList<CacheTreeNode>();
            for(CacheTreeNode n : list){
                result.add(n);
            }
            Collections.sort(result, nodeSort);
            return result;
        }
        return null;
    }
    /**
     * 返回代码表的下一级子代码集合,返回的是代码对象
     * @param codeTableName 代码表ID
     * @return
     */
    public List<S_dict_data> getRootChildrenOneLevelList(String codeTableName){
        CacheTreeNode node = this.getOneRootNode(codeTableName);
//        CacheTreeNode node2 =this.getOneRootNode("101");
//        Object rootCacheNode = null;
//        for(Iterator<Cachable> it = this.getCache().getIterator(); it.hasNext();){
//            rootCacheNode = it.next().getValue();
//            logger.debug("........ {}" + rootCacheNode);
//        }
        if(node == null){
            throw new IllegalArgumentException("not found node: " + codeTableName);
        }
        Collection<CacheTreeNode> list = node.getChildren();
        if(list != null){
            List<S_dict_data> result = new ArrayList<>();
            S_dict_data temp = null;
            for(CacheTreeNode n : list){
                temp = (S_dict_data)n.getSource();
//                temp.setChildSum(n.getChildrenSize());
                result.add(temp);
            }
            Collections.sort(result, sort);
            return result;
        }
        return null;
    }
    /**
     * 根据某个代码ID,返回其下一级所有子代码集合
     * @param id
     * @return
     */
    public List<S_dict_data> getCodeChildrenOneLevelList(String id){
        CacheTreeNode parent = this.get(id);
        if(parent == null){
            return null;
        }
        Collection<CacheTreeNode> list = parent.getChildren();
        if(list != null){
            List<S_dict_data> result = new ArrayList<>();
            for(CacheTreeNode n : list){
                result.add((S_dict_data)n.getSource());
            }
            return result;
        }
        return null;
    }
    /**
     * 返回某个代码下面的所有代码树。
     * @param id 给定的代码id
     * @return
     */
    public List<CacheTreeNode> getCodeChildrenTreeList(String id){
        CacheTreeNode parent = this.get(id);
        if(parent == null){
            return null;
        }
        Collection<CacheTreeNode> list = parent.getChildren();
        if(list != null){
            List<CacheTreeNode> result = new ArrayList<CacheTreeNode>();
            for(CacheTreeNode n : list){
                result.add(n);
            }
            return result;
        }
        return null;
    }
//    /**
//     * 搜索代码树,支持多关键字搜索,返回集合数据展示给ztree,注意:返回结果只有一级不再分层
//     * @param codeTableId 代码表ID
//     * @param keyLike 查询关键字,如:'锅炉' 或者 '锅炉 电 9号',多关键词用空格分隔
//     * @return
//     */
//    public String searchLikeTreeNodeList(String codeTableId, String keyLike){
////        Collection<CacheTreeNode> deptList = this.getRootList();
//        Collection<CacheTreeNode> deptList = this.getCodeChildrenList(codeTableId);
//        JSONArray array = new JSONArray();
//
//        if(deptList != null){
//            // 过滤某个单位下的根数据集合,后续补充
//            // ...
//            String[] keys = StringUtils.stringToArray(keyLike, " ");
//            boolean multiKeys = false;
//            if(keys != null && keys.length > 1){
//                multiKeys = true;
//            }
//
//            List<CacheTreeNode> tempList = null;
//            for(CacheTreeNode node : deptList){
//                // 不是给定单位的数据不要
////                if(((TypeTree)node.getSource()).getDept() != dept){
////                    continue;
////                }
//                if(multiKeys){
//                    logger.debug("------------------");
//                    tempList = node.searchLike(keys);
//                } else {
//                    tempList = node.searchLike(keyLike);
//                }
//                if(!StringUtils.isEmptyList(tempList)){
//                    for(CacheTreeNode c : tempList){
//                        array.add(CodeUtils.createJsonTree(c.getKey(), c.getText()
//                                , Constants.DEFAULT_TREE_ROOT_ID, false, true, CodeUtils.DEFAULT_SEARCH_ICON, false));
//                    }
//                }
//            }
//        }
//        return array.toString();
//    }
    @Override
    protected Map<String, CacheTreeNode> loadRootList() {
        List<S_dict_type> rootList = this.codeService.queryRootCodeList();
        if(StringUtils.isEmptyList(rootList)){
            return null;
        }
        // 需要转成'S_dict_data'统一对象,老系统中代码表是一个表,这里是两个表。2023-03-10
        List<S_dict_data> dictList = new ArrayList<>();
        S_dict_data data = null;
        for(S_dict_type type : rootList){
            data = new S_dict_data();
            data.setDict_code(type.getDict_id());
            data.setDict_label(type.getDict_name());
            data.setParent_id(0L);  // 根节点不存在父id,默认;0
            dictList.add(data);
            //
            this.dictTypeIdMap.put(type.getDict_type(), String.valueOf(type.getDict_id()));
        }
        return obtainMap(dictList);
    }
    @Override
    protected Map<String, CacheTreeNode> loadChildList() {
        List<S_dict_data> list = this.codeService.queryAllCodeItemList();
        return obtainMap(list);
    }
    @Override
    protected CacheTreeNode toCacheTreeNode(S_dict_data entity) {
        return new DefaultCacheTreeNode(entity.getDict_code().toString()
                , entity.getDict_label(), entity, entity.getParent_id().toString());
    }
    @Override
    public String getProviderName() {
        return Constants.CACHE_NAME_DICT;
    }
    @Override
    public Class<?> getProviderType() {
        return S_dict_data.class;
    }
    private Map<String, CacheTreeNode> obtainMap(List<S_dict_data> list){
        if(list != null && list.size() > 0){
            Map<String, CacheTreeNode> map = new TreeMap<String, CacheTreeNode>();
            for(S_dict_data code : list){
                map.put(code.getDict_code().toString(), toCacheTreeNode(code));
            }
            return map;
        } else
            return null;
    }
    private class SortComparator implements Comparator<S_dict_data> {
        @Override
        public int compare(S_dict_data o1, S_dict_data o2) {
            return (int)(o1.getDict_sort() - o2.getDict_sort());
        }
    }
    private class NodeComparator implements Comparator<CacheTreeNode>{
        @Override
        public int compare(CacheTreeNode o1, CacheTreeNode o2) {
            S_dict_data n1 = (S_dict_data)o1.getSource();
            S_dict_data n2 = (S_dict_data)o2.getSource();
            return (int)(n1.getDict_sort() - n2.getDict_sort());
        }
    }
    public void setCodeService(CodeServiceImpl codeService) {
        this.codeService = codeService;
    }
    private CodeServiceImpl codeService;
}
iplatform-base/src/main/java/com/iplatform/base/cache/FormCacheProvider.java
New file
@@ -0,0 +1,135 @@
package com.iplatform.base.cache;
import com.iplatform.base.Constants;
import com.iplatform.base.PlatformRuntimeException;
import com.iplatform.base.pojo.form.FormData;
import com.iplatform.base.pojo.form.FormDataItem;
import com.iplatform.base.service.ConfigFormServiceImpl;
import com.iplatform.base.util.ConfigFormValidateUtils;
import com.iplatform.model.po.S_config_form;
import com.iplatform.model.vo.ConfigFormItemConfigRegListVo;
import com.iplatform.model.vo.ConfigFormItemVo;
import com.iplatform.model.vo.ConfigFormVo;
import com.walker.cache.AbstractCacheProvider;
import com.walker.cache.Cache;
import com.walker.infrastructure.utils.JsonUtils;
import com.walker.infrastructure.utils.StringUtils;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
 * 自定义表单缓存。
 * <p>1、因为这个数据不会动态变动,因此不需要Redis负载支持。</p>
 * @author 时克英
 * @date 2023-05-18
 */
public class FormCacheProvider extends AbstractCacheProvider<S_config_form> {
    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    //~~ 用于验证配置表单的相关方法,后续需要增加表单引擎(对象)来单独处理,
    //~~ 现在临时使用缓存对象处理。2023-05-20
    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    /**
     * 校验提交的表单是否正确。
     * <pre>1) 当前在功能:分组(组合数据)添加数据项时被调用。</pre>
     * @param formData
     * @date 2023-05-20
     */
    public void validateForm(FormData formData){
        //循环取出item数据, 组合成 key => val 的map格式
        Map<String, String> map = new HashMap<>();
        String formItemValue = null;
        for (FormDataItem formDataItem : formData.getFields()) {
            // 2023-05-21 把value中json转字符串时,包含的:\"去掉。
            // 由于还没有使用到,需要后续评估看用那种方法能获取值。
//            formItemValue = formDataItem.getValue();
//            if(StringUtils.isNotEmpty(formItemValue)){
//                formDataItem.setValue(formItemValue.replaceAll("\"", StringUtils.EMPTY_STRING));
//            }
            // 算法临时缓存
            map.put(formDataItem.getName(), formDataItem.getValue());
        }
        //取出表单模型的数据
        S_config_form form = this.getCacheData(formData.getId().toString());
        //解析表单规则进行验证
        ConfigFormVo systemConfigFormVo = null;
        try {
            systemConfigFormVo = JsonUtils.jsonStringToObject(form.getContent(), ConfigFormVo.class);
        } catch (Exception e) {
            logger.error("formData.getId()=" + formData.getId() + ", " + e.getMessage(), e);
            throw new PlatformRuntimeException("模板表单 【" + form.getName() + "】 内容不正确,请检查JSON格式!", e);
        }
//        ConfigFormItemVo configFormItemVo;
//        for (String item : systemConfigFormVo.getFields()) {
        for (ConfigFormItemVo configFormItemVo : systemConfigFormVo.getFields()) {
//            systemConfigFormItemVo = JSONObject.parseObject(item, SystemConfigFormItemVo.class);
//            try {
//                configFormItemVo = JsonUtils.jsonStringToObject(item, ConfigFormItemVo.class);
//            } catch (Exception e) {
//                throw new PlatformRuntimeException("ConfigFormItemVo转换错误:" + item, e);
//            }
            String model = configFormItemVo.get__vModel__(); //字段 name
            if(configFormItemVo.get__config__().getRequired() && map.get(model).equals(StringUtils.EMPTY_STRING)) {
                throw new PlatformRuntimeException(configFormItemVo.get__config__().getLabel() + "不能为空!");
            }
            //正则验证
            checkRule(configFormItemVo.get__config__().getRegList(), map.get(model),  configFormItemVo.get__config__().getLabel());
        }
    }
    /**
     * 验证item规则
     * @param regList List<SystemConfigFormItemConfigRegListVo 正则表达式列表
     * @param value String 验证的值
     * @param name String 提示语字段名称
     * @since 2020-04-16
     */
    private void checkRule(List<ConfigFormItemConfigRegListVo> regList, String value, String name) {
        if(regList.size() > 0) {
            for (ConfigFormItemConfigRegListVo systemConfigFormItemConfigRegListVo : regList) {
                if(!ConfigFormValidateUtils.regular(value, name, systemConfigFormItemConfigRegListVo.getPattern())) {
                    throw new PlatformRuntimeException(systemConfigFormItemConfigRegListVo.getMessage());
                }
            }
        }
    }
    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ end ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    @Override
    protected int loadDataToCache(Cache cache) {
        List<S_config_form> hosts = this.configFormService.selectAll(new S_config_form());
        if(!StringUtils.isEmptyList(hosts)){
            for(S_config_form h : hosts){
                cache.put(String.valueOf(h.getId()), h);
            }
            return hosts.size();
        }
        return 0;
    }
    @Override
    public String getProviderName() {
        return Constants.CACHE_NAME_FORM;
    }
    @Override
    public Class<?> getProviderType() {
        return S_config_form.class;
    }
    public void setConfigFormService(ConfigFormServiceImpl configFormService) {
        this.configFormService = configFormService;
    }
    private ConfigFormServiceImpl configFormService;
}
iplatform-base/src/main/java/com/iplatform/base/cache/LocalCaptchaCacheProvider.java
New file
@@ -0,0 +1,47 @@
package com.iplatform.base.cache;
import com.iplatform.base.Constants;
import com.walker.cache.AbstractCacheProvider;
import com.walker.cache.Cache;
import java.util.HashMap;
import java.util.Map;
/**
 * 验证码(本地)缓存实现。
 * @author 时克英
 * @date 2022-11-07
 */
public class LocalCaptchaCacheProvider extends AbstractCacheProvider<String> {
    private Map<String, Long> captchaKeyExpiredMap = new HashMap<>();
    public LocalCaptchaCacheProvider(){
        this.runClearDirtyCacheTask();
    }
    @Override
    protected int loadDataToCache(Cache cache) {
        return 0;
    }
    @Override
    public String getProviderName() {
        return Constants.CACHE_NAME_CAPTCHA;
    }
    @Override
    public Class<?> getProviderType() {
        return String.class;
    }
    @Override
    public void putCacheData(String key, String data, long expiredSeconds){
        this.putCacheData(key, data);
        this.captchaKeyExpiredMap.put(key, expiredSeconds);
    }
    private void runClearDirtyCacheTask(){
        logger.info("启动定时任务,定期清理过期的推送临时数据,但暂未实现。");
    }
}
iplatform-base/src/main/java/com/iplatform/base/cache/LocalCategoryCacheProvider.java
New file
@@ -0,0 +1,144 @@
package com.iplatform.base.cache;
import com.iplatform.base.CategoryCacheProvider;
import com.iplatform.base.Constants;
import com.iplatform.base.service.CategoryServiceImpl;
import com.iplatform.base.util.CategoryUtils;
import com.iplatform.base.util.cache.CategorySortComparator;
import com.iplatform.model.po.S_category;
import com.iplatform.model.vo.CategoryTreeVo;
import com.walker.cache.AbstractCacheProvider;
import com.walker.cache.Cachable;
import com.walker.cache.Cache;
import com.walker.infrastructure.utils.StringUtils;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class LocalCategoryCacheProvider extends AbstractCacheProvider<S_category> implements CategoryCacheProvider {
    private final CategorySortComparator sortComparator = new CategorySortComparator();
    @Override
    public List<CategoryTreeVo> getTree(Integer type, Integer status, String name, List<Integer> categoryIdList, int owner) {
        List<S_category> allTree = new ArrayList<>();
        S_category tempCategory = null;
        for(Iterator<Cachable> it = this.getCache().getIterator(); it.hasNext();){
            tempCategory = (S_category) it.next().getValue();
//            if(owner != tempCategory.getOwner().intValue()){
//                continue;
//            }
//            if(type != null && type.intValue() != tempCategory.getType().intValue()){
//                continue;
//            }
//            if(status != null && status.intValue() != tempCategory.getStatus().intValue()){
//                continue;
//            }
//            if(!StringUtils.isEmptyList(categoryIdList) && !categoryIdList.contains(tempCategory.getId())){
//                logger.debug("不包含在给定的集合中'categoryIdList'");
//                continue;
//            }
            if(CategoryUtils.isCondition(tempCategory, type, status, name, categoryIdList, owner)){
                allTree.add(tempCategory);
            }
        }
        return CategoryUtils.acquireListTree(allTree, sortComparator);
//        // 重新排序
//        Collections.sort(allTree, sortComparator);
//
//        //
//        List<CategoryTreeVo> treeList = new ArrayList<>();
//        if(allTree.size() == 0){
//            return treeList;
//        }
//
//        //
//        for (S_category category : allTree) {
//            treeList.add(CategoryUtils.toCategoryTreeVo(category));
//        }
//
//        Map<Integer, CategoryTreeVo> map = new HashMap<>(treeList.size());
//        //ID 为 key 存储到map 中
//        for (CategoryTreeVo categoryTreeVo : treeList) {
//            map.put(categoryTreeVo.getId(), categoryTreeVo);
//        }
//
//        List<CategoryTreeVo> list = new ArrayList<>();
//        CategoryTreeVo parentTree = null;
//        for (CategoryTreeVo tree : treeList) {
//            //子集ID返回对象,有则添加。
//            parentTree = map.get(tree.getPid());
//            if (parentTree != null) {
//                parentTree.getChild().add(tree);
//            } else {
//                list.add(tree);
//            }
//        }
//        return list;
    }
    @Override
    public List<CategoryTreeVo> getListTree(Integer type, Integer status, String name, int owner) {
        if(StringUtils.isNotEmpty(name)){
            try {
                name = URLDecoder.decode(name, StringUtils.DEFAULT_CHARSET_UTF8);
            } catch (UnsupportedEncodingException e) {
                throw new RuntimeException(e);
            }
        }
        return getTree(type, status, name, null, owner);
    }
    @Override
    public S_category get(int id) {
        return this.getCacheData(String.valueOf(id));
    }
    @Override
    public void save(S_category category) {
        this.putCacheData(String.valueOf(category.getId()), category);
    }
    @Override
    public void update(S_category category) {
        this.updateCacheData(String.valueOf(category.getId()), category);
    }
    @Override
    public void remove(int id) {
        this.removeCacheData(String.valueOf(id));
    }
    @Override
    protected int loadDataToCache(Cache cache) {
        List<S_category> data = this.categoryService.selectAll(new S_category());
        if(!StringUtils.isEmptyList(data)){
            for(S_category d : data){
                cache.put(String.valueOf(d.getId()), d);
            }
            return data.size();
        }
        return 0;
    }
    @Override
    public String getProviderName() {
        return Constants.CACHE_NAME_CATEGORY;
    }
    @Override
    public Class<?> getProviderType() {
        return S_category.class;
    }
    public void setCategoryService(CategoryServiceImpl categoryService) {
        this.categoryService = categoryService;
    }
    private CategoryServiceImpl categoryService;
}
iplatform-base/src/main/java/com/iplatform/base/cache/LocalDeptCacheProvider.java
New file
@@ -0,0 +1,146 @@
package com.iplatform.base.cache;
import com.iplatform.base.Constants;
import com.iplatform.base.DeptCacheProvider;
import com.iplatform.base.service.DeptServiceImpl;
import com.iplatform.base.util.DeptUtils;
import com.iplatform.model.po.S_dept;
import com.walker.cache.AbstractCacheProvider;
import com.walker.cache.Cache;
import com.walker.infrastructure.utils.StringUtils;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
 * 基于本地内存实现的机构缓存提供者。
 * @author 时克英
 * @date 2022-12-05
 */
public class LocalDeptCacheProvider extends AbstractCacheProvider<S_dept> implements DeptCacheProvider {
    private DeptServiceImpl deptService;
    private Map<String, List<String>> deptChildrenIdList = new ConcurrentHashMap<>();
    public void setDeptService(DeptServiceImpl deptService) {
        this.deptService = deptService;
    }
    @Override
    public void setAllowCacheChildren(boolean allow) {
    }
    @Override
    public S_dept getDept(long deptId) {
        if(deptId == 0){
            return DeptUtils.SUPER_VISOR_DEPT;
        }
        return this.getCacheData(String.valueOf(deptId));
    }
    @Override
    public void updateDept(S_dept s_dept) {
        this.updateCacheData(String.valueOf(s_dept.getId()), s_dept);
    }
    @Override
    public void removeDept(long deptId) {
        String key = String.valueOf(deptId);
        String keyChildren = key + DeptUtils.KEY_CHILDREN_PREFIX;
        S_dept current = this.getDept(deptId);
        if(current == null){
            logger.warn("机构缓存已不存在,无需删除");
            return;
        }
        // 删除该机构子机构缓存列表
        this.removeCacheData(keyChildren);
        this.removeCacheData(key);
        // 更新父机构对应的childrenList
//        String parentId = String.valueOf(current.getParent_id());
        List<String> parentChildrenList = this.getChildrenDeptIdOneLevel(current.getParent_id());
        if(parentChildrenList != null){
            parentChildrenList.remove(key);
            // 由于在内存中,所以一旦删除,引用也就变了。2022-12-03
//            this.deptChildrenIdList.put(parentId + DeptUtils.KEY_CHILDREN_PREFIX, parentChildrenList);
        }
    }
    @Override
    public void putDept(S_dept s_dept) {
        String id = String.valueOf(s_dept.getId());
        try{
            this.putCacheData(id, s_dept);
            this.deptChildrenIdList.put(id + DeptUtils.KEY_CHILDREN_PREFIX, new ArrayList<>());
            if(s_dept.getParent_id().longValue() == 0L){
                // 顶级单位,没有父机构了
                logger.debug("顶级机构,不用更新父机构列表, " + id);
                return;
            }
            // 更新父机构列表
            List<String> parentChildrenList = this.getChildrenDeptIdOneLevel(s_dept.getParent_id());
            if(StringUtils.isEmptyList(parentChildrenList)){
                parentChildrenList = new ArrayList<>();
            }
            parentChildrenList.add(id);
            this.deptChildrenIdList.put(String.valueOf(s_dept.getParent_id()) + DeptUtils.KEY_CHILDREN_PREFIX, parentChildrenList);
        } catch (Exception e){
            throw new RuntimeException("添加 [机构] 缓存错误:" + e.getMessage(), e);
        }
    }
    @Override
    public List<String> getChildrenDeptIdOneLevel(long deptId) {
        String key = deptId + DeptUtils.KEY_CHILDREN_PREFIX;
        List<String> childrenIdList = this.deptChildrenIdList.get(key);
        if(childrenIdList == null){
            logger.error("未找到缓存中子机构列表: " + key);
            return null;
        }
        return childrenIdList;
    }
    @Override
    public List<S_dept> getChildrenDeptOneLevel(long deptId) {
        List<String> childrenIdList = this.getChildrenDeptIdOneLevel(deptId);
        if(StringUtils.isEmptyList(childrenIdList)){
            return null;
        }
        List<S_dept> resultList = new ArrayList<>();
        S_dept s_dept = null;
        for(String id : childrenIdList){
            s_dept = this.getDept(Long.parseLong(id));
            resultList.add(s_dept);
        }
        return resultList;
    }
    @Override
    protected int loadDataToCache(Cache cache) {
        List<S_dept> list = this.deptService.queryAllDeptListForCache();
        if(!StringUtils.isEmptyList(list)){
            for(S_dept h : list){
                this.putDept(h);
            }
            return list.size();
        }
        return 0;
    }
    @Override
    public String getProviderName() {
        return Constants.CACHE_NAME_DEPT;
    }
    @Override
    public Class<?> getProviderType() {
        return S_dept.class;
    }
}
iplatform-base/src/main/java/com/iplatform/base/cache/LocalHostCacheProvider.java
New file
@@ -0,0 +1,50 @@
package com.iplatform.base.cache;
import com.iplatform.base.Constants;
import com.iplatform.model.po.S_host;
import com.walker.cache.AbstractCacheProvider;
import com.walker.cache.Cache;
import com.walker.infrastructure.utils.StringUtils;
import com.walker.jdbc.service.PubService;
import java.util.List;
/**
 * 主机资源缓存定义(本机内存缓存)实现。
 * @author 时克英
 * @date 2022-09-20
 */
public class LocalHostCacheProvider extends AbstractCacheProvider<S_host> {
    private PubService pubService;
    public void setPubService(PubService pubService) {
        this.pubService = pubService;
    }
    public LocalHostCacheProvider(){
        this.setLoadPage(false);
    }
    @Override
    protected int loadDataToCache(Cache cache) {
        List<S_host> hosts = this.pubService.selectAll(new S_host());
        if(!StringUtils.isEmptyList(hosts)){
            for(S_host h : hosts){
                cache.put(String.valueOf(h.getId()), h);
            }
            return hosts.size();
        }
        return 0;
    }
    @Override
    public String getProviderName() {
        return Constants.CACHE_NAME_HOST;
    }
    @Override
    public Class<?> getProviderType() {
        return S_host.class;
    }
}
iplatform-base/src/main/java/com/iplatform/base/cache/LocalNotificationTemplateCache.java
New file
@@ -0,0 +1,88 @@
package com.iplatform.base.cache;
import com.iplatform.base.Constants;
import com.iplatform.base.NotificationTemplateCache;
import com.iplatform.base.NotifyConstants;
import com.iplatform.base.service.NotificationServiceImpl;
import com.iplatform.model.po.SfNotification;
import com.iplatform.model.po.SfTemplateMessage;
import com.iplatform.model.vo.NotificationTemplateVo;
import com.walker.cache.AbstractCacheProvider;
import com.walker.cache.Cache;
import com.walker.infrastructure.utils.StringUtils;
import java.util.List;
import java.util.Map;
public class LocalNotificationTemplateCache extends AbstractCacheProvider<NotificationTemplateVo> implements NotificationTemplateCache {
    @Override
    public NotificationTemplateVo get(String mark) {
        return this.getCacheData(mark);
    }
    @Override
    public void save(NotificationTemplateVo category) {
        this.putCacheData(category.getName(), category);
    }
    @Override
    public void update(NotificationTemplateVo category) {
        this.updateCacheData(category.getName(), category);
    }
    @Override
    public void remove(String mark) {
        this.removeCacheData(mark);
    }
    @Override
    protected int loadDataToCache(Cache cache) {
        List<SfNotification> notificationList = this.notificationService.selectAll(new SfNotification());
        if(!StringUtils.isEmptyList(notificationList)){
            Map<Long, SfTemplateMessage> templateMessageMap = this.notificationService.queryTemplateIdMap();
            if(templateMessageMap.size() == 0){
                logger.warn("提醒模板未找到任何有效数据");
                return 0;
            }
            for(SfNotification h : notificationList){
                NotificationTemplateVo vo = new NotificationTemplateVo();
                vo.setName(h.getMark());
                if(h.getIsWechat().intValue() == NotifyConstants.SWITCH_OPEN){
                    vo.setWechat(true);
                    vo.setWechatId(h.getWechatId());
                    vo.setWechatTempId(templateMessageMap.get(h.getWechatId().longValue()).getTempId());
                }
                if(h.getIsRoutine().intValue() == NotifyConstants.SWITCH_OPEN){
                    vo.setRoutine(true);
                    vo.setRoutineId(h.getRoutineId());
                    vo.setRoutineTempId(templateMessageMap.get(h.getRoutineId().longValue()).getTempId());
                }
                if(h.getIsSms().intValue() == NotifyConstants.SWITCH_OPEN){
                    vo.setSms(true);
                    vo.setSmsId(h.getSmsId());
                    vo.setSmsTempId(templateMessageMap.get(h.getSmsId().longValue()).getTempId());
                }
                cache.put(h.getMark(), vo);
            }
            return notificationList.size();
        }
        return 0;
    }
    @Override
    public String getProviderName() {
        return Constants.CACHE_NAME_NOTIFICATION_TEMPLATE;
    }
    @Override
    public Class<?> getProviderType() {
        return NotificationTemplateVo.class;
    }
    public void setNotificationService(NotificationServiceImpl notificationService) {
        this.notificationService = notificationService;
    }
    private NotificationServiceImpl notificationService;
}
iplatform-base/src/main/java/com/iplatform/base/cache/LocalPushCacheProvider.java
New file
@@ -0,0 +1,43 @@
package com.iplatform.base.cache;
import com.iplatform.base.Constants;
import com.iplatform.base.PushCacheProvider;
import com.walker.cache.AbstractCacheProvider;
import com.walker.cache.Cache;
public class LocalPushCacheProvider extends AbstractCacheProvider<String> implements PushCacheProvider {
    public LocalPushCacheProvider(){
        this.runClearDirtyCacheTask();
    }
    @Override
    protected int loadDataToCache(Cache cache) {
        // 初始化不需要加载数据,缓存中数据2分钟失效
        return 0;
    }
    @Override
    public String getProviderName() {
        return Constants.CACHE_NAME_PUSH;
    }
    @Override
    public Class<?> getProviderType() {
        return String.class;
    }
    @Override
    public void put(String key, String value) {
        this.putCacheData(key, value, 60);
    }
    @Override
    public String get(String key) {
        return this.getCacheData(key);
    }
    private void runClearDirtyCacheTask(){
        logger.info("启动定时任务,定期清理过期的用户登录信息,但暂未实现。");
    }
}
iplatform-base/src/main/java/com/iplatform/base/cache/LocalSystemGroupCache.java
New file
@@ -0,0 +1,81 @@
package com.iplatform.base.cache;
import com.iplatform.base.Constants;
import com.iplatform.base.SystemGroupCache;
import com.iplatform.base.service.GroupServiceImpl;
import com.iplatform.model.po.S_group;
import com.iplatform.model.po.S_group_data;
import com.iplatform.model.vo.SystemGroupVo;
import com.walker.cache.AbstractCacheProvider;
import com.walker.cache.Cache;
import com.walker.infrastructure.utils.StringUtils;
import java.util.ArrayList;
import java.util.List;
public class LocalSystemGroupCache extends AbstractCacheProvider<SystemGroupVo> implements SystemGroupCache {
    @Override
    public SystemGroupVo get(int id) {
        return this.getCacheData(String.valueOf(id));
    }
    @Override
    public void save(SystemGroupVo category) {
        this.putCacheData(String.valueOf(category.getId()), category);
    }
    @Override
    public void update(SystemGroupVo category) {
        this.updateCacheData(String.valueOf(category.getId()), category);
    }
    @Override
    public void remove(int id) {
        this.removeCacheData(String.valueOf(id));
    }
    @Override
    protected int loadDataToCache(Cache cache) {
        List<S_group> hosts = this.groupService.selectAll(new S_group());
        if(!StringUtils.isEmptyList(hosts)){
            List<SystemGroupVo> groupVoList = new ArrayList<>(hosts.size());
            List<S_group_data> groupDataList = this.groupService.queryAllGroupDataList();
            for(S_group h : hosts){
                groupVoList.add(new SystemGroupVo(h));
            }
            if(!StringUtils.isEmptyList(groupDataList)){
                for(S_group_data data : groupDataList){
                    for(SystemGroupVo vo : groupVoList){
                        if(data.getGid().intValue() == vo.getId().intValue()){
                            vo.addGroupData(data);
                        }
                    }
                }
            }
            for(SystemGroupVo h : groupVoList){
                cache.put(String.valueOf(h.getId()), h);
            }
            return hosts.size();
        }
        return 0;
    }
    @Override
    public String getProviderName() {
        return Constants.CACHE_NAME_GROUP;
    }
    @Override
    public Class<?> getProviderType() {
        return SystemGroupVo.class;
    }
    public void setGroupService(GroupServiceImpl groupService) {
        this.groupService = groupService;
    }
    private GroupServiceImpl groupService;
}
iplatform-base/src/main/java/com/iplatform/base/cache/LocalUserCacheProvider.java
New file
@@ -0,0 +1,81 @@
package com.iplatform.base.cache;
import com.iplatform.base.Constants;
import com.iplatform.base.UserCacheProvider;
import com.iplatform.base.service.UserServiceImpl;
import com.iplatform.model.po.S_user_core;
import com.walker.cache.AbstractCacheProvider;
import com.walker.cache.Cache;
import com.walker.infrastructure.utils.StringUtils;
import java.util.List;
/**
 * 本地用户缓存实现。
 * @author 时克英
 * @date 2022-11-06
 */
public class LocalUserCacheProvider extends AbstractCacheProvider<S_user_core> implements UserCacheProvider {
    private UserServiceImpl userService = null;
    public void setUserService(UserServiceImpl userService) {
        this.userService = userService;
    }
    @Override
    protected int loadDataToCache(Cache cache) {
        List<S_user_core> hosts = this.userService.selectAll(new S_user_core());
        if(!StringUtils.isEmptyList(hosts)){
            for(S_user_core h : hosts){
                cache.put(String.valueOf(h.getId()), h);
            }
            return hosts.size();
        }
        return 0;
    }
    @Override
    public String getProviderName() {
        return Constants.CACHE_NAME_USER;
    }
    @Override
    public Class<?> getProviderType() {
        return S_user_core.class;
    }
    @Override
    public S_user_core getUserByLoginId(String loginId) {
        S_user_core userCore = this.getCacheData(loginId);
        if(userCore == null){
            userCore = this.userService.queryLoginUserOnly(loginId);
            if(userCore == null){
                logger.warn("从s_user_core未查询到用户" + ",无法缓存,loginId = {}", loginId);
                return null;
            }
            this.putCacheData(loginId, userCore);
        }
        return userCore;
    }
    @Override
    public S_user_core getUser(long userId) {
        return this.getCacheData(String.valueOf(userId));
    }
    @Override
    public void updateUser(S_user_core user_core) {
        this.updateCacheData(String.valueOf(user_core.getId()), user_core);
    }
    @Override
    public void removeUser(long userId) {
        this.removeCacheData(String.valueOf(userId));
    }
    @Override
    public void putUser(S_user_core user_core) {
        this.putCacheData(String.valueOf(user_core.getId()), user_core);
    }
}
iplatform-base/src/main/java/com/iplatform/base/cache/LocalUserLoginCache.java
New file
@@ -0,0 +1,46 @@
package com.iplatform.base.cache;
import com.iplatform.base.Constants;
import com.iplatform.base.UserLoginCache;
import com.iplatform.model.po.S_user_login;
import com.walker.cache.AbstractCacheProvider;
import com.walker.cache.Cache;
public class LocalUserLoginCache extends AbstractCacheProvider<S_user_login> implements UserLoginCache {
    @Override
    public S_user_login getUserLogin(String loginId) {
        return this.getCacheData(loginId);
    }
    @Override
    public void updateUserLogin(S_user_login user_login) {
        this.updateCacheData(user_login.getUser_name(), user_login);
    }
    @Override
    public void removeUserLogin(String loginId) {
        this.removeCacheData(loginId);
    }
    @Override
    public void putUserLogin(S_user_login user_login) {
        this.putCacheData(user_login.getUser_name(), user_login);
    }
    @Override
    protected int loadDataToCache(Cache cache) {
        // 本地缓存情况下,不考虑重启后加载缓存。
        return 0;
    }
    @Override
    public String getProviderName() {
        return Constants.CACHE_NAME_USER_LOGIN;
    }
    @Override
    public Class<?> getProviderType() {
        return S_user_login.class;
    }
}
iplatform-base/src/main/java/com/iplatform/base/cache/LocalUserOnlineProvider.java
New file
@@ -0,0 +1,88 @@
package com.iplatform.base.cache;
import com.iplatform.base.Constants;
import com.iplatform.base.VariableConstants;
import com.iplatform.base.util.TokenUtils;
import com.walker.cache.AbstractCacheProvider;
import com.walker.cache.Cache;
import com.walker.web.UserOnlineProvider;
import com.walker.web.UserPrincipal;
import java.util.HashMap;
import java.util.Map;
/**
 * 基于内存实现的在线用户提供者实现。
 * @author 时克英
 * @date 2022-11-01
 */
public class LocalUserOnlineProvider extends AbstractCacheProvider<UserPrincipal<?>> implements UserOnlineProvider {
    // 存储用户: key --> 登录过期时间
    private Map<String, Long> captchaKeyExpiredMap = new HashMap<>();
    public LocalUserOnlineProvider(){
        this.runClearDirtyCacheTask();
    }
    @Override
    protected int loadDataToCache(Cache cache) {
        // 内存无法持久化,因此这里无需启动时加载已有缓存
        return 0;
    }
    @Override
    public String getProviderName() {
        return Constants.CACHE_NAME_ONLINE_USER;
    }
    @Override
    public Class<?> getProviderType() {
        return UserPrincipal.class;
    }
    @Override
    public UserPrincipal<?> getUserPrincipal(String token) {
        UserPrincipal<?> userPrincipal = this.getCacheData(token);
        if(userPrincipal == null){
            logger.warn("UserOnlineProvider 缓存用户不存在, token = " + token);
        }
        return userPrincipal;
    }
    @Deprecated
    @Override
    public boolean cacheUserPrincipal(String token, UserPrincipal<?> userPrincipal) {
        logger.debug("写入用户登录缓存: " + token + ", " + userPrincipal.getUserName());
        this.putCacheData(token, userPrincipal, TokenUtils.acquireCacheUserExpiredSeconds(VariableConstants.DEFAULT_TOKEN_EXPIRED_MINUTES));
        return true;
    }
    @Override
    public boolean cacheUserPrincipal(String token, UserPrincipal<?> userPrincipal, long expiredMintues) {
        this.putCacheData(token, userPrincipal, TokenUtils.acquireCacheUserExpiredSeconds(expiredMintues));
        return true;
    }
    @Override
    public boolean updateUserPrincipalTimeStamp(String token) {
        return false;
    }
    @Override
    public boolean removeUserPrincipal(String token) {
        this.removeCacheData(token);
        this.captchaKeyExpiredMap.remove(token);
        return true;
    }
    @Override
    public void putCacheData(String key, UserPrincipal<?> userPrincipal, long expiredSeconds){
        this.putCacheData(key, userPrincipal);
        this.captchaKeyExpiredMap.put(key, expiredSeconds);
    }
    private void runClearDirtyCacheTask(){
        logger.info("启动定时任务,定期清理过期的用户登录信息,但暂未实现。");
    }
}
iplatform-base/src/main/java/com/iplatform/base/cache/LocalWechatCache.java
New file
@@ -0,0 +1,63 @@
package com.iplatform.base.cache;
import com.iplatform.base.Constants;
import com.iplatform.base.WechatCacheProvider;
import com.iplatform.base.WechatConstants;
import com.walker.cache.AbstractCacheProvider;
import com.walker.cache.Cache;
public class LocalWechatCache extends AbstractCacheProvider<String> implements WechatCacheProvider {
    @Override
    public String getMiniAccessToken() {
        return this.getCacheData(WechatConstants.REDIS_WECAHT_MINI_ACCESS_TOKEN_KEY);
    }
    @Override
    public String getJsApiTicket() {
        return this.getCacheData(WechatConstants.REDIS_PUBLIC_JS_API_TICKET);
    }
    @Override
    public String getPublicAccessToken() {
//        throw new UnsupportedOperationException("请使用Redis实现");
        return this.getCacheData(WechatConstants.REDIS_WECAHT_PUBLIC_ACCESS_TOKEN_KEY);
    }
    @Override
    public void putPublicAccessToken(String value, long expiredSeconds) {
//        throw new UnsupportedOperationException("请使用Redis实现");
        this.putCacheData(WechatConstants.REDIS_WECAHT_PUBLIC_ACCESS_TOKEN_KEY, value, expiredSeconds);
    }
    @Override
    public void putJsApiTicket(String value) {
//        throw new UnsupportedOperationException("请使用Redis实现");
        this.putCacheData(WechatConstants.REDIS_PUBLIC_JS_API_TICKET, value, WechatConstants.REDIS_PUBLIC_JS_API_TICKET_EXPRESS);
    }
    @Override
    public void putMiniAccessToken(String value, long expiredSeconds) {
        this.putCacheData(WechatConstants.REDIS_WECAHT_MINI_ACCESS_TOKEN_KEY, value, expiredSeconds);
    }
    @Override
    public void removeMiniAccessToken() {
        this.removeCacheData(WechatConstants.REDIS_WECAHT_MINI_ACCESS_TOKEN_KEY);
    }
    @Override
    protected int loadDataToCache(Cache cache) {
        return 0;
    }
    @Override
    public String getProviderName() {
        return Constants.CACHE_NAME_WECHAT;
    }
    @Override
    public Class<?> getProviderType() {
        return String.class;
    }
}
iplatform-base/src/main/java/com/iplatform/base/cache/MenuCacheProvider.java
New file
@@ -0,0 +1,597 @@
package com.iplatform.base.cache;
import com.iplatform.base.Constants;
import com.iplatform.base.SecurityConstants;
import com.iplatform.base.service.MenuServiceImpl;
import com.iplatform.base.util.MenuUtils;
import com.iplatform.base.util.menu.SystemMenu;
import com.iplatform.model.po.S_menu;
import com.iplatform.model.vo.MetaVo;
import com.iplatform.model.vo.RouterVo;
import com.walker.cache.AbstractCacheProvider;
import com.walker.cache.Cachable;
import com.walker.cache.Cache;
import com.walker.infrastructure.utils.StringUtils;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
/**
 * 菜单本地缓存,因为不需要集群环境,因此只有内存模式(没有redis方式)
 * @author 时克英
 * @date 2022-11-01
 * @date 2024-04-09 增加修改时间戳,在集群环境中能被动触发重新加载本机节点。
 */
public class MenuCacheProvider extends AbstractCacheProvider<S_menu> {
//public class MenuCacheProvider extends RedisCacheProvider<S_menu> {
/*    public MenuCacheProvider(){
        this.setUseRedis(true);
        this.setLoadPage(false);
        // 缓存可展示最大值,超过该值调用getIterator会报错
        CacheConfig.setInitCachemapSize(20480);
    }*/
    private MenuServiceImpl menuService;
    public void setMenuService(MenuServiceImpl menuService) {
        this.menuService = menuService;
    }
    @Override
    protected int loadDataToCache(Cache cache) {
        List<S_menu> list = this.menuService.selectAll(new S_menu());
        if(!StringUtils.isEmptyList(list)){
            for(S_menu h : list){
                cache.put(h.getMenu_id(), h);
            }
            // 每次启动,记录当前加载数据时间戳
            this.reloadMills = System.currentTimeMillis();
            return list.size();
        }
        return 0;
    }
    @Override
    public String getProviderName() {
        return Constants.CACHE_NAME_MENU;
    }
    @Override
    public Class<?> getProviderType() {
        return S_menu.class;
    }
    /**
     * 给定的菜单,是否包含子菜单。
     * @param menuId 给定菜单ID
     * @return
     * @date 2022-12-29
     */
    public boolean isHasChildren(String menuId){
        List<S_menu> menuAll = this.getCacheMenuList(true, MenuUtils.MENU_SCOPE_PLATFORM);
        for(S_menu menu : menuAll){
            if(menu.getParent_id().equals(menuId)){
                return true;
            }
        }
        return false;
    }
    /**
     * 返回菜单集合列表(不是树结构),目前该方法在代码生成功能中使用,展示选择生成在哪个菜单模块。
     * @param roleIdList
     * @param menuScope 菜单范围:0 平台(目前也有3),4 独立单位, -1 表示显示所有菜单,>4 表示机构特定菜单,如班主任菜单。
     * @return
     * @date 2022-11-27
     * @date 2023-06-05 添加参数 menuScope
     * @date 2023-10-11 menuScope 参数增加值(-1)可以展示所有菜单,邮政集团模式
     * @date 2023-10-24 menuScope 参数增加值(大于4),表示特定人员的特定菜单(如:班主任的菜单)
     */
    public List<SystemMenu> getMenuList(List<String> roleIdList, int menuScope){
        if(StringUtils.isEmptyList(roleIdList)){
            // 没有给定角色集合,说明是管理员,显示所有菜单数据
            return this.toSystemMenuList(this.getCacheMenuList(true, menuScope));
        }
        // 2024-04-09
        this.checkReloadForceLocalMemory();
        // 根据角色权限加载不同菜单集合
        List<SystemMenu> menuList = new ArrayList<>(32);
        List<String> menuIdList = this.menuService.queryRoleMenuIdList(roleIdList);
        if(StringUtils.isEmptyList(menuIdList)){
            logger.warn("用户没有任何权限,menuIdList = null, roleIdList = {}", roleIdList);
            return menuList;
        }
        S_menu menu = null;
        for(String menuId : menuIdList){
            menu = this.getCacheData(menuId);
            // 2023-10-11 增加这个if判断,如果是不区分范围,则加载所有菜单
//            if(menuScope != MenuUtils.MENU_SCOPE_ALL){
//                if(menuScope == MenuUtils.MENU_SCOPE_PLATFORM){
//                    // 如果要展示菜单范围是:平台,则菜单中 >= 4的分类都不要,这些是顶级机构业务独立菜单。
//                    if(menu.getType().intValue() >= MenuUtils.MENU_SCOPE_ORG){
//                        continue;
//                    }
//                } else if(menuScope == MenuUtils.MENU_SCOPE_ORG){
////                    if(menu.getType().intValue() < MenuUtils.MENU_SCOPE_ORG){
//                    if(menu.getType().intValue() != MenuUtils.MENU_SCOPE_ORG){
//                        // 如果要展示菜单范围是:顶级机构业务的独立菜单,则平台菜单不显示。2023-06-01
//                        continue;
//                    }
//                } else if(menuScope > MenuUtils.MENU_SCOPE_ORG){
//                    // 对于大于机构菜单的,都属于机构特定菜单,必须与传入范围完全一致,如:班主任菜单等。2023-10-24
//                    if(menu.getType().intValue() != menuScope){
//                        continue;
//                    }
//                }
//            }
            if(MenuUtils.menuScopeNotMatch(menuScope, menu)){
                continue;
            }
            menuList.add(new SystemMenu(menu));
        }
        // 2023-10-13,查找每个菜单上级,如果上级范围类型与下级菜单不一致,也需要加入列表中
        for(String menuId : menuIdList){
            if(MenuUtils.containMenu(menuList, menuId)){
                continue;
            }
            menu = this.getCacheData(menuId);
            menuList.add(new SystemMenu(menu));
        }
//        if(!StringUtils.isEmptyList(menuList)){
//            String pid = null;
//            for(SystemMenu systemMenu : menuList){
//                pid = systemMenu.getParent_id();
//                if(pid.equals(MenuUtils.MENU_ID_ROOT)){
//                    continue;
//                }
//                if(menuIdList.contains(pid)){
//                    this.addParentMenu2Collection(menuList, pid);
//                }
//            }
//        }
//        Collections.sort(menuList, new ParentMenuComparator());
//        Collections.sort(menuList, new MenuOrderNumComparator());
//        Collections.sort(menuList);
        List<SystemMenu> collect = menuList.stream().sorted(Comparator.comparing(SystemMenu::getParent_id).thenComparing(SystemMenu::getOrder_num,Comparator.reverseOrder())).collect(Collectors.toList());
        logger.info("+++++++++++++++++++++");
        return collect;
    }
//    private List<SystemMenu> addParentMenu2Collection(List<SystemMenu> menuList, String menuId){
//        List<SystemMenu> addList = null;
//        boolean exist = false;
//        for(SystemMenu menu : menuList){
//            if(menu.getMenu_id().equals(menuId)){
//                exist = true;
//                break;
//            }
//        }
//        if(!exist){
//            S_menu menu = this.getCacheData(menuId);
//            if(addList == null){
//                addList = new ArrayList<>(8);
//            }
//            addList.add(new SystemMenu(menu));
//        }
//        return addList;
//    }
    public List<SystemMenu> toSystemMenuList(List<S_menu> srcMenuList){
        List<SystemMenu> list = new ArrayList<>(32);
        if(StringUtils.isEmptyList(srcMenuList)){
            return list;
        }
        for(S_menu menu : srcMenuList){
            list.add(new SystemMenu(menu));
        }
        return list;
    }
    /**
     * 获得缓存菜单列表
     * @param containButton 是否包含按钮权限菜单
     * @param menuScope 菜单范围,对独立顶级机构,可以显示机构设置的分类菜单范围,目前有:0-平台(包含3),4-顶级单位独立菜单
     * @return
     * @date 2023-06-01
     */
    public List<S_menu> getCacheMenuList(boolean containButton, int menuScope){
        // 2024-04-09
        this.checkReloadForceLocalMemory();
        List<S_menu> data = new ArrayList<>();
        S_menu menu = null;
        for(Iterator<Cachable> it = this.getCache().getIterator(); it.hasNext();){
            menu = (S_menu) it.next().getValue();
            // 2023-10-24
            if(MenuUtils.menuScopeNotMatch(menuScope, menu)){
                continue;
            }
            if(menu.getStatus().intValue() == MenuUtils.MENU_STATUS_DISABLED){
                // 去掉禁用菜单
                continue;
            }
            if(menu.getMenu_type().equals(MenuUtils.MENU_TYPE_BUTTON)){
                if(containButton){
                    data.add(menu);
                }
            } else {
                data.add(menu);
            }
        }
        // TODO 前端报错 暂时注释
//        Collections.sort(data, new ParentMenuComparator());
//        Collections.sort(data, new MenuOrderNumComparator());
//        Collections.sort(data);
        List<S_menu> dataOrder = data.stream().sorted(Comparator.comparing(S_menu::getParent_id).thenComparing(S_menu::getOrder_num,Comparator.reverseOrder())).collect(Collectors.toList());
        return dataOrder;
    }
    /**
     * 根据父节点的ID获取所有子节点
     *
     * @param list 分类表
     * @param parentId 传入的父节点ID
     * @return String
     */
    public List<SystemMenu> getChildPerms(List<SystemMenu> list, String parentId) {
        List<SystemMenu> returnList = new ArrayList<>();
        for (Iterator<SystemMenu> iterator = list.iterator(); iterator.hasNext();) {
            SystemMenu t = iterator.next();
            // 一、根据传入的某个父节点ID,遍历该父节点的所有子节点
            if (t.getParent_id().equals(parentId)) {
                recursionFn(list, t);
                returnList.add(t);
            }
        }
        return returnList;
    }
    /**
     * 递归列表
     *
     * @param list
     * @param t
     */
    private void recursionFn(List<SystemMenu> list, SystemMenu t) {
        // 得到子节点列表
        List<SystemMenu> childList = getChildList(list, t);
        t.setChildren(childList);
        for (SystemMenu tChild : childList) {
            if (hasChild(list, tChild)) {
                recursionFn(list, tChild);
            }
        }
    }
    /**
     * 得到子节点列表
     */
    private List<SystemMenu> getChildList(List<SystemMenu> list, SystemMenu t) {
        List<SystemMenu> tlist = new ArrayList<>();
        Iterator<SystemMenu> it = list.iterator();
        while (it.hasNext()) {
            SystemMenu n = it.next();
            if (n.getParent_id().equals(t.getMenu_id())) {
                tlist.add(n);
            }
        }
        return tlist;
    }
    /**
     * 判断是否有子节点
     */
    private boolean hasChild(List<SystemMenu> list, SystemMenu t) {
        return getChildList(list, t).size() > 0;
    }
    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    //~ 以下为用户登录菜单展示使用,2022-11-12
    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    /**
     * 返回菜单权限标识字符串集合,该方法若依前端使用。<p></p>
     * 后续会去掉前端对权限点的依赖。
     * @param menuIdList
     * @param showAll 是否显示所有权限点
     * @param menuScope 菜单范围,对独立顶级机构,可以显示机构设置的分类菜单范围,目前有:0-平台(包含3),4-顶级单位独立菜单
     * @return
     * @date 2022-11-12
     * @date 2023-03-20 如果给定菜单id为空,则不返回任何权限点信息。时克英
     * @date 2023-06-07 对于独立单位(商户)则会显示所有该单位的菜单权限信息
     */
    public Set<String> getPermissionSet(List<String> menuIdList, boolean showAll, int menuScope){
        // 2024-04-09
        this.checkReloadForceLocalMemory();
        if(StringUtils.isEmptyList(menuIdList) && !showAll){
            return new HashSet<>();
        }
        List<String> permsList = new ArrayList<>(64);
//        List<S_menu> menuAll = this.getCacheMenuList(true, MenuUtils.MENU_SCOPE_PLATFORM);
        List<S_menu> menuAll = this.getCacheMenuList(true, menuScope);
        if(!StringUtils.isEmptyList(menuAll)){
            for(S_menu menu : menuAll){
                // 2023-10-11 增加这个if判断,如果是不区分范围,则加载所有菜单
//                if(menuScope != MenuUtils.MENU_SCOPE_ALL){
//
//                    // 2023-06-07
//                    if(menuScope == MenuUtils.MENU_SCOPE_PLATFORM){
//                        // 如果要展示菜单范围是:平台,则菜单中 >= 4的分类都不要,这些是顶级机构业务独立菜单。
//                        if(menu.getType().intValue() >= MenuUtils.MENU_SCOPE_ORG){
//                            continue;
//                        }
//                    } else if(menu.getType().intValue() < MenuUtils.MENU_SCOPE_ORG){
//                        // 如果要展示菜单范围是:顶级机构业务的独立菜单,则平台菜单不显示。2023-06-01
//                        continue;
//                    }
//                }
                // 2023-10-24
                if(MenuUtils.menuScopeNotMatch(menuScope, menu)){
                    continue;
                }
                if(StringUtils.isEmpty(menu.getPerms())){
                    continue;
                }
                if(StringUtils.isEmptyList(menuIdList)){
                    // 如果没有限制,全部加上
                    permsList.add(menu.getPerms());
                } else if(menuIdList.contains(menu.getMenu_id())){
                    // 如果限制有,则只添加给定的
                    permsList.add(menu.getPerms());
                }
            }
        }
        Set<String> permsSet = new HashSet<>();
        for(String perms : permsList){
            if (StringUtils.isNotEmpty(perms)) {
                permsSet.addAll(Arrays.asList(perms.trim().split(",")));
            }
        }
        return permsSet;
    }
    /**
     * 返回菜单树形结果,列表中包含多个菜单根节点,menuIdList 为空时,允许返回所有的。
     * @param menuIdList
     * @param containButton
     * @return
     * @date 2023-03-22
     */
    public List<SystemMenu> getMenuTreeAll(List<String> menuIdList, boolean containButton, int menuScope){
        return this.getMenuTree(menuIdList, containButton, true, menuScope);
    }
    /**
     * 返回菜单树形结果,列表中包含多个菜单根节点。
     * @param menuIdList 指定显示的菜单,如果为空表示不特别限制
     * @param containButton 是否包含按钮权限
     * @param showAll 当给定'menuIdList'为空时,是否显示所有菜单
     * @return
     * @date 2022-12-18 更新
     * @date 2023-03-22 添加参数'showAll'
     */
    public List<SystemMenu> getMenuTree(List<String> menuIdList, boolean containButton, boolean showAll, int menuScope){
        List<SystemMenu> menuGroupList = new ArrayList<>(32);
        if(StringUtils.isEmptyList(menuIdList) && !showAll){
            // 2023-03-22 给定为空菜单id,并且不允许显示所有,返回空菜单树。
            return getChildPerms(menuGroupList, "0");
        }
//        List<S_menu> menuAll = this.getCacheMenuList(false);
        List<S_menu> menuAll = this.getCacheMenuList(containButton, menuScope);
        if(!StringUtils.isEmptyList(menuAll)){
            for(S_menu menu : menuAll){
                if(menuIdList == null){
                    // 如果没有限制,全部加上
                    menuGroupList.add(new SystemMenu(menu));
                } else if(menuIdList.contains(menu.getMenu_id())){
                    // 如果限制有,则只添加给定的
                    menuGroupList.add(new SystemMenu(menu));
                }
            }
        }
        List<SystemMenu> menuGroupListOrder = menuGroupList.stream().sorted(Comparator.comparing(SystemMenu::getParent_id).thenComparing(SystemMenu::getOrder_num,Comparator.reverseOrder())).collect(Collectors.toList());
        return getChildPerms(menuGroupListOrder, "0");
    }
    /**
     * 构建前端路由所需要的菜单(若依)
     *
     * @param menus 菜单列表
     * @return 路由列表
     */
    @Deprecated
    public List<RouterVo> buildMenus(List<SystemMenu> menus) {
        List<RouterVo> routers = new LinkedList<RouterVo>();
        for (SystemMenu menu : menus) {
            RouterVo router = new RouterVo();
            router.setHidden(MenuUtils.MENU_INVISIBLE.equals(menu.getVisible()));
            router.setName(MenuUtils.getRouteName(menu));
            router.setPath(MenuUtils.getRouterPath(menu));
            router.setComponent(MenuUtils.getComponent(menu));
            router.setQuery(menu.getQuery());
            router.setMeta(new MetaVo(menu.getMenu_name(), menu.getIcon(), menu.getIs_cache().intValue() == MenuUtils.MENU_CACHE_DISABLE, menu.getPath()));
            List<SystemMenu> cMenus = menu.getChildren();
            if (!cMenus.isEmpty() && cMenus.size() > 0 && menu.getMenu_type().equals(MenuUtils.MENU_TYPE_FOLDER)) {
                router.setAlwaysShow(true);
                router.setRedirect("noRedirect");
                router.setChildren(buildMenus(cMenus));
            }
            else if (MenuUtils.isMenuFrame(menu)) {
                router.setMeta(null);
                List<RouterVo> childrenList = new ArrayList<RouterVo>();
                RouterVo children = new RouterVo();
                children.setPath(menu.getPath());
                children.setComponent(menu.getComponent());
                children.setName(StringUtils.capitalize(menu.getPath()));
                children.setMeta(new MetaVo(menu.getMenu_name(), menu.getIcon(), menu.getIs_cache().intValue() ==  MenuUtils.MENU_CACHE_DISABLE, menu.getPath()));
                children.setQuery(menu.getQuery());
                childrenList.add(children);
                router.setChildren(childrenList);
            }
            else if (menu.getParent_id().equals(MenuUtils.MENU_ID_ROOT) && MenuUtils.isInnerLink(menu)) {
                router.setMeta(new MetaVo(menu.getMenu_name(), menu.getIcon()));
                router.setPath("/");
                List<RouterVo> childrenList = new ArrayList<RouterVo>();
                RouterVo children = new RouterVo();
                String routerPath = MenuUtils.innerLinkReplaceEach(menu.getPath());
                children.setPath(routerPath);
                children.setComponent(MenuUtils.INNER_LINK);
                children.setName(StringUtils.capitalize(routerPath));
                children.setMeta(new MetaVo(menu.getMenu_name(), menu.getIcon(), menu.getPath()));
                childrenList.add(children);
                router.setChildren(childrenList);
            }
            routers.add(router);
        }
        return routers;
    }
    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    //~ 以下为系统权限管理使用,2022-11-02
    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    /**
     * 返回系统所有角色与url关联数据,security权限配置实用。<p></p>
     * 该方法会有重复数据,例如:多个角色具有相同的URL,由业务去除重复。<p></p>
     * list[0] = role_id, list[1] = url <p></p>
     * 因为Map对象相同key会覆盖value,所以换成List集合返回。
     * @return
     * @date 2022-11-02
     */
    public List<String[]> getAllRoleMenuMap(){
        List<String[]> result = new ArrayList<>();
        List<Map<String, Object>> list = this.menuService.queryRolesPermList();
        if(StringUtils.isEmptyList(list)){
            return result;
        }
        String perms = null;
        String menuId = null;
        S_menu menu = null;
        for(Map<String, Object> map : list){
            menuId = map.get("menu_id").toString();
            menu = this.getCacheData(menuId);
            if(menu == null){
                throw new IllegalArgumentException("缓存中未找到菜单:" + menuId);
            }
            perms = menu.getPerms();
            if(StringUtils.isEmpty(perms)){
                continue;
            }
//            result.put(map.get("role_id").toString(), MenuUtils.acquireUrlFromPerms(perms));
            result.add(new String[]{map.get("role_id").toString(), MenuUtils.acquireUrlFromPerms(perms)});
        }
        // 2023-06-07
        // 商户(顶级机构)特有的菜单,也需要加入到角色中,设置默认三方角色:ROLE_MERCHANT
        Set<String> merchantPermSet = this.getPermissionSet(null, true, MenuUtils.MENU_SCOPE_ORG);
        if(merchantPermSet != null){
            for(String onePerms : merchantPermSet){
                result.add(new String[]{SecurityConstants.ROLE_MERCHANT, MenuUtils.acquireUrlFromPerms(onePerms)});
            }
            logger.debug("添加商户(顶级机构)角色权限:{}", merchantPermSet.size());
        }
        return result;
    }
    /**
     * 返回系统所有菜单包含权限的url集合,即:所有perms存在的菜单。
     * @return
     * @date 2022-11-02
     */
    public List<String> getAllMenuUrlList(){
        // 2024-04-09
        this.checkReloadForceLocalMemory();
        List<String> urlList = new ArrayList<>(32);
        S_menu menu = null;
        String perms = null;
        for(Iterator<Cachable> it = this.getCache().getIterator(); it.hasNext();){
            menu = (S_menu) it.next().getValue();
            perms = menu.getPerms();
            if(StringUtils.isEmpty(perms)){
                continue;
            }
            urlList.add(MenuUtils.acquireUrlFromPerms(perms));
        }
        return urlList;
    }
    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    //~ 必须重写写入、更新方法,以触发写入更新时间戳。2024-04-09
    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    @Override
    public void putCacheData(String key, S_menu s_menu){
        super.putCacheData(key, s_menu);
        this.menuUpdateCache.triggerTimeUpdate();
    }
    @Override
    public void updateCacheData(String key, S_menu s_menu){
        super.updateCacheData(key, s_menu);
        this.menuUpdateCache.triggerTimeUpdate();
    }
    /**
     * <pre>
     *     1) 从更新缓存中,找出更新时间;
     *     2) 如果不存在则不更新,如果存在比较当前加载时间,大于初始加载时间则更新
     * </pre>
     * @date 2024-04-09
     */
    private void checkReloadForceLocalMemory(){
        try{
            Long timeStamp = this.menuUpdateCache.getTimeUpdate();
            if(timeStamp == null){
                return;
            }
            if(timeStamp.longValue() > this.reloadMills){
                synchronized (this._lock){
                    logger.info("当前节点,菜单缓存存在更新时间:{},需要重新加载...", timeStamp);
                    this.reload();
                    logger.debug("this.reloadMills = {}", this.reloadMills);
                }
            }
        } catch (Exception ex){
            logger.error("获取菜单更新时间缓存异常,获取不到时间,不重新加载:" + ex.getMessage(), ex);
            return;
        }
    }
    public void setMenuUpdateCache(RedisMenuUpdateCache menuUpdateCache) {
        this.menuUpdateCache = menuUpdateCache;
    }
    private long reloadMills = 0;   // 缓存重新加载的时间(毫秒值)
    private final Object _lock = new Object();
    private RedisMenuUpdateCache menuUpdateCache;
}
iplatform-base/src/main/java/com/iplatform/base/cache/RedisCaptchaCacheProvider.java
New file
@@ -0,0 +1,35 @@
package com.iplatform.base.cache;
import com.iplatform.base.Constants;
import com.walker.cache.Cache;
import com.walker.support.redis.cache.RedisCacheProvider;
/**
 * 验证码(Redis)缓存实现。
 * @date 2022-11-07
 */
public class RedisCaptchaCacheProvider extends RedisCacheProvider<String> {
    public RedisCaptchaCacheProvider(){
        this.setUseRedis(true);
        this.setLoadPage(false);
        // 2024-01-05 设置支持缓存失效,该参数很重要,如果需要失效而没有设置在调用后会出现数据无法失效!
        this.setSupportExpiredCache(true);
    }
    @Override
    public String getProviderName() {
        return Constants.CACHE_NAME_CAPTCHA;
    }
    @Override
    public Class<?> getProviderType() {
        return String.class;
    }
    @Override
    protected int loadDataToCache(Cache cache){
        // 无需启动加载数据
        return 0;
    }
}
iplatform-base/src/main/java/com/iplatform/base/cache/RedisCategoryCacheProvider.java
New file
@@ -0,0 +1,123 @@
package com.iplatform.base.cache;
import com.iplatform.base.CategoryCacheProvider;
import com.iplatform.base.Constants;
import com.iplatform.base.service.CategoryServiceImpl;
import com.iplatform.base.util.CategoryUtils;
import com.iplatform.base.util.cache.CategorySortComparator;
import com.iplatform.model.po.S_category;
import com.iplatform.model.vo.CategoryTreeVo;
import com.walker.cache.Cache;
import com.walker.infrastructure.ApplicationRuntimeException;
import com.walker.infrastructure.utils.JsonUtils;
import com.walker.infrastructure.utils.StringUtils;
import com.walker.support.redis.cache.RedisCache;
import com.walker.support.redis.cache.RedisCacheProvider;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.List;
/**
 * 平台分类数据,Redis缓存实现。
 * @author 时克英
 * @date 2023-05-17
 */
public class RedisCategoryCacheProvider extends RedisCacheProvider<S_category> implements CategoryCacheProvider {
    private final CategorySortComparator sortComparator = new CategorySortComparator();
    public RedisCategoryCacheProvider(){
        this.setUseRedis(true);
        this.setLoadPage(false);
    }
    @Override
    protected int loadDataToCache(Cache cache) {
        List<S_category> hosts = this.categoryService.selectAll(new S_category());
        if(!StringUtils.isEmptyList(hosts)){
            // ------------------------- 切换成普通缓存步骤:3
            if(this.isUseRedis()){
                // 如果redis中缓存数量和数据库中不一致(少),则清空redis缓存,重新加载数据库数据到缓存中。
                long totalCache = cache.getPersistentSize();
                if(totalCache != hosts.size()){
                    logger.info("redis缓存中Category数量小于实际用户,需要清空缓存重新加载! cache = " + totalCache + ", db = " + hosts.size());
                    cache.clear();
                    for(S_category h : hosts){
                        cache.put(String.valueOf(h.getId()), h);
                    }
                }
            }//------------------------------------------
            return hosts.size();
        }
        return 0;
    }
    @Override
    public List<CategoryTreeVo> getTree(Integer type, Integer status, String name, List<Integer> categoryIdList, int owner) {
        List<S_category> allTree = new ArrayList<>();
        S_category tempCategory = null;
        for(String value : ((RedisCache)this.getCache()).getIterator(null)){
            try{
                tempCategory = JsonUtils.jsonStringToObject(value, S_category.class);
            } catch (Exception ex){
                throw new ApplicationRuntimeException("redis存储'S_category'解析错误:" + value, ex);
            }
            if(CategoryUtils.isCondition(tempCategory, type, status, name, categoryIdList, owner)){
                allTree.add(tempCategory);
            }
        }
        return CategoryUtils.acquireListTree(allTree, sortComparator);
    }
    @Override
    public List<CategoryTreeVo> getListTree(Integer type, Integer status, String name, int owner) {
        if(StringUtils.isNotEmpty(name)){
            try {
                name = URLDecoder.decode(name, StringUtils.DEFAULT_CHARSET_UTF8);
            } catch (UnsupportedEncodingException e) {
                throw new RuntimeException(e);
            }
        }
        return getTree(type, status, name, null, owner);
    }
    @Override
    public S_category get(int id) {
        return this.getCacheData(String.valueOf(id));
    }
    @Override
    public void save(S_category category) {
        this.putCacheData(String.valueOf(category.getId()), category);
    }
    @Override
    public void update(S_category category) {
        this.updateCacheData(String.valueOf(category.getId()), category);
    }
    @Override
    public void remove(int id) {
        this.removeCacheData(String.valueOf(id));
    }
    @Override
    public String getProviderName() {
        return Constants.CACHE_NAME_CATEGORY;
    }
    @Override
    public Class<?> getProviderType() {
        return S_category.class;
    }
    public void setCategoryService(CategoryServiceImpl categoryService) {
        this.categoryService = categoryService;
    }
    private CategoryServiceImpl categoryService;
}
iplatform-base/src/main/java/com/iplatform/base/cache/RedisDeptCacheProvider.java
New file
@@ -0,0 +1,213 @@
package com.iplatform.base.cache;
import com.iplatform.base.Constants;
import com.iplatform.base.DeptCacheProvider;
import com.iplatform.base.service.DeptServiceImpl;
import com.iplatform.base.util.DeptUtils;
import com.iplatform.model.po.S_dept;
import com.walker.cache.Cache;
import com.walker.infrastructure.utils.JsonUtils;
import com.walker.infrastructure.utils.StringUtils;
import com.walker.support.redis.cache.RedisCacheProvider;
import java.util.ArrayList;
import java.util.List;
public class RedisDeptCacheProvider extends RedisCacheProvider<String> implements DeptCacheProvider {
    private boolean allowCacheChildren = true;
    private DeptServiceImpl deptService;
    public void setDeptService(DeptServiceImpl deptService) {
        this.deptService = deptService;
    }
    public RedisDeptCacheProvider(){
        this.setUseRedis(true);
        this.setLoadPage(false);
    }
    @Override
    public void setAllowCacheChildren(boolean allow) {
        this.allowCacheChildren = allow;
    }
    @Override
    public S_dept getDept(long deptId) {
        // 超级用户虚拟一个机构
        if(deptId == 0){
            return DeptUtils.SUPER_VISOR_DEPT;
        }
        String data = this.getCacheData(String.valueOf(deptId));
        if(StringUtils.isEmpty(data)){
            logger.error("查询机构缓存不存在,重新加载:" + deptId);
            S_dept s_dept = this.deptService.queryOneDept(deptId);
            if(s_dept == null){
                throw new IllegalStateException("数据库加载机构不存在,id=" + deptId);
            }
            try {
                if(this.allowCacheChildren){
                    this.putDept(s_dept);
                } else {
                    this.putCacheData(String.valueOf(deptId), JsonUtils.objectToJsonString(s_dept));
                }
                return s_dept;
            } catch (Exception ex){
                throw new RuntimeException("添加 [机构] 缓存错误:" + ex.getMessage(), ex);
            }
        }
        try {
            return JsonUtils.jsonStringToObject(data, S_dept.class);
        } catch (Exception e) {
            throw new RuntimeException("获得机构缓存错误:" + deptId + ", " + e.getMessage(), e);
        }
    }
    @Override
    public void updateDept(S_dept s_dept) {
        try {
            this.updateCacheData(String.valueOf(s_dept.getId()), JsonUtils.objectToJsonString(s_dept));
        } catch (Exception e) {
            throw new RuntimeException("更新机构缓存错误:" + s_dept.getId() + ", " + e.getMessage(), e);
        }
    }
    @Override
    public void removeDept(long deptId) {
        String key = String.valueOf(deptId);
        String keyChildren = key + DeptUtils.KEY_CHILDREN_PREFIX;
        S_dept current = this.getDept(deptId);
        if(current == null){
            logger.warn("机构缓存已不存在,无需删除");
            return;
        }
        // 删除该机构子机构缓存列表
        this.removeCacheData(keyChildren);
        this.removeCacheData(key);
        if(current.getParent_id().longValue() == 0){
            return;
        }
        // 2023-07-17,针对机构较大的项目,是不需要把机构树放Redis缓存的。
        if(this.allowCacheChildren){
            // 更新父机构对应的childrenList
            String parentId = String.valueOf(current.getParent_id());
            List<String> parentChildrenList = this.getChildrenDeptIdOneLevel(current.getParent_id());
            if(parentChildrenList != null){
                parentChildrenList.remove(String.valueOf(deptId));
                try {
                    this.updateCacheData(parentId + DeptUtils.KEY_CHILDREN_PREFIX, JsonUtils.objectToJsonString(parentChildrenList));
                } catch (Exception e) {
                    throw new RuntimeException("删除机构缓存时,更新父节点childrenList报错:" + e.getMessage(), e);
                }
            }
        }
    }
    @Override
    public void putDept(S_dept s_dept) {
        String id = String.valueOf(s_dept.getId());
        try {
            // 添加机构对象缓存
            this.putCacheData(id, JsonUtils.objectToJsonString(s_dept));
            if(this.allowCacheChildren){
                // 2023-07-17
                // 添加机构子机构列表,目前为空列表
                this.putCacheData(id + DeptUtils.KEY_CHILDREN_PREFIX, JsonUtils.objectToJsonString(new ArrayList<String>(2)));
            }
            if(s_dept.getParent_id().longValue() == 0){
                // 顶级单位,没有父机构了
                logger.debug("顶级机构,不用更新父机构列表, " + id);
                return;
            }
            if(this.allowCacheChildren){
                // 2023-07-17
                // 更新父机构列表
                List<String> parentChildrenList = this.getChildrenDeptIdOneLevel(s_dept.getParent_id());
                if(StringUtils.isEmptyList(parentChildrenList)){
                    parentChildrenList = new ArrayList<>();
                }
                parentChildrenList.add(id);
                this.updateCacheData(String.valueOf(s_dept.getParent_id()) + DeptUtils.KEY_CHILDREN_PREFIX, JsonUtils.objectToJsonString(parentChildrenList));
                if(logger.isDebugEnabled()){
                    logger.debug("添加机构缓存,parentChildrenList = " + parentChildrenList);
                }
            }
        } catch (Exception e) {
            throw new RuntimeException("添加 [机构] 缓存错误:" + e.getMessage(), e);
        }
    }
    @Override
    public List<String> getChildrenDeptIdOneLevel(long deptId) {
        if(this.allowCacheChildren){
            String key = String.valueOf(deptId) + DeptUtils.KEY_CHILDREN_PREFIX;
            String data = this.getCacheData(key);
            if(StringUtils.isEmpty(data)){
                logger.error("未找到缓存中子机构列表: " + key);
                return null;
            }
            try {
                return JsonUtils.jsonStringToObject(data, List.class);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        } else {
            // 2023-07-17,不支持缓存树结构,从数据库查询
            throw new UnsupportedOperationException("暂未实现代码:从数据库加载:"+ deptId + " 下一级所有子机构ID集合");
        }
    }
    @Override
    public List<S_dept> getChildrenDeptOneLevel(long deptId) {
        List<String> childrenIdList = this.getChildrenDeptIdOneLevel(deptId);
        if(StringUtils.isEmptyList(childrenIdList)){
            return null;
        }
        List<S_dept> resultList = new ArrayList<>();
        S_dept s_dept = null;
        for(String id : childrenIdList){
            s_dept = this.getDept(Long.parseLong(id));
            resultList.add(s_dept);
        }
        return resultList;
    }
//    @Override
//    protected int loadDataToCache(Cache cache) {
//        List<S_dept> list = this.deptService.queryAllDeptListForCache();
//        if(!StringUtils.isEmptyList(list)){
//            // 如果redis中缓存数量和数据库中不一致(少),则清空redis缓存,重新加载数据库数据到缓存中。
//            long totalCache = cache.getPersistentSize();
//            if(totalCache < list.size()){
//                logger.info("redis缓存中[机构]数量小于实际用户,需要清空缓存重新加载! cache = " + totalCache + ", db = " + list.size());
//                cache.clear();
//                for(S_dept h : list){
//                    this.putDept(h);
//                }
//            }
//            return list.size();
//        }
//        return 0;
//    }
    @Override
    public String getProviderName() {
        return Constants.CACHE_NAME_DEPT;
    }
    @Override
    public Class<?> getProviderType() {
        return String.class;
    }
//    private static final String KEY_CHILDREN_PREFIX = ".children";
}
iplatform-base/src/main/java/com/iplatform/base/cache/RedisHostCacheProvider.java
New file
@@ -0,0 +1,61 @@
package com.iplatform.base.cache;
import com.iplatform.base.Constants;
import com.iplatform.model.po.S_host;
import com.walker.cache.Cache;
import com.walker.infrastructure.utils.StringUtils;
import com.walker.jdbc.service.PubService;
import com.walker.support.redis.cache.RedisCacheProvider;
import java.util.List;
/**
 * 主机资源缓存(Redis方式)实现。
 * @author 时克英
 * @date 2022-09-20
 */
public class RedisHostCacheProvider extends RedisCacheProvider<S_host> {
    private PubService pubService;
    public RedisHostCacheProvider(){
        this.setUseRedis(true);
        this.setLoadPage(false);
    }
    @Override
    public String getProviderName() {
        return Constants.CACHE_NAME_HOST;
    }
    @Override
    public Class<?> getProviderType() {
        return S_host.class;
    }
    @Override
    protected int loadDataToCache(Cache cache) {
        List<S_host> hosts = this.pubService.selectAll(new S_host());
        if(!StringUtils.isEmptyList(hosts)){
            // ------------------------- 切换成普通缓存步骤:3
            if(this.isUseRedis()){
                // 如果redis中缓存数量和数据库中不一致(少),则清空redis缓存,重新加载数据库数据到缓存中。
                long totalCache = cache.getPersistentSize();
                if(totalCache < hosts.size()){
                    logger.info("redis缓存中用户数量小于实际用户,需要清空缓存重新加载! cache = " + totalCache + ", db = " + hosts.size());
                    cache.clear();
                    for(S_host h : hosts){
                        cache.put(String.valueOf(h.getId()), h);
                    }
                }
            }//------------------------------------------
            return hosts.size();
        }
        return 0;
    }
    public void setPubService(PubService pubService) {
        this.pubService = pubService;
    }
}
iplatform-base/src/main/java/com/iplatform/base/cache/RedisMenuUpdateCache.java
New file
@@ -0,0 +1,42 @@
package com.iplatform.base.cache;
import com.walker.infrastructure.utils.NumberGenerator;
import com.walker.support.redis.cache.RedisCacheProvider;
/**
 * 记录菜单缓存更新时间的缓存。
 * <pre>
 *     1) 由于菜单缓存未使用Redis持久化,导致在集群环境中,无法触发全量更新。
 *     2) 因此采用记录更新时间记录到redis,当用户修改菜单数据后,记录修改时间;
 *     3) 各个节点在调用方法前,检查时间,如果存在新时间则重新加载本地缓存。
 * </pre>
 * @author 时克英
 * @date 2024-04-09
 */
public class RedisMenuUpdateCache extends RedisCacheProvider<Long> {
    public RedisMenuUpdateCache(){
        this.setUseRedis(true);
        this.setLoadPage(false);
    }
    @Override
    public String getProviderName() {
        return "cache.base.menu_update";
    }
    @Override
    public Class<?> getProviderType() {
        return Long.class;
    }
    public void triggerTimeUpdate(){
        this.putCacheData(KEY_TIME_UPDATE, NumberGenerator.getSequenceNumber());
    }
    public Long getTimeUpdate(){
        return this.getCacheData(KEY_TIME_UPDATE);
    }
    public static final String KEY_TIME_UPDATE = "time_update";
}
iplatform-base/src/main/java/com/iplatform/base/cache/RedisNotificationTemplateCache.java
New file
@@ -0,0 +1,103 @@
package com.iplatform.base.cache;
import com.iplatform.base.Constants;
import com.iplatform.base.NotificationTemplateCache;
import com.iplatform.base.NotifyConstants;
import com.iplatform.base.service.NotificationServiceImpl;
import com.iplatform.model.po.SfNotification;
import com.iplatform.model.po.SfTemplateMessage;
import com.iplatform.model.vo.NotificationTemplateVo;
import com.walker.cache.Cache;
import com.walker.infrastructure.utils.StringUtils;
import com.walker.support.redis.cache.RedisCacheProvider;
import java.util.List;
import java.util.Map;
public class RedisNotificationTemplateCache extends RedisCacheProvider<NotificationTemplateVo> implements NotificationTemplateCache {
    public RedisNotificationTemplateCache(){
        this.setUseRedis(true);
        this.setLoadPage(false);
    }
    @Override
    protected int loadDataToCache(Cache cache) {
        List<SfNotification> notificationList = this.notificationService.selectAll(new SfNotification());
        if(!StringUtils.isEmptyList(notificationList)){
            // ------------------------- 切换成普通缓存步骤:3
            if(this.isUseRedis()){
                // 如果redis中缓存数量和数据库中不一致(少),则清空redis缓存,重新加载数据库数据到缓存中。
                long totalCache = cache.getPersistentSize();
                if(totalCache != notificationList.size()){
                    logger.info(Constants.CACHE_NAME_NOTIFICATION_TEMPLATE + ": redis缓存中用户数量小于实际用户,需要清空缓存重新加载! cache = " + totalCache + ", db = " + notificationList.size());
                    cache.clear();
                    Map<Long, SfTemplateMessage> templateMessageMap = this.notificationService.queryTemplateIdMap();
                    if(templateMessageMap.size() == 0){
                        logger.warn("提醒模板未找到任何有效数据");
                        return 0;
                    }
                    for(SfNotification h : notificationList){
                        NotificationTemplateVo vo = new NotificationTemplateVo();
                        vo.setName(h.getMark());
                        if(h.getIsWechat().intValue() == NotifyConstants.SWITCH_OPEN){
                            vo.setWechat(true);
                            vo.setWechatId(h.getWechatId());
                            vo.setWechatTempId(templateMessageMap.get(h.getWechatId().longValue()).getTempId());
                        }
                        if(h.getIsRoutine().intValue() == NotifyConstants.SWITCH_OPEN){
                            vo.setRoutine(true);
                            vo.setRoutineId(h.getRoutineId());
                            vo.setRoutineTempId(templateMessageMap.get(h.getRoutineId().longValue()).getTempId());
                        }
                        if(h.getIsSms().intValue() == NotifyConstants.SWITCH_OPEN){
                            vo.setSms(true);
                            vo.setSmsId(h.getSmsId());
                            vo.setSmsTempId(templateMessageMap.get(h.getSmsId().longValue()).getTempId());
                        }
                        cache.put(h.getMark(), vo);
                    }
                }
            }//------------------------------------------
            return notificationList.size();
        }
        return 0;
    }
    @Override
    public NotificationTemplateVo get(String mark) {
        return this.getCacheData(mark);
    }
    @Override
    public void save(NotificationTemplateVo category) {
        this.putCacheData(category.getName(), category);
    }
    @Override
    public void update(NotificationTemplateVo category) {
        this.updateCacheData(category.getName(), category);
    }
    @Override
    public void remove(String mark) {
        this.removeCacheData(mark);
    }
    @Override
    public String getProviderName() {
        return Constants.CACHE_NAME_NOTIFICATION_TEMPLATE;
    }
    @Override
    public Class<?> getProviderType() {
        return NotificationTemplateVo.class;
    }
    public void setNotificationService(NotificationServiceImpl notificationService) {
        this.notificationService = notificationService;
    }
    private NotificationServiceImpl notificationService;
}
iplatform-base/src/main/java/com/iplatform/base/cache/RedisPushCacheProvider.java
New file
@@ -0,0 +1,35 @@
package com.iplatform.base.cache;
import com.iplatform.base.Constants;
import com.iplatform.base.PushCacheProvider;
import com.walker.support.redis.cache.RedisCacheProvider;
public class RedisPushCacheProvider extends RedisCacheProvider<String> implements PushCacheProvider {
    public RedisPushCacheProvider(){
        this.setUseRedis(true);
        this.setLoadPage(false);
        // 2024-01-05 设置支持缓存失效,该参数很重要,如果需要失效而没有设置在调用后会出现数据无法失效!
        this.setSupportExpiredCache(true);
    }
    @Override
    public String getProviderName() {
        return Constants.CACHE_NAME_PUSH;
    }
    @Override
    public Class<?> getProviderType() {
        return String.class;
    }
    @Override
    public void put(String key, String value) {
        this.putCacheData(key, value, 60);
    }
    @Override
    public String get(String key) {
        return this.getCacheData(key);
    }
}
iplatform-base/src/main/java/com/iplatform/base/cache/RedisSystemGroupCache.java
New file
@@ -0,0 +1,95 @@
package com.iplatform.base.cache;
import com.iplatform.base.Constants;
import com.iplatform.base.SystemGroupCache;
import com.iplatform.base.service.GroupServiceImpl;
import com.iplatform.model.po.S_group;
import com.iplatform.model.po.S_group_data;
import com.iplatform.model.vo.SystemGroupVo;
import com.walker.cache.Cache;
import com.walker.infrastructure.utils.StringUtils;
import com.walker.support.redis.cache.RedisCacheProvider;
import java.util.ArrayList;
import java.util.List;
public class RedisSystemGroupCache extends RedisCacheProvider<SystemGroupVo> implements SystemGroupCache {
    public RedisSystemGroupCache(){
        this.setUseRedis(true);
        this.setLoadPage(false);
    }
    @Override
    public SystemGroupVo get(int id) {
        return this.getCacheData(String.valueOf(id));
    }
    @Override
    public void save(SystemGroupVo category) {
        this.putCacheData(String.valueOf(category.getId()), category);
    }
    @Override
    public void update(SystemGroupVo category) {
        this.updateCacheData(String.valueOf(category.getId()), category);
    }
    @Override
    public void remove(int id) {
        this.removeCacheData(String.valueOf(id));
    }
    @Override
    protected int loadDataToCache(Cache cache) {
        List<S_group> hosts = this.groupService.selectAll(new S_group());
        if(!StringUtils.isEmptyList(hosts)){
            if(this.isUseRedis()){
                // 如果redis中缓存数量和数据库中不一致(少),则清空redis缓存,重新加载数据库数据到缓存中。
                long totalCache = cache.getPersistentSize();
                if(totalCache != hosts.size()){
                    logger.info("redis缓存中用户数量小于实际用户,需要清空缓存重新加载! cache = " + totalCache + ", db = " + hosts.size());
                    cache.clear();
                    List<SystemGroupVo> groupVoList = new ArrayList<>(hosts.size());
                    List<S_group_data> groupDataList = this.groupService.queryAllGroupDataList();
                    for(S_group h : hosts){
                        groupVoList.add(new SystemGroupVo(h));
                    }
                    if(!StringUtils.isEmptyList(groupDataList)){
                        for(S_group_data data : groupDataList){
                            for(SystemGroupVo vo : groupVoList){
                                if(data.getGid().intValue() == vo.getId().intValue()){
                                    vo.addGroupData(data);
                                }
                            }
                        }
                    }
                    for(SystemGroupVo h : groupVoList){
                        cache.put(String.valueOf(h.getId()), h);
                    }
                }
            }
            return hosts.size();
        }
        return 0;
    }
    @Override
    public String getProviderName() {
        return Constants.CACHE_NAME_GROUP;
    }
    @Override
    public Class<?> getProviderType() {
        return SystemGroupVo.class;
    }
    public void setGroupService(GroupServiceImpl groupService) {
        this.groupService = groupService;
    }
    private GroupServiceImpl groupService;
}
Diff truncated after the above file
iplatform-base/src/main/java/com/iplatform/base/cache/RedisUserCacheProvider.java iplatform-base/src/main/java/com/iplatform/base/cache/RedisUserLoginCache.java iplatform-base/src/main/java/com/iplatform/base/cache/RedisUserOnlineProvider.java iplatform-base/src/main/java/com/iplatform/base/cache/RedisWechatCache.java iplatform-base/src/main/java/com/iplatform/base/callback/AfterLoginCallback.java iplatform-base/src/main/java/com/iplatform/base/callback/GeneralLoginCallback.java iplatform-base/src/main/java/com/iplatform/base/callback/PlatformCallbackPostProcessor.java iplatform-base/src/main/java/com/iplatform/base/callback/SecurityCallback.java iplatform-base/src/main/java/com/iplatform/base/callback/TestAfterLoginCallback.java iplatform-base/src/main/java/com/iplatform/base/callback/TestUserCallback.java iplatform-base/src/main/java/com/iplatform/base/callback/UserProfileCallback.java iplatform-base/src/main/java/com/iplatform/base/captcha/AbstractCaptchaProvider.java iplatform-base/src/main/java/com/iplatform/base/captcha/BlockPuzzleCaptchaProvider.java iplatform-base/src/main/java/com/iplatform/base/captcha/DefaultCaptchaProvider.java iplatform-base/src/main/java/com/iplatform/base/captcha/JigsawCaptchaProvider.java iplatform-base/src/main/java/com/iplatform/base/captcha/JigsawResult.java iplatform-base/src/main/java/com/iplatform/base/captcha/NoneCaptchaProvider.java iplatform-base/src/main/java/com/iplatform/base/captcha/SmsCaptchaProvider.java iplatform-base/src/main/java/com/iplatform/base/captcha/TextCaptchaProvider.java iplatform-base/src/main/java/com/iplatform/base/captcha/ThirdPartyCaptchaProvider.java iplatform-base/src/main/java/com/iplatform/base/config/ApiProperties.java iplatform-base/src/main/java/com/iplatform/base/config/BeanPostProcessorConfig.java iplatform-base/src/main/java/com/iplatform/base/config/CacheConfiguration.java iplatform-base/src/main/java/com/iplatform/base/config/CacheProperties.java iplatform-base/src/main/java/com/iplatform/base/config/CaptchaConfig.java iplatform-base/src/main/java/com/iplatform/base/config/CaptchaProperties.java iplatform-base/src/main/java/com/iplatform/base/config/DataImportConfig.java iplatform-base/src/main/java/com/iplatform/base/config/DatabaseMetaConfig.java iplatform-base/src/main/java/com/iplatform/base/config/DruidMonitorConfig.java iplatform-base/src/main/java/com/iplatform/base/config/FileProperties.java iplatform-base/src/main/java/com/iplatform/base/config/JacksonConfig.java iplatform-base/src/main/java/com/iplatform/base/config/LocalCacheConfig.java iplatform-base/src/main/java/com/iplatform/base/config/LogProperties.java iplatform-base/src/main/java/com/iplatform/base/config/LoginStrategyProperties.java iplatform-base/src/main/java/com/iplatform/base/config/PushConfig.java iplatform-base/src/main/java/com/iplatform/base/config/PushProperties.java iplatform-base/src/main/java/com/iplatform/base/config/PushWechatConfig.java iplatform-base/src/main/java/com/iplatform/base/config/RedisCacheConfig.java iplatform-base/src/main/java/com/iplatform/base/config/RestTemplateProperties.java iplatform-base/src/main/java/com/iplatform/base/config/SecurityUserProperties.java iplatform-base/src/main/java/com/iplatform/base/config/SpiConfig.java iplatform-base/src/main/java/com/iplatform/base/config/TcpProperties.java iplatform-base/src/main/java/com/iplatform/base/config/ThirdPartyConfig.java iplatform-base/src/main/java/com/iplatform/base/config/ThreadPoolConfig.java iplatform-base/src/main/java/com/iplatform/base/config/WebCommonConfig.java iplatform-base/src/main/java/com/iplatform/base/controller/CaptchaController.java iplatform-base/src/main/java/com/iplatform/base/controller/IndexController.java iplatform-base/src/main/java/com/iplatform/base/controller/TestMenuController.java iplatform-base/src/main/java/com/iplatform/base/controller/WechatCallbackApi.java iplatform-base/src/main/java/com/iplatform/base/di/AbstractDataImportEngine.java iplatform-base/src/main/java/com/iplatform/base/di/DataImportEngine.java iplatform-base/src/main/java/com/iplatform/base/di/FileDataImportEngine.java iplatform-base/src/main/java/com/iplatform/base/di/JdbcExcelDataImportor.java iplatform-base/src/main/java/com/iplatform/base/di/PlatformDataImportEngine.java iplatform-base/src/main/java/com/iplatform/base/di/PlatformExcelTemplateGenerator.java iplatform-base/src/main/java/com/iplatform/base/di/TemplateInfo.java iplatform-base/src/main/java/com/iplatform/base/event/RoleSecurityChangeEvent.java iplatform-base/src/main/java/com/iplatform/base/exception/CaptchaException.java iplatform-base/src/main/java/com/iplatform/base/exception/LoginException.java iplatform-base/src/main/java/com/iplatform/base/pojo/CaptchaParam.java iplatform-base/src/main/java/com/iplatform/base/pojo/ConfigParam.java iplatform-base/src/main/java/com/iplatform/base/pojo/DeptParam.java iplatform-base/src/main/java/com/iplatform/base/pojo/GroupDataParam.java iplatform-base/src/main/java/com/iplatform/base/pojo/KeywordsParam.java iplatform-base/src/main/java/com/iplatform/base/pojo/MenuParam.java iplatform-base/src/main/java/com/iplatform/base/pojo/RequestLogin.java iplatform-base/src/main/java/com/iplatform/base/pojo/UserInfo.java iplatform-base/src/main/java/com/iplatform/base/pojo/UserInfoRequest.java iplatform-base/src/main/java/com/iplatform/base/pojo/UserParam.java iplatform-base/src/main/java/com/iplatform/base/pojo/dict/DictParam.java iplatform-base/src/main/java/com/iplatform/base/pojo/form/FormData.java iplatform-base/src/main/java/com/iplatform/base/pojo/form/FormDataItem.java iplatform-base/src/main/java/com/iplatform/base/pojo/form/RequestForm.java iplatform-base/src/main/java/com/iplatform/base/pojo/group/GroupData.java iplatform-base/src/main/java/com/iplatform/base/pojo/log/LoginLogParam.java iplatform-base/src/main/java/com/iplatform/base/pojo/log/OperateLogParam.java iplatform-base/src/main/java/com/iplatform/base/pojo/notify/InfoParam.java iplatform-base/src/main/java/com/iplatform/base/pojo/notify/NotificationParam.java iplatform-base/src/main/java/com/iplatform/base/pojo/role/RoleAuthParam.java iplatform-base/src/main/java/com/iplatform/base/pojo/role/RoleParam.java iplatform-base/src/main/java/com/iplatform/base/pojo/role/RoleUserParam.java iplatform-base/src/main/java/com/iplatform/base/push/DefaultPushListener.java iplatform-base/src/main/java/com/iplatform/base/push/DefaultPushManager.java iplatform-base/src/main/java/com/iplatform/base/push/MockSmsPush.java iplatform-base/src/main/java/com/iplatform/base/push/SystemPush.java iplatform-base/src/main/java/com/iplatform/base/service/ApiTimeServiceImpl.java iplatform-base/src/main/java/com/iplatform/base/service/CategoryServiceImpl.java iplatform-base/src/main/java/com/iplatform/base/service/CodeServiceImpl.java iplatform-base/src/main/java/com/iplatform/base/service/CommonServiceImpl.java iplatform-base/src/main/java/com/iplatform/base/service/ConfigArgumentServiceImpl.java iplatform-base/src/main/java/com/iplatform/base/service/ConfigFormServiceImpl.java iplatform-base/src/main/java/com/iplatform/base/service/DataImportServiceImpl.java iplatform-base/src/main/java/com/iplatform/base/service/DeptServiceImpl.java iplatform-base/src/main/java/com/iplatform/base/service/GroupServiceImpl.java iplatform-base/src/main/java/com/iplatform/base/service/LogServiceImpl.java iplatform-base/src/main/java/com/iplatform/base/service/LoginServiceImpl.java iplatform-base/src/main/java/com/iplatform/base/service/MenuServiceImpl.java iplatform-base/src/main/java/com/iplatform/base/service/NotificationServiceImpl.java iplatform-base/src/main/java/com/iplatform/base/service/PushServiceImpl.java iplatform-base/src/main/java/com/iplatform/base/service/RoleServiceImpl.java iplatform-base/src/main/java/com/iplatform/base/service/UserDeptApiServiceImpl.java iplatform-base/src/main/java/com/iplatform/base/service/UserServiceImpl.java iplatform-base/src/main/java/com/iplatform/base/service_api/UserAndDeptServiceApi.java iplatform-base/src/main/java/com/iplatform/base/support/CtomsDataSource.java iplatform-base/src/main/java/com/iplatform/base/support/DatabaseArgumentsManager.java iplatform-base/src/main/java/com/iplatform/base/support/DeptTreeGenerator.java iplatform-base/src/main/java/com/iplatform/base/support/DictTreeGenerator.java iplatform-base/src/main/java/com/iplatform/base/support/LoadBalanceFileOperateSpi.java iplatform-base/src/main/java/com/iplatform/base/support/LogAspect.java iplatform-base/src/main/java/com/iplatform/base/support/NothingSecuritySpi.java iplatform-base/src/main/java/com/iplatform/base/support/PlatformOperationInterceptor.java iplatform-base/src/main/java/com/iplatform/base/support/RedisArgumentsManager.java iplatform-base/src/main/java/com/iplatform/base/support/TimeStatisticsInterceptor.java iplatform-base/src/main/java/com/iplatform/base/support/strategy/AbstractLoginStrategy.java iplatform-base/src/main/java/com/iplatform/base/support/strategy/LoginStrategyManager.java iplatform-base/src/main/java/com/iplatform/base/support/strategy/MobileOnceLoginStrategy.java iplatform-base/src/main/java/com/iplatform/base/support/strategy/WebOnceLoginStrategy.java iplatform-base/src/main/java/com/iplatform/base/util/ArgumentsManagerUtils.java iplatform-base/src/main/java/com/iplatform/base/util/CaptchaUtils.java iplatform-base/src/main/java/com/iplatform/base/util/CategoryUtils.java iplatform-base/src/main/java/com/iplatform/base/util/ConfigFormValidateUtils.java iplatform-base/src/main/java/com/iplatform/base/util/DataImportUtils.java iplatform-base/src/main/java/com/iplatform/base/util/DeptUtils.java iplatform-base/src/main/java/com/iplatform/base/util/MenuUtils.java iplatform-base/src/main/java/com/iplatform/base/util/NotificationUtils.java iplatform-base/src/main/java/com/iplatform/base/util/PlatformRSAUtils.java iplatform-base/src/main/java/com/iplatform/base/util/RandomUtils.java iplatform-base/src/main/java/com/iplatform/base/util/ResponseValueUtils.java iplatform-base/src/main/java/com/iplatform/base/util/RestTemplateUtils.java iplatform-base/src/main/java/com/iplatform/base/util/RoleUtils.java iplatform-base/src/main/java/com/iplatform/base/util/TextUtils.java iplatform-base/src/main/java/com/iplatform/base/util/TokenUtils.java iplatform-base/src/main/java/com/iplatform/base/util/UserUtils.java iplatform-base/src/main/java/com/iplatform/base/util/VerifyImgUtil.java iplatform-base/src/main/java/com/iplatform/base/util/cache/CacheInfo.java iplatform-base/src/main/java/com/iplatform/base/util/cache/CategorySortComparator.java iplatform-base/src/main/java/com/iplatform/base/util/dept/SystemDept.java iplatform-base/src/main/java/com/iplatform/base/util/dict/SystemDictData.java iplatform-base/src/main/java/com/iplatform/base/util/menu/MenuOrderNumComparator.java iplatform-base/src/main/java/com/iplatform/base/util/menu/MenuTree.java iplatform-base/src/main/java/com/iplatform/base/util/menu/ParentMenuComparator.java iplatform-base/src/main/java/com/iplatform/base/util/menu/SystemMenu.java iplatform-base/src/main/java/com/iplatform/base/util/role/SystemRole.java iplatform-base/src/main/java/com/iplatform/base/util/user/SystemUser.java iplatform-base/src/main/resources/images/demo.txt iplatform-base/src/main/resources/images/jigsaw/original/1.png iplatform-base/src/main/resources/images/jigsaw/original/2.png iplatform-base/src/main/resources/images/jigsaw/original/3.png iplatform-base/src/main/resources/images/jigsaw/original/4.png iplatform-base/src/main/resources/images/jigsaw/original/5.png iplatform-base/src/main/resources/images/jigsaw/original/6.png iplatform-base/src/main/resources/images/jigsaw/slidingBlock/1.png iplatform-base/src/main/resources/images/jigsaw/slidingBlock/2.png iplatform-base/src/main/resources/images/jigsaw/slidingBlock/3.png iplatform-base/src/main/resources/images/jigsaw/slidingBlock/4.png iplatform-base/src/main/resources/images/jigsaw/slidingBlock/5.png iplatform-base/src/main/resources/images/jigsaw/slidingBlock/6.png iplatform-base/src/main/resources/images/jigsaw_bg/1.jpg iplatform-base/src/main/resources/images/jigsaw_bg/10.jpg iplatform-base/src/main/resources/images/jigsaw_bg/11.jpg iplatform-base/src/main/resources/images/jigsaw_bg/11.png iplatform-base/src/main/resources/images/jigsaw_bg/12.jpg iplatform-base/src/main/resources/images/jigsaw_bg/13.jpg iplatform-base/src/main/resources/images/jigsaw_bg/14.jpg iplatform-base/src/main/resources/images/jigsaw_bg/15.jpg iplatform-base/src/main/resources/images/jigsaw_bg/16.jpg iplatform-base/src/main/resources/images/jigsaw_bg/2.jpg iplatform-base/src/main/resources/images/jigsaw_bg/3.jpg iplatform-base/src/main/resources/images/jigsaw_bg/4.jpg iplatform-base/src/main/resources/images/jigsaw_bg/5.jpg iplatform-base/src/main/resources/images/jigsaw_bg/6.jpg iplatform-base/src/main/resources/images/jigsaw_bg/7.jpg iplatform-base/src/main/resources/images/jigsaw_bg/8.jpg iplatform-base/src/main/resources/images/jigsaw_bg/9.jpg iplatform-base/src/main/resources/images/jigsaw_block/1.png iplatform-base/src/main/resources/images/jigsaw_block/10.png iplatform-base/src/main/resources/images/jigsaw_block/11.png iplatform-base/src/main/resources/images/jigsaw_block/12.png iplatform-base/src/main/resources/images/jigsaw_block/13.png iplatform-base/src/main/resources/images/jigsaw_block/14.png iplatform-base/src/main/resources/images/jigsaw_block/15.png iplatform-base/src/main/resources/images/jigsaw_block/16.png iplatform-base/src/main/resources/images/jigsaw_block/17.png iplatform-base/src/main/resources/images/jigsaw_block/18.png iplatform-base/src/main/resources/images/jigsaw_block/19.png iplatform-base/src/main/resources/images/jigsaw_block/2.png iplatform-base/src/main/resources/images/jigsaw_block/20.png iplatform-base/src/main/resources/images/jigsaw_block/21.png iplatform-base/src/main/resources/images/jigsaw_block/22.png iplatform-base/src/main/resources/images/jigsaw_block/3.png iplatform-base/src/main/resources/images/jigsaw_block/4.png iplatform-base/src/main/resources/images/jigsaw_block/5.png iplatform-base/src/main/resources/images/jigsaw_block/6.png iplatform-base/src/main/resources/images/jigsaw_block/7.png iplatform-base/src/main/resources/images/jigsaw_block/8.png iplatform-base/src/main/resources/images/jigsaw_block/9.png iplatform-base/src/test/java/com/iplatform/base/MenuCacheProvider.java iplatform-base/src/test/java/com/iplatform/base/TestCaptcha.java iplatform-base/src/test/java/com/iplatform/base/TestEntity.java iplatform-base/src/test/java/com/iplatform/base/TestMenu.java iplatform-base/src/test/java/com/iplatform/base/redis/MenuCacheProvider.java iplatform-core/pom.xml iplatform-core/src/main/java/com/iplatform/core/BeanContextAware.java iplatform-core/src/main/java/com/iplatform/core/LoginStrategy.java iplatform-core/src/main/java/com/iplatform/core/PlatformConfiguration.java iplatform-core/src/main/java/com/iplatform/core/PlatformException.java iplatform-core/src/main/java/com/iplatform/core/RegularConstants.java iplatform-core/src/main/java/com/iplatform/core/SimpleVariable.java iplatform-core/src/main/java/com/iplatform/core/TokenAwareContext.java iplatform-core/src/main/java/com/iplatform/core/TokenEntity.java iplatform-core/src/main/java/com/iplatform/core/UserMerchantType.java iplatform-core/src/main/java/com/iplatform/core/config/BasicConfig.java iplatform-core/src/main/java/com/iplatform/core/config/LoadBalanceProperties.java iplatform-core/src/main/java/com/iplatform/core/config/PropertyPostProcessorConfig.java iplatform-core/src/main/java/com/iplatform/core/config/enc/EncryptionWrapperDetector.java iplatform-core/src/main/java/com/iplatform/core/config/enc/PropertySourcePostProcessor.java iplatform-core/src/main/java/com/iplatform/core/config/enc/PropertySourceWrapper.java iplatform-core/src/main/java/com/iplatform/core/util/AESUtils.java iplatform-core/src/main/java/com/iplatform/core/util/ThreadUtils.java iplatform-core/src/main/java/com/iplatform/core/workflow/AbstractContext.java iplatform-core/src/main/java/com/iplatform/core/workflow/Actorable.java iplatform-core/src/main/java/com/iplatform/core/workflow/Constants.java iplatform-core/src/main/java/com/iplatform/core/workflow/Context.java iplatform-core/src/main/java/com/iplatform/core/workflow/WorkflowCallback.java iplatform-core/src/main/java/com/iplatform/core/workflow/WorkflowForm.java iplatform-file-server/pom.xml iplatform-file-server/src/main/java/com/iplatform/App.java iplatform-file-server/src/main/java/com/iplatform/file/DefaultFileOperateSpi.java iplatform-file-server/src/main/java/com/iplatform/file/FileEngineFactory.java iplatform-file-server/src/main/java/com/iplatform/file/FileStoreCallback.java iplatform-file-server/src/main/java/com/iplatform/file/config/FileConfig.java iplatform-file-server/src/main/java/com/iplatform/file/config/FileWebConfig.java iplatform-file-server/src/main/java/com/iplatform/file/config/FtpProperties.java iplatform-file-server/src/main/java/com/iplatform/file/controller/AttachmentController.java iplatform-file-server/src/main/java/com/iplatform/file/controller/FileController.java iplatform-file-server/src/main/java/com/iplatform/file/controller/OssFileApi.java iplatform-file-server/src/main/java/com/iplatform/file/service/FileServiceImpl.java iplatform-file-server/src/main/java/com/iplatform/file/support/AbstractOssFileEngine.java iplatform-file-server/src/main/java/com/iplatform/file/support/AliOssFileEngine.java iplatform-file-server/src/main/java/com/iplatform/file/support/AttachmentJdbcCallback.java iplatform-file-server/src/main/java/com/iplatform/file/support/AwsOssFileEngine.java iplatform-file-server/src/main/java/com/iplatform/file/support/DefaultFileSystemEngine.java iplatform-file-server/src/main/java/com/iplatform/file/support/DefaultFtpFileEngine.java iplatform-file-server/src/main/java/com/iplatform/file/support/JdbcCallback.java iplatform-file-server/src/main/java/com/iplatform/file/support/QnOssFileEngine.java iplatform-file-server/src/main/java/com/iplatform/file/support/TxOssFileEngine.java iplatform-file-server/src/main/java/com/iplatform/file/util/FileResultUtils.java iplatform-file-server/src/main/java/com/iplatform/file/util/FileStoreUtils.java iplatform-file-server/src/main/java/com/iplatform/file/util/ImageUtils.java iplatform-file-server/src/main/java/com/iplatform/model/po/S_file.java iplatform-file-server/src/main/java/com/iplatform/model/po/S_file_mapper.java iplatform-file-server/src/main/java/com/iplatform/model/po/SfAttachment.java iplatform-file-server/src/main/java/com/iplatform/model/po/SfAttachment_mapper.java iplatform-file-server/src/main/java/com/iplatform/model/vo/FileResultVo.java iplatform-file-server/src/test/java/com/iplatform/file/TestOss.java iplatform-model-pojo/pom.xml iplatform-model-pojo/src/main/java/com/iplatform/model/po/S_category.java iplatform-model-pojo/src/main/java/com/iplatform/model/po/S_category_mapper.java iplatform-model-pojo/src/main/java/com/iplatform/model/po/S_chat.java iplatform-model-pojo/src/main/java/com/iplatform/model/po/S_chat_mapper.java iplatform-model-pojo/src/main/java/com/iplatform/model/po/S_class_ext.java iplatform-model-pojo/src/main/java/com/iplatform/model/po/S_class_ext_mapper.java iplatform-model-pojo/src/main/java/com/iplatform/model/po/S_code.java iplatform-model-pojo/src/main/java/com/iplatform/model/po/S_code_mapper.java iplatform-model-pojo/src/main/java/com/iplatform/model/po/S_config.java iplatform-model-pojo/src/main/java/com/iplatform/model/po/S_config_form.java iplatform-model-pojo/src/main/java/com/iplatform/model/po/S_config_form_mapper.java iplatform-model-pojo/src/main/java/com/iplatform/model/po/S_config_mapper.java iplatform-model-pojo/src/main/java/com/iplatform/model/po/S_dept.java iplatform-model-pojo/src/main/java/com/iplatform/model/po/S_dept_mapper.java iplatform-model-pojo/src/main/java/com/iplatform/model/po/S_dialog_history.java iplatform-model-pojo/src/main/java/com/iplatform/model/po/S_dialog_history_mapper.java iplatform-model-pojo/src/main/java/com/iplatform/model/po/S_dialog_run.java iplatform-model-pojo/src/main/java/com/iplatform/model/po/S_dialog_run_mapper.java iplatform-model-pojo/src/main/java/com/iplatform/model/po/S_dict_data.java iplatform-model-pojo/src/main/java/com/iplatform/model/po/S_dict_data_mapper.java iplatform-model-pojo/src/main/java/com/iplatform/model/po/S_dict_type.java iplatform-model-pojo/src/main/java/com/iplatform/model/po/S_dict_type_mapper.java iplatform-model-pojo/src/main/java/com/iplatform/model/po/S_group.java iplatform-model-pojo/src/main/java/com/iplatform/model/po/S_group_data.java iplatform-model-pojo/src/main/java/com/iplatform/model/po/S_group_data_mapper.java iplatform-model-pojo/src/main/java/com/iplatform/model/po/S_group_mapper.java iplatform-model-pojo/src/main/java/com/iplatform/model/po/S_host.java iplatform-model-pojo/src/main/java/com/iplatform/model/po/S_host_mapper.java iplatform-model-pojo/src/main/java/com/iplatform/model/po/S_login_info.java iplatform-model-pojo/src/main/java/com/iplatform/model/po/S_login_info_mapper.java iplatform-model-pojo/src/main/java/com/iplatform/model/po/S_menu.java iplatform-model-pojo/src/main/java/com/iplatform/model/po/S_menu_mapper.java iplatform-model-pojo/src/main/java/com/iplatform/model/po/S_message.java iplatform-model-pojo/src/main/java/com/iplatform/model/po/S_message_mapper.java iplatform-model-pojo/src/main/java/com/iplatform/model/po/S_oper_log.java iplatform-model-pojo/src/main/java/com/iplatform/model/po/S_oper_log_mapper.java iplatform-model-pojo/src/main/java/com/iplatform/model/po/S_role.java iplatform-model-pojo/src/main/java/com/iplatform/model/po/S_role_mapper.java iplatform-model-pojo/src/main/java/com/iplatform/model/po/S_user_core.java iplatform-model-pojo/src/main/java/com/iplatform/model/po/S_user_core_mapper.java iplatform-model-pojo/src/main/java/com/iplatform/model/po/S_user_login.java iplatform-model-pojo/src/main/java/com/iplatform/model/po/S_user_login_mapper.java iplatform-model-pojo/src/main/java/com/iplatform/model/po/S_user_profile.java iplatform-model-pojo/src/main/java/com/iplatform/model/po/S_user_profile_mapper.java iplatform-model-pojo/src/main/java/com/iplatform/model/po/SfNotification.java iplatform-model-pojo/src/main/java/com/iplatform/model/po/SfNotification_mapper.java iplatform-model-pojo/src/main/java/com/iplatform/model/po/SfTemplateMessage.java iplatform-model-pojo/src/main/java/com/iplatform/model/po/SfTemplateMessage_mapper.java iplatform-model-pojo/src/main/java/com/iplatform/model/po/TcpEquip.java iplatform-model-pojo/src/main/java/com/iplatform/model/po/TcpEquipHistory.java iplatform-model-pojo/src/main/java/com/iplatform/model/po/TcpEquipHistory_mapper.java iplatform-model-pojo/src/main/java/com/iplatform/model/po/TcpEquipStatus.java iplatform-model-pojo/src/main/java/com/iplatform/model/po/TcpEquipStatus_mapper.java iplatform-model-pojo/src/main/java/com/iplatform/model/po/TcpEquip_mapper.java iplatform-model-pojo/src/main/java/com/iplatform/model/to/UserAndDeptTo.java iplatform-model-pojo/src/main/java/com/iplatform/model/to/UserAndDeptToResult.java iplatform-model-pojo/src/main/java/com/iplatform/model/vo/ApiTime.java iplatform-model-pojo/src/main/java/com/iplatform/model/vo/CategoryTreeVo.java iplatform-model-pojo/src/main/java/com/iplatform/model/vo/ConfigFormItemConfigRegListVo.java iplatform-model-pojo/src/main/java/com/iplatform/model/vo/ConfigFormItemConfigVo.java iplatform-model-pojo/src/main/java/com/iplatform/model/vo/ConfigFormItemVo.java iplatform-model-pojo/src/main/java/com/iplatform/model/vo/ConfigFormVo.java iplatform-model-pojo/src/main/java/com/iplatform/model/vo/CopyRightVo.java iplatform-model-pojo/src/main/java/com/iplatform/model/vo/MenuVo.java iplatform-model-pojo/src/main/java/com/iplatform/model/vo/MetaVo.java iplatform-model-pojo/src/main/java/com/iplatform/model/vo/NotificationConfigVo.java iplatform-model-pojo/src/main/java/com/iplatform/model/vo/NotificationTemplateVo.java iplatform-model-pojo/src/main/java/com/iplatform/model/vo/OrderCenterNumVo.java iplatform-model-pojo/src/main/java/com/iplatform/model/vo/RouterVo.java iplatform-model-pojo/src/main/java/com/iplatform/model/vo/SystemGroupVo.java iplatform-model-pojo/src/main/java/com/iplatform/model/vo/WeChatAccessTokenVo.java pom.xml