From 13b4f626aa01eb800c0ca84059e5e8c708a91c5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=B5=B5=E5=BF=A0=E6=9E=97?= <170083662@qq.com> Date: Sat, 4 Apr 2026 10:31:32 +0800 Subject: [PATCH] =?UTF-8?q?remove(AI):=20=E7=A7=BB=E9=99=A4=E6=89=80?= =?UTF-8?q?=E6=9C=89AI=E7=9B=B8=E5=85=B3=E6=A8=A1=E5=9D=97=E4=BB=A3?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 删除Ollama客户端及DTO类定义 - 移除AI聊天、分析、知识库控制器 - 清理AI相关数据传输对象 - 删除知识库实体类和映射器 - 移除AI分析服务和提示词模板 - 清理AI相关的XML映射文件 - 移除AI配置属性类定义 - 删除知识库服务实现类 --- .../com/gxwebsoft/ai/client/OllamaClient.java | 194 ------ .../ai/client/dto/OllamaChatRequest.java | 19 - .../ai/client/dto/OllamaChatResponse.java | 28 - .../ai/client/dto/OllamaEmbeddingRequest.java | 14 - .../client/dto/OllamaEmbeddingResponse.java | 13 - .../ai/client/dto/OllamaMessage.java | 14 - .../ai/client/dto/OllamaTagsResponse.java | 22 - .../ai/config/AiOllamaProperties.java | 68 -- .../ai/controller/AiAnalyticsController.java | 37 - .../ai/controller/AiChatController.java | 95 --- .../ai/controller/AiKbController.java | 54 -- .../ai/dto/AiAnalyticsAskRequest.java | 13 - .../ai/dto/AiAnalyticsAskResult.java | 12 - .../com/gxwebsoft/ai/dto/AiChatRequest.java | 35 - .../com/gxwebsoft/ai/dto/AiChatResult.java | 15 - .../com/gxwebsoft/ai/dto/AiKbAskRequest.java | 12 - .../com/gxwebsoft/ai/dto/AiKbAskResult.java | 12 - .../java/com/gxwebsoft/ai/dto/AiKbHit.java | 15 - .../gxwebsoft/ai/dto/AiKbIngestResult.java | 15 - .../gxwebsoft/ai/dto/AiKbQueryRequest.java | 12 - .../com/gxwebsoft/ai/dto/AiKbQueryResult.java | 13 - .../java/com/gxwebsoft/ai/dto/AiMessage.java | 14 - .../ai/dto/AiShopMetricsQueryRequest.java | 18 - .../ai/dto/AiShopMetricsQueryResult.java | 16 - .../gxwebsoft/ai/dto/AiShopMetricsRow.java | 23 - .../com/gxwebsoft/ai/entity/AiKbChunk.java | 57 -- .../com/gxwebsoft/ai/entity/AiKbDocument.java | 59 -- .../gxwebsoft/ai/mapper/AiKbChunkMapper.java | 10 - .../ai/mapper/AiKbDocumentMapper.java | 10 - .../ai/mapper/AiShopAnalyticsMapper.java | 16 - .../ai/mapper/xml/AiShopAnalyticsMapper.xml | 24 - .../com/gxwebsoft/ai/prompt/AiPrompts.java | 24 - .../ai/service/AiAnalyticsService.java | 82 --- .../gxwebsoft/ai/service/AiChatService.java | 94 --- .../ai/service/AiKbChunkService.java | 8 - .../ai/service/AiKbDocumentService.java | 8 - .../gxwebsoft/ai/service/AiKbRagService.java | 426 ------------ .../ai/service/AiShopAnalyticsService.java | 46 -- .../ai/service/impl/AiKbChunkServiceImpl.java | 12 - .../service/impl/AiKbDocumentServiceImpl.java | 12 - .../com/gxwebsoft/ai/util/AiTextUtil.java | 100 --- .../auto/controller/QrLoginController.java | 104 --- .../auto/dto/QrLoginConfirmRequest.java | 50 -- .../com/gxwebsoft/auto/dto/QrLoginData.java | 55 -- .../auto/dto/QrLoginGenerateResponse.java | 29 - .../auto/dto/QrLoginStatusResponse.java | 32 - .../auto/service/QrLoginService.java | 46 -- .../auto/service/impl/QrLoginServiceImpl.java | 239 ------- .../ClinicAppointmentController.java | 122 ---- .../ClinicDoctorApplyController.java | 128 ---- .../ClinicDoctorUserController.java | 128 ---- .../controller/ClinicMedicineController.java | 127 ---- .../ClinicMedicineInoutController.java | 127 ---- .../ClinicMedicineStockController.java | 127 ---- .../ClinicPatientUserController.java | 129 ---- .../ClinicPrescriptionController.java | 191 ------ .../ClinicPrescriptionItemController.java | 121 ---- ...务中台-排班信息接口对接文档20251114(2).docx | Bin 115163 -> 0 bytes .../clinic/dto/PrescriptionOrderRequest.java | 24 - .../clinic/entity/ClinicAppointment.java | 81 --- .../clinic/entity/ClinicDoctorApply.java | 125 ---- .../clinic/entity/ClinicDoctorUser.java | 99 --- .../clinic/entity/ClinicMedicine.java | 71 -- .../clinic/entity/ClinicMedicineInout.java | 99 --- .../clinic/entity/ClinicMedicineStock.java | 59 -- .../clinic/entity/ClinicPatientUser.java | 85 --- .../clinic/entity/ClinicPrescription.java | 133 ---- .../clinic/entity/ClinicPrescriptionItem.java | 99 --- .../mapper/ClinicAppointmentMapper.java | 37 - .../mapper/ClinicDoctorApplyMapper.java | 37 - .../clinic/mapper/ClinicDoctorUserMapper.java | 37 - .../mapper/ClinicMedicineInoutMapper.java | 37 - .../clinic/mapper/ClinicMedicineMapper.java | 37 - .../mapper/ClinicMedicineStockMapper.java | 37 - .../mapper/ClinicPatientUserMapper.java | 37 - .../mapper/ClinicPrescriptionItemMapper.java | 38 -- .../mapper/ClinicPrescriptionMapper.java | 38 -- .../mapper/xml/ClinicAppointmentMapper.xml | 62 -- .../mapper/xml/ClinicDoctorApplyMapper.xml | 114 ---- .../mapper/xml/ClinicDoctorUserMapper.xml | 86 --- .../mapper/xml/ClinicMedicineInoutMapper.xml | 93 --- .../mapper/xml/ClinicMedicineMapper.xml | 66 -- .../mapper/xml/ClinicMedicineStockMapper.xml | 54 -- .../mapper/xml/ClinicPatientUserMapper.xml | 71 -- .../xml/ClinicPrescriptionItemMapper.xml | 73 -- .../mapper/xml/ClinicPrescriptionMapper.xml | 132 ---- .../clinic/param/ClinicAppointmentParam.java | 58 -- .../clinic/param/ClinicDoctorApplyParam.java | 114 ---- .../clinic/param/ClinicDoctorUserParam.java | 86 --- .../param/ClinicMedicineInoutParam.java | 102 --- .../clinic/param/ClinicMedicineParam.java | 63 -- .../param/ClinicMedicineStockParam.java | 50 -- .../clinic/param/ClinicPatientUserParam.java | 66 -- .../param/ClinicPrescriptionItemParam.java | 81 --- .../clinic/param/ClinicPrescriptionParam.java | 101 --- .../service/ClinicAppointmentService.java | 42 -- .../service/ClinicDoctorApplyService.java | 42 -- .../service/ClinicDoctorUserService.java | 42 -- .../service/ClinicMedicineInoutService.java | 42 -- .../clinic/service/ClinicMedicineService.java | 42 -- .../service/ClinicMedicineStockService.java | 42 -- .../service/ClinicPatientUserService.java | 42 -- .../ClinicPrescriptionItemService.java | 43 -- .../service/ClinicPrescriptionService.java | 45 -- .../impl/ClinicAppointmentServiceImpl.java | 47 -- .../impl/ClinicDoctorApplyServiceImpl.java | 47 -- .../impl/ClinicDoctorUserServiceImpl.java | 47 -- .../impl/ClinicMedicineInoutServiceImpl.java | 47 -- .../impl/ClinicMedicineServiceImpl.java | 47 -- .../impl/ClinicMedicineStockServiceImpl.java | 47 -- .../impl/ClinicPatientUserServiceImpl.java | 47 -- .../ClinicPrescriptionItemServiceImpl.java | 48 -- .../impl/ClinicPrescriptionServiceImpl.java | 75 -- .../cms/controller/CmsAppController.java | 646 ++++++++++++++++++ .../cms/controller/CmsAppFieldController.java | 188 +++++ .../controller/CmsAppSettingController.java | 121 ++++ .../java/com/gxwebsoft/cms/entity/CmsApp.java | 368 ++++++++++ .../com/gxwebsoft/cms/entity/CmsAppField.java | 75 ++ .../gxwebsoft/cms/entity/CmsAppSetting.java | 89 +++ .../cms/mapper/CmsAppFieldMapper.java | 43 ++ .../gxwebsoft/cms/mapper/CmsAppMapper.java | 53 ++ .../cms/mapper/CmsAppSettingMapper.java | 37 + .../cms/mapper/xml/CmsAppFieldMapper.xml | 82 +++ .../gxwebsoft/cms/mapper/xml/CmsAppMapper.xml | 463 +++++++++++++ .../cms/mapper/xml/CmsAppSettingMapper.xml | 81 +++ .../cms/param/CmsAppFieldImportParam.java | 56 ++ .../gxwebsoft/cms/param/CmsAppFieldParam.java | 66 ++ .../com/gxwebsoft/cms/param/CmsAppParam.java | 231 +++++++ .../cms/param/CmsAppSettingParam.java | 86 +++ .../cms/service/CmsAppFieldService.java | 43 ++ .../gxwebsoft/cms/service/CmsAppService.java | 92 +++ .../cms/service/CmsAppSettingService.java | 42 ++ .../service/impl/CmsAppFieldServiceImpl.java | 54 ++ .../cms/service/impl/CmsAppServiceImpl.java | 587 ++++++++++++++++ .../service/impl/CmsAppServiceImplHelper.java | 239 +++++++ .../impl/CmsAppSettingServiceImpl.java | 47 ++ .../com/gxwebsoft/cms/sql/cms_app_publish.sql | 17 + .../controller/DormitoryApplyController.java | 128 ---- .../controller/DormitoryBedController.java | 126 ---- .../DormitoryBuildingController.java | 126 ---- .../controller/DormitoryFloorController.java | 126 ---- .../controller/DormitoryRecordController.java | 126 ---- .../dormitory/entity/DormitoryApply.java | 89 --- .../dormitory/entity/DormitoryBed.java | 91 --- .../dormitory/entity/DormitoryBuilding.java | 48 -- .../dormitory/entity/DormitoryFloor.java | 56 -- .../dormitory/entity/DormitoryRecord.java | 69 -- .../mapper/DormitoryApplyMapper.java | 37 - .../dormitory/mapper/DormitoryBedMapper.java | 37 - .../mapper/DormitoryBuildingMapper.java | 37 - .../mapper/DormitoryFloorMapper.java | 37 - .../mapper/DormitoryRecordMapper.java | 37 - .../mapper/xml/DormitoryApplyMapper.xml | 87 --- .../mapper/xml/DormitoryBedMapper.xml | 73 -- .../mapper/xml/DormitoryBuildingMapper.xml | 51 -- .../mapper/xml/DormitoryFloorMapper.xml | 55 -- .../mapper/xml/DormitoryRecordMapper.xml | 65 -- .../dormitory/param/DormitoryApplyParam.java | 87 --- .../dormitory/param/DormitoryBedParam.java | 69 -- .../param/DormitoryBuildingParam.java | 46 -- .../dormitory/param/DormitoryFloorParam.java | 50 -- .../dormitory/param/DormitoryRecordParam.java | 62 -- .../service/DormitoryApplyService.java | 42 -- .../service/DormitoryBedService.java | 42 -- .../service/DormitoryBuildingService.java | 42 -- .../service/DormitoryFloorService.java | 42 -- .../service/DormitoryRecordService.java | 42 -- .../impl/DormitoryApplyServiceImpl.java | 47 -- .../service/impl/DormitoryBedServiceImpl.java | 47 -- .../impl/DormitoryBuildingServiceImpl.java | 47 -- .../impl/DormitoryFloorServiceImpl.java | 47 -- .../impl/DormitoryRecordServiceImpl.java | 47 -- .../controller/EnterpriseController.java | 147 ---- .../enterprise/entity/Enterprise.java | 67 -- .../enterprise/mapper/EnterpriseMapper.java | 13 - .../mapper/xml/EnterpriseMapper.xml | 7 - .../enterprise/service/EnterpriseService.java | 13 - .../service/impl/EnterpriseServiceImpl.java | 17 - .../impl/CmsAppServiceImplCacheTest.java | 66 ++ 179 files changed, 3872 insertions(+), 9566 deletions(-) delete mode 100644 src/main/java/com/gxwebsoft/ai/client/OllamaClient.java delete mode 100644 src/main/java/com/gxwebsoft/ai/client/dto/OllamaChatRequest.java delete mode 100644 src/main/java/com/gxwebsoft/ai/client/dto/OllamaChatResponse.java delete mode 100644 src/main/java/com/gxwebsoft/ai/client/dto/OllamaEmbeddingRequest.java delete mode 100644 src/main/java/com/gxwebsoft/ai/client/dto/OllamaEmbeddingResponse.java delete mode 100644 src/main/java/com/gxwebsoft/ai/client/dto/OllamaMessage.java delete mode 100644 src/main/java/com/gxwebsoft/ai/client/dto/OllamaTagsResponse.java delete mode 100644 src/main/java/com/gxwebsoft/ai/config/AiOllamaProperties.java delete mode 100644 src/main/java/com/gxwebsoft/ai/controller/AiAnalyticsController.java delete mode 100644 src/main/java/com/gxwebsoft/ai/controller/AiChatController.java delete mode 100644 src/main/java/com/gxwebsoft/ai/controller/AiKbController.java delete mode 100644 src/main/java/com/gxwebsoft/ai/dto/AiAnalyticsAskRequest.java delete mode 100644 src/main/java/com/gxwebsoft/ai/dto/AiAnalyticsAskResult.java delete mode 100644 src/main/java/com/gxwebsoft/ai/dto/AiChatRequest.java delete mode 100644 src/main/java/com/gxwebsoft/ai/dto/AiChatResult.java delete mode 100644 src/main/java/com/gxwebsoft/ai/dto/AiKbAskRequest.java delete mode 100644 src/main/java/com/gxwebsoft/ai/dto/AiKbAskResult.java delete mode 100644 src/main/java/com/gxwebsoft/ai/dto/AiKbHit.java delete mode 100644 src/main/java/com/gxwebsoft/ai/dto/AiKbIngestResult.java delete mode 100644 src/main/java/com/gxwebsoft/ai/dto/AiKbQueryRequest.java delete mode 100644 src/main/java/com/gxwebsoft/ai/dto/AiKbQueryResult.java delete mode 100644 src/main/java/com/gxwebsoft/ai/dto/AiMessage.java delete mode 100644 src/main/java/com/gxwebsoft/ai/dto/AiShopMetricsQueryRequest.java delete mode 100644 src/main/java/com/gxwebsoft/ai/dto/AiShopMetricsQueryResult.java delete mode 100644 src/main/java/com/gxwebsoft/ai/dto/AiShopMetricsRow.java delete mode 100644 src/main/java/com/gxwebsoft/ai/entity/AiKbChunk.java delete mode 100644 src/main/java/com/gxwebsoft/ai/entity/AiKbDocument.java delete mode 100644 src/main/java/com/gxwebsoft/ai/mapper/AiKbChunkMapper.java delete mode 100644 src/main/java/com/gxwebsoft/ai/mapper/AiKbDocumentMapper.java delete mode 100644 src/main/java/com/gxwebsoft/ai/mapper/AiShopAnalyticsMapper.java delete mode 100644 src/main/java/com/gxwebsoft/ai/mapper/xml/AiShopAnalyticsMapper.xml delete mode 100644 src/main/java/com/gxwebsoft/ai/prompt/AiPrompts.java delete mode 100644 src/main/java/com/gxwebsoft/ai/service/AiAnalyticsService.java delete mode 100644 src/main/java/com/gxwebsoft/ai/service/AiChatService.java delete mode 100644 src/main/java/com/gxwebsoft/ai/service/AiKbChunkService.java delete mode 100644 src/main/java/com/gxwebsoft/ai/service/AiKbDocumentService.java delete mode 100644 src/main/java/com/gxwebsoft/ai/service/AiKbRagService.java delete mode 100644 src/main/java/com/gxwebsoft/ai/service/AiShopAnalyticsService.java delete mode 100644 src/main/java/com/gxwebsoft/ai/service/impl/AiKbChunkServiceImpl.java delete mode 100644 src/main/java/com/gxwebsoft/ai/service/impl/AiKbDocumentServiceImpl.java delete mode 100644 src/main/java/com/gxwebsoft/ai/util/AiTextUtil.java delete mode 100644 src/main/java/com/gxwebsoft/auto/controller/QrLoginController.java delete mode 100644 src/main/java/com/gxwebsoft/auto/dto/QrLoginConfirmRequest.java delete mode 100644 src/main/java/com/gxwebsoft/auto/dto/QrLoginData.java delete mode 100644 src/main/java/com/gxwebsoft/auto/dto/QrLoginGenerateResponse.java delete mode 100644 src/main/java/com/gxwebsoft/auto/dto/QrLoginStatusResponse.java delete mode 100644 src/main/java/com/gxwebsoft/auto/service/QrLoginService.java delete mode 100644 src/main/java/com/gxwebsoft/auto/service/impl/QrLoginServiceImpl.java delete mode 100644 src/main/java/com/gxwebsoft/clinic/controller/ClinicAppointmentController.java delete mode 100644 src/main/java/com/gxwebsoft/clinic/controller/ClinicDoctorApplyController.java delete mode 100644 src/main/java/com/gxwebsoft/clinic/controller/ClinicDoctorUserController.java delete mode 100644 src/main/java/com/gxwebsoft/clinic/controller/ClinicMedicineController.java delete mode 100644 src/main/java/com/gxwebsoft/clinic/controller/ClinicMedicineInoutController.java delete mode 100644 src/main/java/com/gxwebsoft/clinic/controller/ClinicMedicineStockController.java delete mode 100644 src/main/java/com/gxwebsoft/clinic/controller/ClinicPatientUserController.java delete mode 100644 src/main/java/com/gxwebsoft/clinic/controller/ClinicPrescriptionController.java delete mode 100644 src/main/java/com/gxwebsoft/clinic/controller/ClinicPrescriptionItemController.java delete mode 100644 src/main/java/com/gxwebsoft/clinic/controller/业务中台-排班信息接口对接文档20251114(2).docx delete mode 100644 src/main/java/com/gxwebsoft/clinic/dto/PrescriptionOrderRequest.java delete mode 100644 src/main/java/com/gxwebsoft/clinic/entity/ClinicAppointment.java delete mode 100644 src/main/java/com/gxwebsoft/clinic/entity/ClinicDoctorApply.java delete mode 100644 src/main/java/com/gxwebsoft/clinic/entity/ClinicDoctorUser.java delete mode 100644 src/main/java/com/gxwebsoft/clinic/entity/ClinicMedicine.java delete mode 100644 src/main/java/com/gxwebsoft/clinic/entity/ClinicMedicineInout.java delete mode 100644 src/main/java/com/gxwebsoft/clinic/entity/ClinicMedicineStock.java delete mode 100644 src/main/java/com/gxwebsoft/clinic/entity/ClinicPatientUser.java delete mode 100644 src/main/java/com/gxwebsoft/clinic/entity/ClinicPrescription.java delete mode 100644 src/main/java/com/gxwebsoft/clinic/entity/ClinicPrescriptionItem.java delete mode 100644 src/main/java/com/gxwebsoft/clinic/mapper/ClinicAppointmentMapper.java delete mode 100644 src/main/java/com/gxwebsoft/clinic/mapper/ClinicDoctorApplyMapper.java delete mode 100644 src/main/java/com/gxwebsoft/clinic/mapper/ClinicDoctorUserMapper.java delete mode 100644 src/main/java/com/gxwebsoft/clinic/mapper/ClinicMedicineInoutMapper.java delete mode 100644 src/main/java/com/gxwebsoft/clinic/mapper/ClinicMedicineMapper.java delete mode 100644 src/main/java/com/gxwebsoft/clinic/mapper/ClinicMedicineStockMapper.java delete mode 100644 src/main/java/com/gxwebsoft/clinic/mapper/ClinicPatientUserMapper.java delete mode 100644 src/main/java/com/gxwebsoft/clinic/mapper/ClinicPrescriptionItemMapper.java delete mode 100644 src/main/java/com/gxwebsoft/clinic/mapper/ClinicPrescriptionMapper.java delete mode 100644 src/main/java/com/gxwebsoft/clinic/mapper/xml/ClinicAppointmentMapper.xml delete mode 100644 src/main/java/com/gxwebsoft/clinic/mapper/xml/ClinicDoctorApplyMapper.xml delete mode 100644 src/main/java/com/gxwebsoft/clinic/mapper/xml/ClinicDoctorUserMapper.xml delete mode 100644 src/main/java/com/gxwebsoft/clinic/mapper/xml/ClinicMedicineInoutMapper.xml delete mode 100644 src/main/java/com/gxwebsoft/clinic/mapper/xml/ClinicMedicineMapper.xml delete mode 100644 src/main/java/com/gxwebsoft/clinic/mapper/xml/ClinicMedicineStockMapper.xml delete mode 100644 src/main/java/com/gxwebsoft/clinic/mapper/xml/ClinicPatientUserMapper.xml delete mode 100644 src/main/java/com/gxwebsoft/clinic/mapper/xml/ClinicPrescriptionItemMapper.xml delete mode 100644 src/main/java/com/gxwebsoft/clinic/mapper/xml/ClinicPrescriptionMapper.xml delete mode 100644 src/main/java/com/gxwebsoft/clinic/param/ClinicAppointmentParam.java delete mode 100644 src/main/java/com/gxwebsoft/clinic/param/ClinicDoctorApplyParam.java delete mode 100644 src/main/java/com/gxwebsoft/clinic/param/ClinicDoctorUserParam.java delete mode 100644 src/main/java/com/gxwebsoft/clinic/param/ClinicMedicineInoutParam.java delete mode 100644 src/main/java/com/gxwebsoft/clinic/param/ClinicMedicineParam.java delete mode 100644 src/main/java/com/gxwebsoft/clinic/param/ClinicMedicineStockParam.java delete mode 100644 src/main/java/com/gxwebsoft/clinic/param/ClinicPatientUserParam.java delete mode 100644 src/main/java/com/gxwebsoft/clinic/param/ClinicPrescriptionItemParam.java delete mode 100644 src/main/java/com/gxwebsoft/clinic/param/ClinicPrescriptionParam.java delete mode 100644 src/main/java/com/gxwebsoft/clinic/service/ClinicAppointmentService.java delete mode 100644 src/main/java/com/gxwebsoft/clinic/service/ClinicDoctorApplyService.java delete mode 100644 src/main/java/com/gxwebsoft/clinic/service/ClinicDoctorUserService.java delete mode 100644 src/main/java/com/gxwebsoft/clinic/service/ClinicMedicineInoutService.java delete mode 100644 src/main/java/com/gxwebsoft/clinic/service/ClinicMedicineService.java delete mode 100644 src/main/java/com/gxwebsoft/clinic/service/ClinicMedicineStockService.java delete mode 100644 src/main/java/com/gxwebsoft/clinic/service/ClinicPatientUserService.java delete mode 100644 src/main/java/com/gxwebsoft/clinic/service/ClinicPrescriptionItemService.java delete mode 100644 src/main/java/com/gxwebsoft/clinic/service/ClinicPrescriptionService.java delete mode 100644 src/main/java/com/gxwebsoft/clinic/service/impl/ClinicAppointmentServiceImpl.java delete mode 100644 src/main/java/com/gxwebsoft/clinic/service/impl/ClinicDoctorApplyServiceImpl.java delete mode 100644 src/main/java/com/gxwebsoft/clinic/service/impl/ClinicDoctorUserServiceImpl.java delete mode 100644 src/main/java/com/gxwebsoft/clinic/service/impl/ClinicMedicineInoutServiceImpl.java delete mode 100644 src/main/java/com/gxwebsoft/clinic/service/impl/ClinicMedicineServiceImpl.java delete mode 100644 src/main/java/com/gxwebsoft/clinic/service/impl/ClinicMedicineStockServiceImpl.java delete mode 100644 src/main/java/com/gxwebsoft/clinic/service/impl/ClinicPatientUserServiceImpl.java delete mode 100644 src/main/java/com/gxwebsoft/clinic/service/impl/ClinicPrescriptionItemServiceImpl.java delete mode 100644 src/main/java/com/gxwebsoft/clinic/service/impl/ClinicPrescriptionServiceImpl.java create mode 100644 src/main/java/com/gxwebsoft/cms/controller/CmsAppController.java create mode 100644 src/main/java/com/gxwebsoft/cms/controller/CmsAppFieldController.java create mode 100644 src/main/java/com/gxwebsoft/cms/controller/CmsAppSettingController.java create mode 100644 src/main/java/com/gxwebsoft/cms/entity/CmsApp.java create mode 100644 src/main/java/com/gxwebsoft/cms/entity/CmsAppField.java create mode 100644 src/main/java/com/gxwebsoft/cms/entity/CmsAppSetting.java create mode 100644 src/main/java/com/gxwebsoft/cms/mapper/CmsAppFieldMapper.java create mode 100644 src/main/java/com/gxwebsoft/cms/mapper/CmsAppMapper.java create mode 100644 src/main/java/com/gxwebsoft/cms/mapper/CmsAppSettingMapper.java create mode 100644 src/main/java/com/gxwebsoft/cms/mapper/xml/CmsAppFieldMapper.xml create mode 100644 src/main/java/com/gxwebsoft/cms/mapper/xml/CmsAppMapper.xml create mode 100644 src/main/java/com/gxwebsoft/cms/mapper/xml/CmsAppSettingMapper.xml create mode 100644 src/main/java/com/gxwebsoft/cms/param/CmsAppFieldImportParam.java create mode 100644 src/main/java/com/gxwebsoft/cms/param/CmsAppFieldParam.java create mode 100644 src/main/java/com/gxwebsoft/cms/param/CmsAppParam.java create mode 100644 src/main/java/com/gxwebsoft/cms/param/CmsAppSettingParam.java create mode 100644 src/main/java/com/gxwebsoft/cms/service/CmsAppFieldService.java create mode 100644 src/main/java/com/gxwebsoft/cms/service/CmsAppService.java create mode 100644 src/main/java/com/gxwebsoft/cms/service/CmsAppSettingService.java create mode 100644 src/main/java/com/gxwebsoft/cms/service/impl/CmsAppFieldServiceImpl.java create mode 100644 src/main/java/com/gxwebsoft/cms/service/impl/CmsAppServiceImpl.java create mode 100644 src/main/java/com/gxwebsoft/cms/service/impl/CmsAppServiceImplHelper.java create mode 100644 src/main/java/com/gxwebsoft/cms/service/impl/CmsAppSettingServiceImpl.java create mode 100644 src/main/java/com/gxwebsoft/cms/sql/cms_app_publish.sql delete mode 100644 src/main/java/com/gxwebsoft/dormitory/controller/DormitoryApplyController.java delete mode 100644 src/main/java/com/gxwebsoft/dormitory/controller/DormitoryBedController.java delete mode 100644 src/main/java/com/gxwebsoft/dormitory/controller/DormitoryBuildingController.java delete mode 100644 src/main/java/com/gxwebsoft/dormitory/controller/DormitoryFloorController.java delete mode 100644 src/main/java/com/gxwebsoft/dormitory/controller/DormitoryRecordController.java delete mode 100644 src/main/java/com/gxwebsoft/dormitory/entity/DormitoryApply.java delete mode 100644 src/main/java/com/gxwebsoft/dormitory/entity/DormitoryBed.java delete mode 100644 src/main/java/com/gxwebsoft/dormitory/entity/DormitoryBuilding.java delete mode 100644 src/main/java/com/gxwebsoft/dormitory/entity/DormitoryFloor.java delete mode 100644 src/main/java/com/gxwebsoft/dormitory/entity/DormitoryRecord.java delete mode 100644 src/main/java/com/gxwebsoft/dormitory/mapper/DormitoryApplyMapper.java delete mode 100644 src/main/java/com/gxwebsoft/dormitory/mapper/DormitoryBedMapper.java delete mode 100644 src/main/java/com/gxwebsoft/dormitory/mapper/DormitoryBuildingMapper.java delete mode 100644 src/main/java/com/gxwebsoft/dormitory/mapper/DormitoryFloorMapper.java delete mode 100644 src/main/java/com/gxwebsoft/dormitory/mapper/DormitoryRecordMapper.java delete mode 100644 src/main/java/com/gxwebsoft/dormitory/mapper/xml/DormitoryApplyMapper.xml delete mode 100644 src/main/java/com/gxwebsoft/dormitory/mapper/xml/DormitoryBedMapper.xml delete mode 100644 src/main/java/com/gxwebsoft/dormitory/mapper/xml/DormitoryBuildingMapper.xml delete mode 100644 src/main/java/com/gxwebsoft/dormitory/mapper/xml/DormitoryFloorMapper.xml delete mode 100644 src/main/java/com/gxwebsoft/dormitory/mapper/xml/DormitoryRecordMapper.xml delete mode 100644 src/main/java/com/gxwebsoft/dormitory/param/DormitoryApplyParam.java delete mode 100644 src/main/java/com/gxwebsoft/dormitory/param/DormitoryBedParam.java delete mode 100644 src/main/java/com/gxwebsoft/dormitory/param/DormitoryBuildingParam.java delete mode 100644 src/main/java/com/gxwebsoft/dormitory/param/DormitoryFloorParam.java delete mode 100644 src/main/java/com/gxwebsoft/dormitory/param/DormitoryRecordParam.java delete mode 100644 src/main/java/com/gxwebsoft/dormitory/service/DormitoryApplyService.java delete mode 100644 src/main/java/com/gxwebsoft/dormitory/service/DormitoryBedService.java delete mode 100644 src/main/java/com/gxwebsoft/dormitory/service/DormitoryBuildingService.java delete mode 100644 src/main/java/com/gxwebsoft/dormitory/service/DormitoryFloorService.java delete mode 100644 src/main/java/com/gxwebsoft/dormitory/service/DormitoryRecordService.java delete mode 100644 src/main/java/com/gxwebsoft/dormitory/service/impl/DormitoryApplyServiceImpl.java delete mode 100644 src/main/java/com/gxwebsoft/dormitory/service/impl/DormitoryBedServiceImpl.java delete mode 100644 src/main/java/com/gxwebsoft/dormitory/service/impl/DormitoryBuildingServiceImpl.java delete mode 100644 src/main/java/com/gxwebsoft/dormitory/service/impl/DormitoryFloorServiceImpl.java delete mode 100644 src/main/java/com/gxwebsoft/dormitory/service/impl/DormitoryRecordServiceImpl.java delete mode 100644 src/main/java/com/gxwebsoft/enterprise/controller/EnterpriseController.java delete mode 100644 src/main/java/com/gxwebsoft/enterprise/entity/Enterprise.java delete mode 100644 src/main/java/com/gxwebsoft/enterprise/mapper/EnterpriseMapper.java delete mode 100644 src/main/java/com/gxwebsoft/enterprise/mapper/xml/EnterpriseMapper.xml delete mode 100644 src/main/java/com/gxwebsoft/enterprise/service/EnterpriseService.java delete mode 100644 src/main/java/com/gxwebsoft/enterprise/service/impl/EnterpriseServiceImpl.java create mode 100644 src/test/java/com/gxwebsoft/cms/service/impl/CmsAppServiceImplCacheTest.java diff --git a/src/main/java/com/gxwebsoft/ai/client/OllamaClient.java b/src/main/java/com/gxwebsoft/ai/client/OllamaClient.java deleted file mode 100644 index 6e08d1b..0000000 --- a/src/main/java/com/gxwebsoft/ai/client/OllamaClient.java +++ /dev/null @@ -1,194 +0,0 @@ -package com.gxwebsoft.ai.client; - -import com.fasterxml.jackson.databind.ObjectMapper; -import com.gxwebsoft.ai.client.dto.*; -import com.gxwebsoft.ai.config.AiOllamaProperties; -import com.gxwebsoft.common.core.exception.BusinessException; -import com.gxwebsoft.common.core.utils.JSONUtil; -import okhttp3.*; -import okio.BufferedSource; -import org.springframework.stereotype.Component; - -import javax.annotation.Resource; -import java.io.IOException; -import java.time.Duration; -import java.util.Objects; -import java.util.function.Consumer; - -/** - * 轻量 Ollama HTTP Client(兼容 /api/chat、/api/embeddings、/api/tags)。 - */ -@Component -public class OllamaClient { - @Resource - private AiOllamaProperties props; - - @Resource - private ObjectMapper objectMapper; - - private volatile OkHttpClient http; - - private OkHttpClient http() { - OkHttpClient c = http; - if (c != null) { - return c; - } - synchronized (this) { - if (http == null) { - http = new OkHttpClient.Builder() - .connectTimeout(Duration.ofMillis(props.getConnectTimeoutMs())) - .readTimeout(Duration.ofMillis(props.getReadTimeoutMs())) - .writeTimeout(Duration.ofMillis(props.getWriteTimeoutMs())) - .build(); - } - return http; - } - } - - public OllamaTagsResponse tags() { - return getJson("/api/tags", OllamaTagsResponse.class); - } - - public OllamaChatResponse chat(OllamaChatRequest req) { - if (req.getStream() == null) { - req.setStream(false); - } - return postJson("/api/chat", req, OllamaChatResponse.class); - } - - /** - * 流式对话:Ollama 会返回按行分隔的 JSON。 - */ - public void chatStream(OllamaChatRequest req, Consumer onEvent) { - Objects.requireNonNull(onEvent, "onEvent"); - if (req.getStream() == null) { - req.setStream(true); - } - - Request request = buildPost(baseUrl(), "/api/chat", JSONUtil.toJSONString(req)); - try (Response resp = http().newCall(request).execute()) { - if (!resp.isSuccessful()) { - throw new BusinessException("Ollama chat stream failed: HTTP " + resp.code()); - } - ResponseBody body = resp.body(); - if (body == null) { - throw new BusinessException("Ollama chat stream failed: empty body"); - } - - BufferedSource source = body.source(); - String line; - while ((line = source.readUtf8Line()) != null) { - line = line.trim(); - if (line.isEmpty()) { - continue; - } - OllamaChatResponse event = objectMapper.readValue(line, OllamaChatResponse.class); - onEvent.accept(event); - if (Boolean.TRUE.equals(event.getDone())) { - break; - } - } - } catch (IOException e) { - throw new BusinessException("Ollama chat stream IO error: " + e.getMessage()); - } - } - - public OllamaEmbeddingResponse embedding(String prompt) { - OllamaEmbeddingRequest req = new OllamaEmbeddingRequest(); - req.setModel(props.getEmbedModel()); - req.setPrompt(prompt); - return postJson("/api/embeddings", req, OllamaEmbeddingResponse.class); - } - - private String baseUrl() { - if (props.getBaseUrl() == null || props.getBaseUrl().trim().isEmpty()) { - throw new BusinessException("ai.ollama.base-url 未配置"); - } - return props.getBaseUrl().trim(); - } - - private String fallbackUrl() { - if (props.getFallbackUrl() == null || props.getFallbackUrl().trim().isEmpty()) { - return null; - } - return props.getFallbackUrl().trim(); - } - - private T getJson(String path, Class clazz) { - try { - return getJsonOnce(baseUrl(), path, clazz); - } catch (Exception e) { - String fb = fallbackUrl(); - if (fb == null) { - throw e; - } - return getJsonOnce(fb, path, clazz); - } - } - - private T getJsonOnce(String base, String path, Class clazz) { - Request req = new Request.Builder() - .url(join(base, path)) - .get() - .build(); - try (Response resp = http().newCall(req).execute()) { - if (!resp.isSuccessful()) { - throw new BusinessException("Ollama GET failed: HTTP " + resp.code()); - } - ResponseBody body = resp.body(); - if (body == null) { - throw new BusinessException("Ollama GET failed: empty body"); - } - return objectMapper.readValue(body.string(), clazz); - } catch (IOException e) { - throw new BusinessException("Ollama GET IO error: " + e.getMessage()); - } - } - - private T postJson(String path, Object payload, Class clazz) { - String json = JSONUtil.toJSONString(payload); - try { - return postJsonOnce(baseUrl(), path, json, clazz); - } catch (Exception e) { - String fb = fallbackUrl(); - if (fb == null) { - throw e; - } - return postJsonOnce(fb, path, json, clazz); - } - } - - private T postJsonOnce(String base, String path, String json, Class clazz) { - Request req = buildPost(base, path, json); - try (Response resp = http().newCall(req).execute()) { - if (!resp.isSuccessful()) { - throw new BusinessException("Ollama POST failed: HTTP " + resp.code()); - } - ResponseBody body = resp.body(); - if (body == null) { - throw new BusinessException("Ollama POST failed: empty body"); - } - return objectMapper.readValue(body.string(), clazz); - } catch (IOException e) { - throw new BusinessException("Ollama POST IO error: " + e.getMessage()); - } - } - - private Request buildPost(String base, String path, String json) { - RequestBody body = RequestBody.create(json, MediaType.parse("application/json; charset=utf-8")); - return new Request.Builder() - .url(join(base, path)) - .post(body) - .build(); - } - - private static String join(String base, String path) { - String b = base; - if (b.endsWith("/")) { - b = b.substring(0, b.length() - 1); - } - String p = path.startsWith("/") ? path : ("/" + path); - return b + p; - } -} - diff --git a/src/main/java/com/gxwebsoft/ai/client/dto/OllamaChatRequest.java b/src/main/java/com/gxwebsoft/ai/client/dto/OllamaChatRequest.java deleted file mode 100644 index 938894a..0000000 --- a/src/main/java/com/gxwebsoft/ai/client/dto/OllamaChatRequest.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.gxwebsoft.ai.client.dto; - -import lombok.Data; - -import java.util.List; -import java.util.Map; - -@Data -public class OllamaChatRequest { - private String model; - private List messages; - private Boolean stream; - - /** - * Ollama options,例如:temperature、top_k、top_p、num_predict... - */ - private Map options; -} - diff --git a/src/main/java/com/gxwebsoft/ai/client/dto/OllamaChatResponse.java b/src/main/java/com/gxwebsoft/ai/client/dto/OllamaChatResponse.java deleted file mode 100644 index ec9291b..0000000 --- a/src/main/java/com/gxwebsoft/ai/client/dto/OllamaChatResponse.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.gxwebsoft.ai.client.dto; - -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonProperty; -import lombok.Data; - -@Data -@JsonIgnoreProperties(ignoreUnknown = true) -public class OllamaChatResponse { - private String model; - - @JsonProperty("created_at") - private String createdAt; - - private OllamaMessage message; - - private Boolean done; - - @JsonProperty("total_duration") - private Long totalDuration; - - @JsonProperty("prompt_eval_count") - private Integer promptEvalCount; - - @JsonProperty("eval_count") - private Integer evalCount; -} - diff --git a/src/main/java/com/gxwebsoft/ai/client/dto/OllamaEmbeddingRequest.java b/src/main/java/com/gxwebsoft/ai/client/dto/OllamaEmbeddingRequest.java deleted file mode 100644 index 5629eba..0000000 --- a/src/main/java/com/gxwebsoft/ai/client/dto/OllamaEmbeddingRequest.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.gxwebsoft.ai.client.dto; - -import lombok.Data; - -@Data -public class OllamaEmbeddingRequest { - private String model; - - /** - * Ollama embeddings 目前常用字段为 prompt。 - */ - private String prompt; -} - diff --git a/src/main/java/com/gxwebsoft/ai/client/dto/OllamaEmbeddingResponse.java b/src/main/java/com/gxwebsoft/ai/client/dto/OllamaEmbeddingResponse.java deleted file mode 100644 index 233e863..0000000 --- a/src/main/java/com/gxwebsoft/ai/client/dto/OllamaEmbeddingResponse.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.gxwebsoft.ai.client.dto; - -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import lombok.Data; - -import java.util.List; - -@Data -@JsonIgnoreProperties(ignoreUnknown = true) -public class OllamaEmbeddingResponse { - private List embedding; -} - diff --git a/src/main/java/com/gxwebsoft/ai/client/dto/OllamaMessage.java b/src/main/java/com/gxwebsoft/ai/client/dto/OllamaMessage.java deleted file mode 100644 index 6606c69..0000000 --- a/src/main/java/com/gxwebsoft/ai/client/dto/OllamaMessage.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.gxwebsoft.ai.client.dto; - -import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.NoArgsConstructor; - -@Data -@NoArgsConstructor -@AllArgsConstructor -public class OllamaMessage { - private String role; - private String content; -} - diff --git a/src/main/java/com/gxwebsoft/ai/client/dto/OllamaTagsResponse.java b/src/main/java/com/gxwebsoft/ai/client/dto/OllamaTagsResponse.java deleted file mode 100644 index c1fe7c0..0000000 --- a/src/main/java/com/gxwebsoft/ai/client/dto/OllamaTagsResponse.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.gxwebsoft.ai.client.dto; - -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import lombok.Data; - -import java.util.List; - -@Data -@JsonIgnoreProperties(ignoreUnknown = true) -public class OllamaTagsResponse { - private List models; - - @Data - @JsonIgnoreProperties(ignoreUnknown = true) - public static class Model { - private String name; - private Long size; - private String digest; - private String modified_at; - } -} - diff --git a/src/main/java/com/gxwebsoft/ai/config/AiOllamaProperties.java b/src/main/java/com/gxwebsoft/ai/config/AiOllamaProperties.java deleted file mode 100644 index e0c1732..0000000 --- a/src/main/java/com/gxwebsoft/ai/config/AiOllamaProperties.java +++ /dev/null @@ -1,68 +0,0 @@ -package com.gxwebsoft.ai.config; - -import lombok.Data; -import org.springframework.boot.context.properties.ConfigurationProperties; -import org.springframework.stereotype.Component; - -/** - * Ollama API 配置。 - * - * 说明:本项目通过自建 Ollama 网关提供服务,因此这里用 baseUrl + fallbackUrl。 - */ -@Data -@Component -@ConfigurationProperties(prefix = "ai.ollama") -public class AiOllamaProperties { - /** - * 主地址,例如:https://ai-api.websoft.top - */ - private String baseUrl; - - /** - * 备用地址,例如:http://47.119.165.234:11434 - */ - private String fallbackUrl; - - /** - * 对话模型,例如:qwen3.5:cloud - */ - private String chatModel; - - /** - * 向量模型,例如:qwen3-embedding:4b - */ - private String embedModel; - - /** - * HTTP 超时(毫秒)。 - */ - private long connectTimeoutMs = 10_000; - private long readTimeoutMs = 300_000; - private long writeTimeoutMs = 60_000; - - /** - * 并发上限(用于 embedding/入库等批处理场景)。 - */ - private int maxConcurrency = 4; - - /** - * RAG:检索候选 chunk 最大数量(避免一次性拉取过多数据导致内存/耗时过高)。 - */ - private int ragMaxCandidates = 2000; - - /** - * RAG:检索返回 topK。 - */ - private int ragTopK = 5; - - /** - * RAG:单个 chunk 最大字符数(用于入库切分)。 - */ - private int ragChunkSize = 800; - - /** - * RAG:chunk 重叠字符数(用于减少语义断裂)。 - */ - private int ragChunkOverlap = 120; -} - diff --git a/src/main/java/com/gxwebsoft/ai/controller/AiAnalyticsController.java b/src/main/java/com/gxwebsoft/ai/controller/AiAnalyticsController.java deleted file mode 100644 index fe72b49..0000000 --- a/src/main/java/com/gxwebsoft/ai/controller/AiAnalyticsController.java +++ /dev/null @@ -1,37 +0,0 @@ -package com.gxwebsoft.ai.controller; - -import com.gxwebsoft.ai.dto.*; -import com.gxwebsoft.ai.service.AiAnalyticsService; -import com.gxwebsoft.common.core.web.ApiResult; -import com.gxwebsoft.common.core.web.BaseController; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.tags.Tag; -import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.web.bind.annotation.*; - -import javax.annotation.Resource; - -@Tag(name = "AI - 订单数据分析") -@RestController -@RequestMapping("/api/ai/analytics") -public class AiAnalyticsController extends BaseController { - @Resource - private AiAnalyticsService analyticsService; - - @PreAuthorize("isAuthenticated()") - @Operation(summary = "查询商城订单按天指标(当前租户)") - @PostMapping("/query") - public ApiResult query(@RequestBody AiShopMetricsQueryRequest request) { - Integer tenantId = getTenantId(); - return success(analyticsService.queryShopMetrics(tenantId, request)); - } - - @PreAuthorize("isAuthenticated()") - @Operation(summary = "AI 解析并输出订单分析结论(当前租户)") - @PostMapping("/ask") - public ApiResult ask(@RequestBody AiAnalyticsAskRequest request) { - Integer tenantId = getTenantId(); - return success(analyticsService.askShopAnalytics(tenantId, request)); - } -} - diff --git a/src/main/java/com/gxwebsoft/ai/controller/AiChatController.java b/src/main/java/com/gxwebsoft/ai/controller/AiChatController.java deleted file mode 100644 index 45d5564..0000000 --- a/src/main/java/com/gxwebsoft/ai/controller/AiChatController.java +++ /dev/null @@ -1,95 +0,0 @@ -package com.gxwebsoft.ai.controller; - -import com.gxwebsoft.ai.client.OllamaClient; -import com.gxwebsoft.ai.client.dto.OllamaChatResponse; -import com.gxwebsoft.ai.client.dto.OllamaTagsResponse; -import com.gxwebsoft.ai.dto.AiChatRequest; -import com.gxwebsoft.ai.dto.AiChatResult; -import com.gxwebsoft.ai.dto.AiMessage; -import com.gxwebsoft.ai.service.AiChatService; -import com.gxwebsoft.common.core.web.ApiResult; -import com.gxwebsoft.common.core.web.BaseController; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.tags.Tag; -import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.web.bind.annotation.*; -import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; - -import javax.annotation.Resource; -import java.util.Collections; -import java.util.LinkedHashMap; -import java.util.Map; -import java.util.concurrent.CompletableFuture; - -@Tag(name = "AI - 对话") -@RestController -@RequestMapping("/api/ai") -public class AiChatController extends BaseController { - @Resource - private OllamaClient ollamaClient; - - @Resource - private AiChatService aiChatService; - - @PreAuthorize("isAuthenticated()") - @Operation(summary = "获取 Ollama 模型列表") - @GetMapping("/models") - public ApiResult models() { - return success(ollamaClient.tags()); - } - - @PreAuthorize("isAuthenticated()") - @Operation(summary = "非流式对话") - @PostMapping("/chat") - public ApiResult chat(@RequestBody AiChatRequest request) { - return success(aiChatService.chat(request)); - } - - @PreAuthorize("isAuthenticated()") - @Operation(summary = "流式对话(SSE)") - @PostMapping("/chat/stream") - public SseEmitter chatStream(@RequestBody AiChatRequest request) { - // 10 分钟超时(可根据前端需要调整) - SseEmitter emitter = new SseEmitter(10 * 60 * 1000L); - - CompletableFuture.runAsync(() -> { - try { - aiChatService.chatStream(request, - delta -> { - try { - emitter.send(SseEmitter.event().name("delta").data(delta)); - } catch (Exception e) { - // 客户端断开会触发 send 异常,这里直接结束即可 - emitter.complete(); - } - }, - (OllamaChatResponse done) -> { - try { - Map meta = new LinkedHashMap<>(); - meta.put("model", done.getModel()); - meta.put("prompt_eval_count", done.getPromptEvalCount()); - meta.put("eval_count", done.getEvalCount()); - meta.put("total_duration", done.getTotalDuration()); - emitter.send(SseEmitter.event().name("done").data(meta)); - } catch (Exception ignored) { - } finally { - emitter.complete(); - } - }); - } catch (Exception e) { - emitter.completeWithError(e); - } - }); - - return emitter; - } - - @PreAuthorize("isAuthenticated()") - @Operation(summary = "流式对话(SSE, GET 版本,便于 EventSource)") - @GetMapping("/chat/stream") - public SseEmitter chatStreamGet(@RequestParam("prompt") String prompt) { - AiChatRequest req = new AiChatRequest(); - req.setMessages(Collections.singletonList(new AiMessage("user", prompt))); - return chatStream(req); - } -} diff --git a/src/main/java/com/gxwebsoft/ai/controller/AiKbController.java b/src/main/java/com/gxwebsoft/ai/controller/AiKbController.java deleted file mode 100644 index afd2078..0000000 --- a/src/main/java/com/gxwebsoft/ai/controller/AiKbController.java +++ /dev/null @@ -1,54 +0,0 @@ -package com.gxwebsoft.ai.controller; - -import com.gxwebsoft.ai.dto.*; -import com.gxwebsoft.ai.service.AiKbRagService; -import com.gxwebsoft.common.core.web.ApiResult; -import com.gxwebsoft.common.core.web.BaseController; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.tags.Tag; -import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.web.bind.annotation.*; -import org.springframework.web.multipart.MultipartFile; - -import javax.annotation.Resource; - -@Tag(name = "AI - 知识库(RAG)") -@RestController -@RequestMapping("/api/ai/kb") -public class AiKbController extends BaseController { - @Resource - private AiKbRagService ragService; - - @PreAuthorize("isAuthenticated()") - @Operation(summary = "上传文档入库(txt/md/html 优先)") - @PostMapping("/upload") - public ApiResult upload(@RequestParam("file") MultipartFile file) { - Integer tenantId = getTenantId(); - return success(ragService.ingestUpload(tenantId, file)); - } - - @PreAuthorize("isAuthenticated()") - @Operation(summary = "同步 CMS 文章到知识库(当前租户)") - @PostMapping("/sync/cms") - public ApiResult syncCms() { - Integer tenantId = getTenantId(); - return success(ragService.syncCms(tenantId)); - } - - @PreAuthorize("isAuthenticated()") - @Operation(summary = "仅检索(返回 topK 命中)") - @PostMapping("/query") - public ApiResult query(@RequestBody AiKbQueryRequest request) { - Integer tenantId = getTenantId(); - return success(ragService.query(tenantId, request)); - } - - @PreAuthorize("isAuthenticated()") - @Operation(summary = "知识库问答(RAG 生成 + 返回检索结果)") - @PostMapping("/ask") - public ApiResult ask(@RequestBody AiKbAskRequest request) { - Integer tenantId = getTenantId(); - return success(ragService.ask(tenantId, request)); - } -} - diff --git a/src/main/java/com/gxwebsoft/ai/dto/AiAnalyticsAskRequest.java b/src/main/java/com/gxwebsoft/ai/dto/AiAnalyticsAskRequest.java deleted file mode 100644 index 085472a..0000000 --- a/src/main/java/com/gxwebsoft/ai/dto/AiAnalyticsAskRequest.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.gxwebsoft.ai.dto; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -@Data -@Schema(name = "AiAnalyticsAskRequest", description = "AI 数据分析提问") -public class AiAnalyticsAskRequest { - private String question; - private String startDate; - private String endDate; -} - diff --git a/src/main/java/com/gxwebsoft/ai/dto/AiAnalyticsAskResult.java b/src/main/java/com/gxwebsoft/ai/dto/AiAnalyticsAskResult.java deleted file mode 100644 index f11b61d..0000000 --- a/src/main/java/com/gxwebsoft/ai/dto/AiAnalyticsAskResult.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.gxwebsoft.ai.dto; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -@Data -@Schema(name = "AiAnalyticsAskResult", description = "AI 数据分析结果") -public class AiAnalyticsAskResult { - private String analysis; - private AiShopMetricsQueryResult data; -} - diff --git a/src/main/java/com/gxwebsoft/ai/dto/AiChatRequest.java b/src/main/java/com/gxwebsoft/ai/dto/AiChatRequest.java deleted file mode 100644 index eaa90ff..0000000 --- a/src/main/java/com/gxwebsoft/ai/dto/AiChatRequest.java +++ /dev/null @@ -1,35 +0,0 @@ -package com.gxwebsoft.ai.dto; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -import java.util.List; - -@Data -@Schema(name = "AiChatRequest", description = "AI 对话请求") -public class AiChatRequest { - @Schema(description = "可选:直接传一句话。若 messages 为空则使用该字段构造 user message") - private String prompt; - - @Schema(description = "可选:OpenAI 风格 messages(role: system/user/assistant)") - private List messages; - - @Schema(description = "可选:覆盖默认模型") - private String model; - - @Schema(description = "是否流式输出(/chat/stream 端点通常忽略此字段)") - private Boolean stream; - - @Schema(description = "temperature") - private Double temperature; - - @Schema(description = "top_k") - private Integer topK; - - @Schema(description = "top_p") - private Double topP; - - @Schema(description = "num_predict(类似 max_tokens)") - private Integer numPredict; -} - diff --git a/src/main/java/com/gxwebsoft/ai/dto/AiChatResult.java b/src/main/java/com/gxwebsoft/ai/dto/AiChatResult.java deleted file mode 100644 index c38dd41..0000000 --- a/src/main/java/com/gxwebsoft/ai/dto/AiChatResult.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.gxwebsoft.ai.dto; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.NoArgsConstructor; - -@Data -@NoArgsConstructor -@AllArgsConstructor -@Schema(name = "AiChatResult", description = "AI 对话结果") -public class AiChatResult { - private String content; -} - diff --git a/src/main/java/com/gxwebsoft/ai/dto/AiKbAskRequest.java b/src/main/java/com/gxwebsoft/ai/dto/AiKbAskRequest.java deleted file mode 100644 index 199134c..0000000 --- a/src/main/java/com/gxwebsoft/ai/dto/AiKbAskRequest.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.gxwebsoft.ai.dto; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -@Data -@Schema(name = "AiKbAskRequest", description = "知识库问答请求") -public class AiKbAskRequest { - private String question; - private Integer topK; -} - diff --git a/src/main/java/com/gxwebsoft/ai/dto/AiKbAskResult.java b/src/main/java/com/gxwebsoft/ai/dto/AiKbAskResult.java deleted file mode 100644 index a23a480..0000000 --- a/src/main/java/com/gxwebsoft/ai/dto/AiKbAskResult.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.gxwebsoft.ai.dto; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -@Data -@Schema(name = "AiKbAskResult", description = "知识库问答结果") -public class AiKbAskResult { - private String answer; - private AiKbQueryResult retrieval; -} - diff --git a/src/main/java/com/gxwebsoft/ai/dto/AiKbHit.java b/src/main/java/com/gxwebsoft/ai/dto/AiKbHit.java deleted file mode 100644 index e204685..0000000 --- a/src/main/java/com/gxwebsoft/ai/dto/AiKbHit.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.gxwebsoft.ai.dto; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -@Data -@Schema(name = "AiKbHit", description = "知识库命中") -public class AiKbHit { - private String chunkId; - private Integer documentId; - private String title; - private Double score; - private String content; -} - diff --git a/src/main/java/com/gxwebsoft/ai/dto/AiKbIngestResult.java b/src/main/java/com/gxwebsoft/ai/dto/AiKbIngestResult.java deleted file mode 100644 index c14778d..0000000 --- a/src/main/java/com/gxwebsoft/ai/dto/AiKbIngestResult.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.gxwebsoft.ai.dto; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -@Data -@Schema(name = "AiKbIngestResult", description = "知识库入库结果") -public class AiKbIngestResult { - private Integer documentId; - private String title; - private Integer chunks; - private Integer updatedDocuments; - private Integer skippedDocuments; -} - diff --git a/src/main/java/com/gxwebsoft/ai/dto/AiKbQueryRequest.java b/src/main/java/com/gxwebsoft/ai/dto/AiKbQueryRequest.java deleted file mode 100644 index c4ad3c6..0000000 --- a/src/main/java/com/gxwebsoft/ai/dto/AiKbQueryRequest.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.gxwebsoft.ai.dto; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -@Data -@Schema(name = "AiKbQueryRequest", description = "知识库检索请求") -public class AiKbQueryRequest { - private String query; - private Integer topK; -} - diff --git a/src/main/java/com/gxwebsoft/ai/dto/AiKbQueryResult.java b/src/main/java/com/gxwebsoft/ai/dto/AiKbQueryResult.java deleted file mode 100644 index 33b082e..0000000 --- a/src/main/java/com/gxwebsoft/ai/dto/AiKbQueryResult.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.gxwebsoft.ai.dto; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -import java.util.List; - -@Data -@Schema(name = "AiKbQueryResult", description = "知识库检索结果") -public class AiKbQueryResult { - private List hits; -} - diff --git a/src/main/java/com/gxwebsoft/ai/dto/AiMessage.java b/src/main/java/com/gxwebsoft/ai/dto/AiMessage.java deleted file mode 100644 index 1e9ba61..0000000 --- a/src/main/java/com/gxwebsoft/ai/dto/AiMessage.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.gxwebsoft.ai.dto; - -import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.NoArgsConstructor; - -@Data -@NoArgsConstructor -@AllArgsConstructor -public class AiMessage { - private String role; - private String content; -} - diff --git a/src/main/java/com/gxwebsoft/ai/dto/AiShopMetricsQueryRequest.java b/src/main/java/com/gxwebsoft/ai/dto/AiShopMetricsQueryRequest.java deleted file mode 100644 index ba31384..0000000 --- a/src/main/java/com/gxwebsoft/ai/dto/AiShopMetricsQueryRequest.java +++ /dev/null @@ -1,18 +0,0 @@ -package com.gxwebsoft.ai.dto; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -@Data -@Schema(name = "AiShopMetricsQueryRequest", description = "商城订单指标查询请求") -public class AiShopMetricsQueryRequest { - @Schema(description = "开始日期(YYYY-MM-DD)") - private String startDate; - - @Schema(description = "结束日期(YYYY-MM-DD),包含该天") - private String endDate; - - @Schema(description = "是否按天分组,默认 true") - private Boolean groupByDay; -} - diff --git a/src/main/java/com/gxwebsoft/ai/dto/AiShopMetricsQueryResult.java b/src/main/java/com/gxwebsoft/ai/dto/AiShopMetricsQueryResult.java deleted file mode 100644 index f4086cc..0000000 --- a/src/main/java/com/gxwebsoft/ai/dto/AiShopMetricsQueryResult.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.gxwebsoft.ai.dto; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -import java.util.List; - -@Data -@Schema(name = "AiShopMetricsQueryResult", description = "商城订单指标查询结果") -public class AiShopMetricsQueryResult { - private Integer tenantId; - private String startDate; - private String endDate; - private List rows; -} - diff --git a/src/main/java/com/gxwebsoft/ai/dto/AiShopMetricsRow.java b/src/main/java/com/gxwebsoft/ai/dto/AiShopMetricsRow.java deleted file mode 100644 index 747ecdf..0000000 --- a/src/main/java/com/gxwebsoft/ai/dto/AiShopMetricsRow.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.gxwebsoft.ai.dto; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -import java.math.BigDecimal; - -@Data -@Schema(name = "AiShopMetricsRow", description = "商城订单指标行(按 tenant/day)") -public class AiShopMetricsRow { - private Integer tenantId; - private String day; - - private Long orderCnt; - private Long paidOrderCnt; - private BigDecimal gmv; - private BigDecimal refundAmt; - private Long payUserCnt; - - private BigDecimal aov; - private BigDecimal payRate; -} - diff --git a/src/main/java/com/gxwebsoft/ai/entity/AiKbChunk.java b/src/main/java/com/gxwebsoft/ai/entity/AiKbChunk.java deleted file mode 100644 index 47ffd0c..0000000 --- a/src/main/java/com/gxwebsoft/ai/entity/AiKbChunk.java +++ /dev/null @@ -1,57 +0,0 @@ -package com.gxwebsoft.ai.entity; - -import com.baomidou.mybatisplus.annotation.IdType; -import com.baomidou.mybatisplus.annotation.TableId; -import com.baomidou.mybatisplus.annotation.TableLogic; -import com.fasterxml.jackson.annotation.JsonFormat; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -import java.io.Serializable; -import java.time.LocalDateTime; - -/** - * 知识库分段(chunk)。 - */ -@Data -@Schema(name = "AiKbChunk", description = "AI 知识库分段") -public class AiKbChunk implements Serializable { - private static final long serialVersionUID = 1L; - - @TableId(value = "id", type = IdType.AUTO) - private Integer id; - - private Integer documentId; - - /** - * 外部引用用的唯一 ID(便于在答案里引用)。 - */ - private String chunkId; - - private Integer chunkIndex; - - private String title; - - private String content; - - private String contentHash; - - /** - * embedding JSON(数组),存成文本便于快速落库。 - */ - private String embedding; - - /** - * embedding 的 L2 范数,用于余弦相似度。 - */ - private Double embeddingNorm; - - @TableLogic - private Integer deleted; - - private Integer tenantId; - - @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") - private LocalDateTime createTime; -} - diff --git a/src/main/java/com/gxwebsoft/ai/entity/AiKbDocument.java b/src/main/java/com/gxwebsoft/ai/entity/AiKbDocument.java deleted file mode 100644 index dac0c3b..0000000 --- a/src/main/java/com/gxwebsoft/ai/entity/AiKbDocument.java +++ /dev/null @@ -1,59 +0,0 @@ -package com.gxwebsoft.ai.entity; - -import com.baomidou.mybatisplus.annotation.IdType; -import com.baomidou.mybatisplus.annotation.TableId; -import com.baomidou.mybatisplus.annotation.TableLogic; -import com.fasterxml.jackson.annotation.JsonFormat; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -import java.io.Serializable; -import java.time.LocalDateTime; - -/** - * 知识库文档(来源:上传、CMS 等)。 - */ -@Data -@Schema(name = "AiKbDocument", description = "AI 知识库文档") -public class AiKbDocument implements Serializable { - private static final long serialVersionUID = 1L; - - @TableId(value = "document_id", type = IdType.AUTO) - private Integer documentId; - - private String title; - - /** - * upload / cms - */ - private String sourceType; - - /** - * 例如 CMS article_id - */ - private Integer sourceId; - - /** - * 例如文件名、路径等 - */ - private String sourceRef; - - /** - * 文档文本内容哈希(用于增量同步/去重)。 - */ - private String contentHash; - - private Integer status; - - @TableLogic - private Integer deleted; - - private Integer tenantId; - - @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") - private LocalDateTime updateTime; - - @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") - private LocalDateTime createTime; -} - diff --git a/src/main/java/com/gxwebsoft/ai/mapper/AiKbChunkMapper.java b/src/main/java/com/gxwebsoft/ai/mapper/AiKbChunkMapper.java deleted file mode 100644 index 1628de5..0000000 --- a/src/main/java/com/gxwebsoft/ai/mapper/AiKbChunkMapper.java +++ /dev/null @@ -1,10 +0,0 @@ -package com.gxwebsoft.ai.mapper; - -import com.baomidou.mybatisplus.core.mapper.BaseMapper; -import com.gxwebsoft.ai.entity.AiKbChunk; -import org.apache.ibatis.annotations.Mapper; - -@Mapper -public interface AiKbChunkMapper extends BaseMapper { -} - diff --git a/src/main/java/com/gxwebsoft/ai/mapper/AiKbDocumentMapper.java b/src/main/java/com/gxwebsoft/ai/mapper/AiKbDocumentMapper.java deleted file mode 100644 index 8dc61c6..0000000 --- a/src/main/java/com/gxwebsoft/ai/mapper/AiKbDocumentMapper.java +++ /dev/null @@ -1,10 +0,0 @@ -package com.gxwebsoft.ai.mapper; - -import com.baomidou.mybatisplus.core.mapper.BaseMapper; -import com.gxwebsoft.ai.entity.AiKbDocument; -import org.apache.ibatis.annotations.Mapper; - -@Mapper -public interface AiKbDocumentMapper extends BaseMapper { -} - diff --git a/src/main/java/com/gxwebsoft/ai/mapper/AiShopAnalyticsMapper.java b/src/main/java/com/gxwebsoft/ai/mapper/AiShopAnalyticsMapper.java deleted file mode 100644 index 27221ac..0000000 --- a/src/main/java/com/gxwebsoft/ai/mapper/AiShopAnalyticsMapper.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.gxwebsoft.ai.mapper; - -import com.gxwebsoft.ai.dto.AiShopMetricsRow; -import org.apache.ibatis.annotations.Mapper; -import org.apache.ibatis.annotations.Param; - -import java.time.LocalDateTime; -import java.util.List; - -@Mapper -public interface AiShopAnalyticsMapper { - List queryMetrics(@Param("tenantId") Integer tenantId, - @Param("start") LocalDateTime start, - @Param("end") LocalDateTime end); -} - diff --git a/src/main/java/com/gxwebsoft/ai/mapper/xml/AiShopAnalyticsMapper.xml b/src/main/java/com/gxwebsoft/ai/mapper/xml/AiShopAnalyticsMapper.xml deleted file mode 100644 index 0e745b6..0000000 --- a/src/main/java/com/gxwebsoft/ai/mapper/xml/AiShopAnalyticsMapper.xml +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - - - diff --git a/src/main/java/com/gxwebsoft/ai/prompt/AiPrompts.java b/src/main/java/com/gxwebsoft/ai/prompt/AiPrompts.java deleted file mode 100644 index 106ae56..0000000 --- a/src/main/java/com/gxwebsoft/ai/prompt/AiPrompts.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.gxwebsoft.ai.prompt; - -/** - * 统一提示词模板(尽量简短、可控)。 - */ -public class AiPrompts { - private AiPrompts() { - } - - public static final String SYSTEM_SUPPORT = - "你是 WebSoft 客服AI。规则:\n" + - "- 只使用给定的“上下文资料”回答,禁止编造。\n" + - "- 如果资料不足,直接说“资料不足”,并列出需要补充的信息。\n" + - "- 答案末尾必须给引用,格式:[source:chunk_id]。\n" + - "- 输出中文,简洁可执行。\n"; - - public static final String SYSTEM_ANALYTICS = - "你是商城订单数据分析助手。你将基于提供的按天指标数据给出结论。\n" + - "要求:\n" + - "- 只基于数据陈述,不要编造不存在的数字。\n" + - "- 输出包含:结论、关键指标变化、异常点、建议的下一步核查。\n" + - "- 输出中文,简洁。\n"; -} - diff --git a/src/main/java/com/gxwebsoft/ai/service/AiAnalyticsService.java b/src/main/java/com/gxwebsoft/ai/service/AiAnalyticsService.java deleted file mode 100644 index 0f37aec..0000000 --- a/src/main/java/com/gxwebsoft/ai/service/AiAnalyticsService.java +++ /dev/null @@ -1,82 +0,0 @@ -package com.gxwebsoft.ai.service; - -import cn.hutool.core.util.StrUtil; -import com.gxwebsoft.ai.dto.*; -import com.gxwebsoft.ai.prompt.AiPrompts; -import com.gxwebsoft.common.core.exception.BusinessException; -import com.gxwebsoft.common.core.utils.JSONUtil; -import org.springframework.stereotype.Service; - -import javax.annotation.Resource; -import java.time.LocalDate; -import java.time.format.DateTimeParseException; -import java.util.Arrays; - -@Service -public class AiAnalyticsService { - @Resource - private AiShopAnalyticsService shopAnalyticsService; - @Resource - private AiChatService aiChatService; - - public AiShopMetricsQueryResult queryShopMetrics(Integer tenantId, AiShopMetricsQueryRequest request) { - if (tenantId == null) { - throw new BusinessException("tenantId 不能为空"); - } - if (request == null) { - throw new BusinessException("请求不能为空"); - } - LocalDate start = parseDate(request.getStartDate(), "startDate"); - LocalDate end = parseDate(request.getEndDate(), "endDate"); - if (end.isBefore(start)) { - throw new BusinessException("endDate 不能早于 startDate"); - } - - AiShopMetricsQueryResult r = new AiShopMetricsQueryResult(); - r.setTenantId(tenantId); - r.setStartDate(start.toString()); - r.setEndDate(end.toString()); - r.setRows(shopAnalyticsService.queryTenantDaily(tenantId, start, end)); - return r; - } - - public AiAnalyticsAskResult askShopAnalytics(Integer tenantId, AiAnalyticsAskRequest request) { - if (request == null || StrUtil.isBlank(request.getQuestion())) { - throw new BusinessException("question 不能为空"); - } - AiShopMetricsQueryRequest q = new AiShopMetricsQueryRequest(); - q.setStartDate(request.getStartDate()); - q.setEndDate(request.getEndDate()); - q.setGroupByDay(true); - AiShopMetricsQueryResult data = queryShopMetrics(tenantId, q); - - String userPrompt = - "用户问题:\n" + request.getQuestion() + "\n\n" + - "数据(JSON,字段含义:order_cnt=订单数,paid_order_cnt=已支付订单数,gmv=已支付金额,refund_amt=退款金额,pay_user_cnt=支付用户数,aov=客单价,pay_rate=支付率):\n" + - JSONUtil.toJSONString(data, true); - - AiChatRequest chat = new AiChatRequest(); - chat.setMessages(Arrays.asList( - new AiMessage("system", AiPrompts.SYSTEM_ANALYTICS), - new AiMessage("user", userPrompt) - )); - - AiChatResult resp = aiChatService.chat(chat); - AiAnalyticsAskResult r = new AiAnalyticsAskResult(); - r.setAnalysis(resp.getContent()); - r.setData(data); - return r; - } - - private static LocalDate parseDate(String s, String field) { - if (StrUtil.isBlank(s)) { - throw new BusinessException(field + " 不能为空,格式 YYYY-MM-DD"); - } - try { - return LocalDate.parse(s.trim()); - } catch (DateTimeParseException e) { - throw new BusinessException(field + " 格式错误,需 YYYY-MM-DD"); - } - } -} - diff --git a/src/main/java/com/gxwebsoft/ai/service/AiChatService.java b/src/main/java/com/gxwebsoft/ai/service/AiChatService.java deleted file mode 100644 index c457607..0000000 --- a/src/main/java/com/gxwebsoft/ai/service/AiChatService.java +++ /dev/null @@ -1,94 +0,0 @@ -package com.gxwebsoft.ai.service; - -import cn.hutool.core.util.StrUtil; -import com.gxwebsoft.ai.client.OllamaClient; -import com.gxwebsoft.ai.client.dto.OllamaChatRequest; -import com.gxwebsoft.ai.client.dto.OllamaChatResponse; -import com.gxwebsoft.ai.client.dto.OllamaMessage; -import com.gxwebsoft.ai.config.AiOllamaProperties; -import com.gxwebsoft.ai.dto.AiChatRequest; -import com.gxwebsoft.ai.dto.AiChatResult; -import com.gxwebsoft.ai.dto.AiMessage; -import com.gxwebsoft.common.core.exception.BusinessException; -import org.springframework.stereotype.Service; - -import javax.annotation.Resource; -import java.util.*; -import java.util.function.Consumer; - -@Service -public class AiChatService { - @Resource - private AiOllamaProperties props; - - @Resource - private OllamaClient ollamaClient; - - public AiChatResult chat(AiChatRequest request) { - OllamaChatRequest req = buildChatRequest(request, false); - OllamaChatResponse resp = ollamaClient.chat(req); - String content = resp != null && resp.getMessage() != null ? resp.getMessage().getContent() : null; - return new AiChatResult(content == null ? "" : content); - } - - public void chatStream(AiChatRequest request, Consumer onDelta, Consumer onFinal) { - Objects.requireNonNull(onDelta, "onDelta"); - OllamaChatRequest req = buildChatRequest(request, true); - ollamaClient.chatStream(req, event -> { - String delta = event != null && event.getMessage() != null ? event.getMessage().getContent() : null; - if (StrUtil.isNotBlank(delta)) { - onDelta.accept(delta); - } - if (Boolean.TRUE.equals(event.getDone()) && onFinal != null) { - onFinal.accept(event); - } - }); - } - - private OllamaChatRequest buildChatRequest(AiChatRequest request, boolean stream) { - if (request == null) { - throw new BusinessException("请求不能为空"); - } - - List messages = request.getMessages(); - if ((messages == null || messages.isEmpty()) && StrUtil.isBlank(request.getPrompt())) { - throw new BusinessException("prompt 或 messages 不能为空"); - } - if (messages == null || messages.isEmpty()) { - messages = Collections.singletonList(new AiMessage("user", request.getPrompt())); - } - - List ollamaMessages = new ArrayList<>(); - for (AiMessage m : messages) { - if (m == null || StrUtil.isBlank(m.getRole()) || m.getContent() == null) { - continue; - } - ollamaMessages.add(new OllamaMessage(m.getRole(), m.getContent())); - } - if (ollamaMessages.isEmpty()) { - throw new BusinessException("messages 为空或无有效内容"); - } - - Map options = new HashMap<>(); - if (request.getTemperature() != null) { - options.put("temperature", request.getTemperature()); - } - if (request.getTopK() != null) { - options.put("top_k", request.getTopK()); - } - if (request.getTopP() != null) { - options.put("top_p", request.getTopP()); - } - if (request.getNumPredict() != null) { - options.put("num_predict", request.getNumPredict()); - } - - OllamaChatRequest req = new OllamaChatRequest(); - req.setModel(StrUtil.blankToDefault(request.getModel(), props.getChatModel())); - req.setMessages(ollamaMessages); - req.setStream(stream); - req.setOptions(options.isEmpty() ? null : options); - return req; - } -} - diff --git a/src/main/java/com/gxwebsoft/ai/service/AiKbChunkService.java b/src/main/java/com/gxwebsoft/ai/service/AiKbChunkService.java deleted file mode 100644 index 0bcc717..0000000 --- a/src/main/java/com/gxwebsoft/ai/service/AiKbChunkService.java +++ /dev/null @@ -1,8 +0,0 @@ -package com.gxwebsoft.ai.service; - -import com.baomidou.mybatisplus.extension.service.IService; -import com.gxwebsoft.ai.entity.AiKbChunk; - -public interface AiKbChunkService extends IService { -} - diff --git a/src/main/java/com/gxwebsoft/ai/service/AiKbDocumentService.java b/src/main/java/com/gxwebsoft/ai/service/AiKbDocumentService.java deleted file mode 100644 index 601f778..0000000 --- a/src/main/java/com/gxwebsoft/ai/service/AiKbDocumentService.java +++ /dev/null @@ -1,8 +0,0 @@ -package com.gxwebsoft.ai.service; - -import com.baomidou.mybatisplus.extension.service.IService; -import com.gxwebsoft.ai.entity.AiKbDocument; - -public interface AiKbDocumentService extends IService { -} - diff --git a/src/main/java/com/gxwebsoft/ai/service/AiKbRagService.java b/src/main/java/com/gxwebsoft/ai/service/AiKbRagService.java deleted file mode 100644 index 04c6c1f..0000000 --- a/src/main/java/com/gxwebsoft/ai/service/AiKbRagService.java +++ /dev/null @@ -1,426 +0,0 @@ -package com.gxwebsoft.ai.service; - -import cn.hutool.core.util.ObjectUtil; -import cn.hutool.core.util.StrUtil; -import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; -import com.gxwebsoft.ai.client.OllamaClient; -import com.gxwebsoft.ai.client.dto.OllamaEmbeddingResponse; -import com.gxwebsoft.ai.config.AiOllamaProperties; -import com.gxwebsoft.ai.dto.*; -import com.gxwebsoft.ai.entity.AiKbChunk; -import com.gxwebsoft.ai.entity.AiKbDocument; -import com.gxwebsoft.ai.prompt.AiPrompts; -import com.gxwebsoft.ai.util.AiTextUtil; -import com.gxwebsoft.cms.entity.CmsArticle; -import com.gxwebsoft.cms.entity.CmsArticleContent; -import com.gxwebsoft.cms.service.CmsArticleContentService; -import com.gxwebsoft.cms.service.CmsArticleService; -import com.gxwebsoft.common.core.exception.BusinessException; -import com.gxwebsoft.common.core.utils.JSONUtil; -import com.fasterxml.jackson.databind.ObjectMapper; -import org.apache.tika.Tika; -import org.springframework.stereotype.Service; -import org.springframework.web.multipart.MultipartFile; - -import javax.annotation.Resource; -import java.nio.charset.StandardCharsets; -import java.time.LocalDateTime; -import java.util.*; -import java.util.concurrent.*; -import java.util.stream.Collectors; - -@Service -public class AiKbRagService { - @Resource - private AiOllamaProperties props; - @Resource - private OllamaClient ollamaClient; - @Resource - private AiKbDocumentService documentService; - @Resource - private AiKbChunkService chunkService; - @Resource - private CmsArticleService cmsArticleService; - @Resource - private CmsArticleContentService cmsArticleContentService; - @Resource - private AiChatService aiChatService; - @Resource - private ObjectMapper objectMapper; - - private final Tika tika = new Tika(); - private volatile ExecutorService embedPool; - - private ExecutorService pool() { - ExecutorService p = embedPool; - if (p != null) { - return p; - } - synchronized (this) { - if (embedPool == null) { - embedPool = Executors.newFixedThreadPool(Math.max(1, props.getMaxConcurrency())); - } - return embedPool; - } - } - - public AiKbIngestResult ingestUpload(Integer tenantId, MultipartFile file) { - if (tenantId == null) { - throw new BusinessException("tenantId 不能为空"); - } - if (file == null || file.isEmpty()) { - throw new BusinessException("文件不能为空"); - } - String title = StrUtil.blankToDefault(file.getOriginalFilename(), "upload"); - String text = extractText(file); - if (StrUtil.isBlank(text)) { - throw new BusinessException("无法解析文件内容,请上传 txt/md/html 等可解析文本"); - } - - String contentHash = AiTextUtil.sha256(text); - AiKbDocument doc = new AiKbDocument(); - doc.setTitle(title); - doc.setSourceType("upload"); - doc.setSourceRef(title); - doc.setContentHash(contentHash); - doc.setStatus(0); - doc.setTenantId(tenantId); - doc.setCreateTime(LocalDateTime.now()); - doc.setUpdateTime(LocalDateTime.now()); - documentService.save(doc); - - int chunks = ingestChunks(doc, text); - - AiKbIngestResult r = new AiKbIngestResult(); - r.setDocumentId(doc.getDocumentId()); - r.setTitle(doc.getTitle()); - r.setChunks(chunks); - r.setUpdatedDocuments(1); - r.setSkippedDocuments(0); - return r; - } - - /** - * 同步 CMS(仅当前 tenant)。 - */ - public AiKbIngestResult syncCms(Integer tenantId) { - if (tenantId == null) { - throw new BusinessException("tenantId 不能为空"); - } - // 仅同步“已发布且未删除”的文章 - List articles = cmsArticleService.list(new LambdaQueryWrapper() - .eq(CmsArticle::getTenantId, tenantId) - .eq(CmsArticle::getDeleted, 0) - .eq(CmsArticle::getStatus, 0)); - if (articles == null || articles.isEmpty()) { - AiKbIngestResult r = new AiKbIngestResult(); - r.setUpdatedDocuments(0); - r.setSkippedDocuments(0); - r.setChunks(0); - return r; - } - - Set articleIds = articles.stream().map(CmsArticle::getArticleId).collect(Collectors.toSet()); - List contents = cmsArticleContentService.list(new LambdaQueryWrapper() - .in(CmsArticleContent::getArticleId, articleIds)); - - Map contentByArticle = contents.stream() - .collect(Collectors.toMap( - CmsArticleContent::getArticleId, - c -> c, - (a, b) -> (a.getCreateTime() != null && b.getCreateTime() != null && a.getCreateTime().isAfter(b.getCreateTime())) ? a : b - )); - - int updatedDocs = 0; - int skippedDocs = 0; - int totalChunks = 0; - - for (CmsArticle a : articles) { - CmsArticleContent c = contentByArticle.get(a.getArticleId()); - String raw = ""; - if (a.getOverview() != null) { - raw += a.getOverview() + "\n"; - } - if (c != null && c.getContent() != null) { - raw += c.getContent(); - } - String text = a.getTitle() + "\n" + AiTextUtil.stripHtml(raw); - text = AiTextUtil.normalizeWhitespace(text); - if (StrUtil.isBlank(text)) { - continue; - } - String hash = AiTextUtil.sha256(text); - - AiKbDocument existing = documentService.getOne(new LambdaQueryWrapper() - .eq(AiKbDocument::getTenantId, tenantId) - .eq(AiKbDocument::getSourceType, "cms") - .eq(AiKbDocument::getSourceId, a.getArticleId()) - .last("limit 1")); - - if (existing != null && StrUtil.equals(existing.getContentHash(), hash)) { - skippedDocs++; - continue; - } - - AiKbDocument doc; - if (existing == null) { - doc = new AiKbDocument(); - doc.setTitle(a.getTitle()); - doc.setSourceType("cms"); - doc.setSourceId(a.getArticleId()); - doc.setSourceRef(a.getCode()); - doc.setContentHash(hash); - doc.setStatus(0); - doc.setTenantId(tenantId); - doc.setCreateTime(LocalDateTime.now()); - doc.setUpdateTime(LocalDateTime.now()); - documentService.save(doc); - } else { - doc = existing; - doc.setTitle(a.getTitle()); - doc.setSourceRef(a.getCode()); - doc.setContentHash(hash); - doc.setUpdateTime(LocalDateTime.now()); - documentService.updateById(doc); - // 重新入库:先删除旧 chunk - chunkService.remove(new LambdaQueryWrapper().eq(AiKbChunk::getDocumentId, doc.getDocumentId())); - } - - int chunks = ingestChunks(doc, text); - totalChunks += chunks; - updatedDocs++; - } - - AiKbIngestResult r = new AiKbIngestResult(); - r.setUpdatedDocuments(updatedDocs); - r.setSkippedDocuments(skippedDocs); - r.setChunks(totalChunks); - return r; - } - - public AiKbQueryResult query(Integer tenantId, AiKbQueryRequest request) { - if (tenantId == null) { - throw new BusinessException("tenantId 不能为空"); - } - if (request == null || StrUtil.isBlank(request.getQuery())) { - throw new BusinessException("query 不能为空"); - } - int topK = request.getTopK() != null ? request.getTopK() : props.getRagTopK(); - topK = Math.max(1, Math.min(20, topK)); - - float[] qEmb = embedding(request.getQuery()); - float qNorm = l2(qEmb); - if (qNorm == 0f) { - throw new BusinessException("query embedding 为空"); - } - - // MVP:按 tenant 拉取最新 N 条候选 chunk,再做余弦相似度排序 - List candidates = chunkService.list(new LambdaQueryWrapper() - .eq(AiKbChunk::getTenantId, tenantId) - .orderByDesc(AiKbChunk::getId) - .last("limit " + props.getRagMaxCandidates())); - - PriorityQueue pq = new PriorityQueue<>(Comparator.comparingDouble(h -> h.getScore() == null ? -1d : h.getScore())); - for (AiKbChunk c : candidates) { - if (StrUtil.isBlank(c.getEmbedding())) { - continue; - } - float[] cEmb = parseEmbedding(c.getEmbedding()); - if (cEmb == null || cEmb.length == 0) { - continue; - } - Double cNormD = c.getEmbeddingNorm(); - float cNorm = cNormD == null ? l2(cEmb) : cNormD.floatValue(); - if (cNorm == 0f) { - continue; - } - double score = dot(qEmb, cEmb) / (qNorm * cNorm); - AiKbHit hit = new AiKbHit(); - hit.setChunkId(c.getChunkId()); - hit.setDocumentId(c.getDocumentId()); - hit.setTitle(StrUtil.blankToDefault(c.getTitle(), "")); - hit.setScore(score); - // 返回给前端时避免过长 - hit.setContent(clip(c.getContent(), 900)); - - if (pq.size() < topK) { - pq.add(hit); - } else if (hit.getScore() != null && hit.getScore() > pq.peek().getScore()) { - pq.poll(); - pq.add(hit); - } - } - - List hits = new ArrayList<>(pq); - hits.sort((a, b) -> Double.compare(b.getScore(), a.getScore())); - AiKbQueryResult r = new AiKbQueryResult(); - r.setHits(hits); - return r; - } - - public AiKbAskResult ask(Integer tenantId, AiKbAskRequest request) { - if (request == null || StrUtil.isBlank(request.getQuestion())) { - throw new BusinessException("question 不能为空"); - } - AiKbQueryRequest q = new AiKbQueryRequest(); - q.setQuery(request.getQuestion()); - q.setTopK(request.getTopK()); - AiKbQueryResult retrieval = query(tenantId, q); - - String context = buildContext(retrieval); - String userPrompt = "上下文资料:\n" + context + "\n\n用户问题:\n" + request.getQuestion(); - - AiChatRequest chatReq = new AiChatRequest(); - chatReq.setMessages(Arrays.asList( - new AiMessage("system", AiPrompts.SYSTEM_SUPPORT), - new AiMessage("user", userPrompt) - )); - AiChatResult chat = aiChatService.chat(chatReq); - - AiKbAskResult r = new AiKbAskResult(); - r.setAnswer(chat.getContent()); - r.setRetrieval(retrieval); - return r; - } - - private int ingestChunks(AiKbDocument doc, String text) { - List chunks = AiTextUtil.chunkText(text, props.getRagChunkSize(), props.getRagChunkOverlap()); - if (chunks.isEmpty()) { - return 0; - } - LocalDateTime now = LocalDateTime.now(); - - List> futures = new ArrayList<>(chunks.size()); - for (int i = 0; i < chunks.size(); i++) { - final int idx = i; - final String chunkText = chunks.get(i); - futures.add(CompletableFuture.supplyAsync(() -> { - OllamaEmbeddingResponse emb = ollamaClient.embedding(chunkText); - if (emb == null || emb.getEmbedding() == null || emb.getEmbedding().isEmpty()) { - throw new BusinessException("embedding 生成失败"); - } - float[] v = toFloat(emb.getEmbedding()); - float norm = l2(v); - AiKbChunk c = new AiKbChunk(); - c.setDocumentId(doc.getDocumentId()); - c.setChunkId(UUID.randomUUID().toString().replace("-", "")); - c.setChunkIndex(idx); - c.setTitle(doc.getTitle()); - c.setContent(chunkText); - c.setContentHash(AiTextUtil.sha256(chunkText)); - c.setEmbedding(JSONUtil.toJSONString(emb.getEmbedding())); - c.setEmbeddingNorm((double) norm); - c.setTenantId(doc.getTenantId()); - c.setCreateTime(now); - c.setDeleted(0); - return c; - }, pool())); - } - - List entities = futures.stream().map(f -> { - try { - return f.get(10, TimeUnit.MINUTES); - } catch (Exception e) { - throw new BusinessException("embedding 批处理失败: " + e.getMessage()); - } - }).collect(Collectors.toList()); - - chunkService.saveBatch(entities); - return entities.size(); - } - - private String extractText(MultipartFile file) { - try { - String contentType = file.getContentType(); - String filename = file.getOriginalFilename(); - - // 优先:对纯文本直接读 UTF-8 - if ((contentType != null && contentType.startsWith("text/")) - || (filename != null && (filename.endsWith(".txt") || filename.endsWith(".md") || filename.endsWith(".html") || filename.endsWith(".htm")))) { - return AiTextUtil.normalizeWhitespace(new String(file.getBytes(), StandardCharsets.UTF_8)); - } - - // 尝试用 tika 解析(注意:当前依赖为 tika-core,解析能力有限) - String parsed = tika.parseToString(file.getInputStream()); - return AiTextUtil.normalizeWhitespace(parsed); - } catch (Exception e) { - return ""; - } - } - - private float[] embedding(String text) { - OllamaEmbeddingResponse emb = ollamaClient.embedding(text); - if (emb == null || emb.getEmbedding() == null || emb.getEmbedding().isEmpty()) { - throw new BusinessException("embedding 生成失败"); - } - return toFloat(emb.getEmbedding()); - } - - private float[] parseEmbedding(String json) { - try { - // embedding 是一维数组,存储为 JSON 文本 - double[] d = ObjectUtil.isEmpty(json) ? null : objectMapper.readValue(json, double[].class); - if (d == null || d.length == 0) { - return null; - } - float[] f = new float[d.length]; - for (int i = 0; i < d.length; i++) { - f[i] = (float) d[i]; - } - return f; - } catch (Exception e) { - return null; - } - } - - private static float[] toFloat(List v) { - float[] out = new float[v.size()]; - for (int i = 0; i < v.size(); i++) { - Double d = v.get(i); - out[i] = d == null ? 0f : d.floatValue(); - } - return out; - } - - private static float dot(float[] a, float[] b) { - int n = Math.min(a.length, b.length); - double s = 0d; - for (int i = 0; i < n; i++) { - s += (double) a[i] * (double) b[i]; - } - return (float) s; - } - - private static float l2(float[] a) { - double s = 0d; - for (float v : a) { - s += (double) v * (double) v; - } - return (float) Math.sqrt(s); - } - - private static String clip(String s, int max) { - if (s == null) { - return ""; - } - if (s.length() <= max) { - return s; - } - return s.substring(0, max) + "..."; - } - - private static String buildContext(AiKbQueryResult retrieval) { - if (retrieval == null || retrieval.getHits() == null || retrieval.getHits().isEmpty()) { - return "(无)"; - } - StringBuilder sb = new StringBuilder(); - for (AiKbHit h : retrieval.getHits()) { - sb.append("[source:").append(h.getChunkId()).append("] "); - if (StrUtil.isNotBlank(h.getTitle())) { - sb.append(h.getTitle()).append("\n"); - } - sb.append(h.getContent()).append("\n\n"); - } - return sb.toString(); - } -} diff --git a/src/main/java/com/gxwebsoft/ai/service/AiShopAnalyticsService.java b/src/main/java/com/gxwebsoft/ai/service/AiShopAnalyticsService.java deleted file mode 100644 index 09aad90..0000000 --- a/src/main/java/com/gxwebsoft/ai/service/AiShopAnalyticsService.java +++ /dev/null @@ -1,46 +0,0 @@ -package com.gxwebsoft.ai.service; - -import com.gxwebsoft.ai.dto.AiShopMetricsRow; -import com.gxwebsoft.ai.mapper.AiShopAnalyticsMapper; -import org.springframework.stereotype.Service; - -import javax.annotation.Resource; -import java.math.BigDecimal; -import java.math.RoundingMode; -import java.time.LocalDate; -import java.time.LocalDateTime; -import java.util.List; - -@Service -public class AiShopAnalyticsService { - @Resource - private AiShopAnalyticsMapper mapper; - - public List queryTenantDaily(Integer tenantId, LocalDate startDate, LocalDate endDateInclusive) { - LocalDateTime start = startDate.atStartOfDay(); - LocalDateTime endExclusive = endDateInclusive.plusDays(1).atStartOfDay(); - List rows = mapper.queryMetrics(tenantId, start, endExclusive); - if (rows == null) { - return null; - } - for (AiShopMetricsRow r : rows) { - long orderCnt = r.getOrderCnt() == null ? 0L : r.getOrderCnt(); - long paidCnt = r.getPaidOrderCnt() == null ? 0L : r.getPaidOrderCnt(); - BigDecimal gmv = r.getGmv() == null ? BigDecimal.ZERO : r.getGmv(); - - if (paidCnt > 0) { - r.setAov(gmv.divide(BigDecimal.valueOf(paidCnt), 4, RoundingMode.HALF_UP)); - } else { - r.setAov(BigDecimal.ZERO); - } - if (orderCnt > 0) { - r.setPayRate(BigDecimal.valueOf(paidCnt) - .divide(BigDecimal.valueOf(orderCnt), 4, RoundingMode.HALF_UP)); - } else { - r.setPayRate(BigDecimal.ZERO); - } - } - return rows; - } -} - diff --git a/src/main/java/com/gxwebsoft/ai/service/impl/AiKbChunkServiceImpl.java b/src/main/java/com/gxwebsoft/ai/service/impl/AiKbChunkServiceImpl.java deleted file mode 100644 index de9e157..0000000 --- a/src/main/java/com/gxwebsoft/ai/service/impl/AiKbChunkServiceImpl.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.gxwebsoft.ai.service.impl; - -import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; -import com.gxwebsoft.ai.entity.AiKbChunk; -import com.gxwebsoft.ai.mapper.AiKbChunkMapper; -import com.gxwebsoft.ai.service.AiKbChunkService; -import org.springframework.stereotype.Service; - -@Service -public class AiKbChunkServiceImpl extends ServiceImpl implements AiKbChunkService { -} - diff --git a/src/main/java/com/gxwebsoft/ai/service/impl/AiKbDocumentServiceImpl.java b/src/main/java/com/gxwebsoft/ai/service/impl/AiKbDocumentServiceImpl.java deleted file mode 100644 index 7295e96..0000000 --- a/src/main/java/com/gxwebsoft/ai/service/impl/AiKbDocumentServiceImpl.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.gxwebsoft.ai.service.impl; - -import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; -import com.gxwebsoft.ai.entity.AiKbDocument; -import com.gxwebsoft.ai.mapper.AiKbDocumentMapper; -import com.gxwebsoft.ai.service.AiKbDocumentService; -import org.springframework.stereotype.Service; - -@Service -public class AiKbDocumentServiceImpl extends ServiceImpl implements AiKbDocumentService { -} - diff --git a/src/main/java/com/gxwebsoft/ai/util/AiTextUtil.java b/src/main/java/com/gxwebsoft/ai/util/AiTextUtil.java deleted file mode 100644 index d7044f6..0000000 --- a/src/main/java/com/gxwebsoft/ai/util/AiTextUtil.java +++ /dev/null @@ -1,100 +0,0 @@ -package com.gxwebsoft.ai.util; - -import cn.hutool.core.util.StrUtil; - -import java.nio.charset.StandardCharsets; -import java.security.MessageDigest; -import java.util.ArrayList; -import java.util.List; - -public class AiTextUtil { - private AiTextUtil() { - } - - public static String sha256(String s) { - if (s == null) { - return null; - } - try { - MessageDigest md = MessageDigest.getInstance("SHA-256"); - byte[] dig = md.digest(s.getBytes(StandardCharsets.UTF_8)); - StringBuilder sb = new StringBuilder(dig.length * 2); - for (byte b : dig) { - sb.append(String.format("%02x", b)); - } - return sb.toString(); - } catch (Exception e) { - throw new RuntimeException(e); - } - } - - /** - * 很轻量的 HTML 转纯文本(不追求完美,只用于知识库入库前清洗)。 - */ - public static String stripHtml(String html) { - if (StrUtil.isBlank(html)) { - return ""; - } - String s = html; - s = s.replaceAll("(?is)]*>.*?", " "); - s = s.replaceAll("(?is)]*>.*?", " "); - s = s.replaceAll("(?is)", "\n"); - s = s.replaceAll("(?is)", "\n"); - s = s.replaceAll("(?is)<[^>]+>", " "); - // 常见 HTML 实体最小处理 - s = s.replace(" ", " "); - s = s.replace("<", "<").replace(">", ">").replace("&", "&").replace(""", "\""); - return normalizeWhitespace(s); - } - - public static String normalizeWhitespace(String s) { - if (s == null) { - return ""; - } - // 合并空白,保留换行用于 chunking - String x = s.replace("\r", "\n"); - x = x.replaceAll("[\\t\\f\\u000B]+", " "); - x = x.replaceAll("[ ]{2,}", " "); - x = x.replaceAll("\\n{3,}", "\n\n"); - return x.trim(); - } - - /** - * 按字符数切分,并做固定 overlap。 - */ - public static List chunkText(String text, int chunkSize, int overlap) { - String s = normalizeWhitespace(text); - List out = new ArrayList<>(); - if (StrUtil.isBlank(s)) { - return out; - } - if (chunkSize <= 0) { - chunkSize = 800; - } - if (overlap < 0) { - overlap = 0; - } - if (overlap >= chunkSize) { - overlap = Math.max(0, chunkSize / 5); - } - - int n = s.length(); - int start = 0; - while (start < n) { - int end = Math.min(n, start + chunkSize); - String chunk = s.substring(start, end).trim(); - if (!chunk.isEmpty()) { - out.add(chunk); - } - if (end >= n) { - break; - } - start = end - overlap; - if (start < 0) { - start = 0; - } - } - return out; - } -} - diff --git a/src/main/java/com/gxwebsoft/auto/controller/QrLoginController.java b/src/main/java/com/gxwebsoft/auto/controller/QrLoginController.java deleted file mode 100644 index d296d82..0000000 --- a/src/main/java/com/gxwebsoft/auto/controller/QrLoginController.java +++ /dev/null @@ -1,104 +0,0 @@ -package com.gxwebsoft.auto.controller; - -import com.gxwebsoft.auto.dto.QrLoginConfirmRequest; -import com.gxwebsoft.auto.dto.QrLoginGenerateResponse; -import com.gxwebsoft.auto.dto.QrLoginStatusResponse; -import com.gxwebsoft.auto.service.QrLoginService; -import com.gxwebsoft.common.core.web.BaseController; -import com.gxwebsoft.common.core.web.ApiResult; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.Parameter; -import io.swagger.v3.oas.annotations.tags.Tag; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.web.bind.annotation.*; - -import javax.validation.Valid; - -/** - * 认证模块 - * - * @author 科技小王子 - * @since 2025-03-06 22:50:25 - */ -@Tag(name = "认证模块") -@RestController -@RequestMapping("/api/qr-login") -public class QrLoginController extends BaseController { - - @Autowired - private QrLoginService qrLoginService; - - /** - * 生成扫码登录token - */ - @Operation(summary = "生成扫码登录token") - @PostMapping("/generate") - public ApiResult generateQrLoginToken() { - try { - QrLoginGenerateResponse response = qrLoginService.generateQrLoginToken(); - return success("生成成功", response); - } catch (Exception e) { - return fail(e.getMessage()); - } - } - - /** - * 检查扫码登录状态 - */ - @Operation(summary = "检查扫码登录状态") - @GetMapping("/status/{token}") - public ApiResult checkQrLoginStatus( - @Parameter(description = "扫码登录token") @PathVariable String token) { - try { - QrLoginStatusResponse response = qrLoginService.checkQrLoginStatus(token); - return success("查询成功", response); - } catch (Exception e) { - return fail(e.getMessage()); - } - } - - /** - * 确认扫码登录 - */ - @Operation(summary = "确认扫码登录") - @PostMapping("/confirm") - public ApiResult confirmQrLogin(@Valid @RequestBody QrLoginConfirmRequest request) { - try { - QrLoginStatusResponse response = qrLoginService.confirmQrLogin(request); - return success("确认成功", response); - } catch (Exception e) { - return fail(e.getMessage()); - } - } - - /** - * 扫码操作(可选接口,用于移动端扫码后更新状态) - */ - @Operation(summary = "扫码操作") - @PostMapping("/scan/{token}") - public ApiResult scanQrCode(@Parameter(description = "扫码登录token") @PathVariable String token) { - try { - boolean result = qrLoginService.scanQrCode(token); - return success("操作成功", result); - } catch (Exception e) { - return fail(e.getMessage()); - } - } - - /** - * 微信小程序扫码登录确认(便捷接口) - */ - @Operation(summary = "微信小程序扫码登录确认") - @PostMapping("/wechat-confirm") - public ApiResult wechatMiniProgramConfirm(@Valid @RequestBody QrLoginConfirmRequest request) { - try { - // 设置平台为微信小程序 - request.setPlatform("miniprogram"); - QrLoginStatusResponse response = qrLoginService.confirmQrLogin(request); - return success("微信小程序登录确认成功", response); - } catch (Exception e) { - return fail(e.getMessage()); - } - } - -} diff --git a/src/main/java/com/gxwebsoft/auto/dto/QrLoginConfirmRequest.java b/src/main/java/com/gxwebsoft/auto/dto/QrLoginConfirmRequest.java deleted file mode 100644 index f3b423e..0000000 --- a/src/main/java/com/gxwebsoft/auto/dto/QrLoginConfirmRequest.java +++ /dev/null @@ -1,50 +0,0 @@ -package com.gxwebsoft.auto.dto; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -import javax.validation.constraints.NotBlank; - -/** - * 扫码登录确认请求 - * - * @author 科技小王子 - * @since 2025-08-31 - */ -@Data -@Schema(description = "扫码登录确认请求") -public class QrLoginConfirmRequest { - - @Schema(description = "扫码登录token") - @NotBlank(message = "token不能为空") - private String token; - - @Schema(description = "用户ID") - private Integer userId; - - @Schema(description = "登录平台: web-网页端, app-移动应用, miniprogram-微信小程序") - private String platform; - - @Schema(description = "微信小程序相关信息") - private WechatMiniProgramInfo wechatInfo; - - /** - * 微信小程序信息 - */ - @Data - @Schema(description = "微信小程序信息") - public static class WechatMiniProgramInfo { - @Schema(description = "微信openid") - private String openid; - - @Schema(description = "微信unionid") - private String unionid; - - @Schema(description = "微信昵称") - private String nickname; - - @Schema(description = "微信头像") - private String avatar; - } - -} diff --git a/src/main/java/com/gxwebsoft/auto/dto/QrLoginData.java b/src/main/java/com/gxwebsoft/auto/dto/QrLoginData.java deleted file mode 100644 index 563bf1d..0000000 --- a/src/main/java/com/gxwebsoft/auto/dto/QrLoginData.java +++ /dev/null @@ -1,55 +0,0 @@ -package com.gxwebsoft.auto.dto; - -import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.NoArgsConstructor; - -import java.time.LocalDateTime; - -/** - * 扫码登录数据模型 - * - * @author 科技小王子 - * @since 2025-08-31 - */ -@Data -@NoArgsConstructor -@AllArgsConstructor -public class QrLoginData { - - /** - * 扫码登录token - */ - private String token; - - /** - * 状态: pending-等待扫码, scanned-已扫码, confirmed-已确认, expired-已过期 - */ - private String status; - - /** - * 用户ID(扫码确认后设置) - */ - private Integer userId; - - /** - * 用户名(扫码确认后设置) - */ - private String username; - - /** - * 创建时间 - */ - private LocalDateTime createTime; - - /** - * 过期时间 - */ - private LocalDateTime expireTime; - - /** - * JWT访问令牌(确认后生成) - */ - private String accessToken; - -} diff --git a/src/main/java/com/gxwebsoft/auto/dto/QrLoginGenerateResponse.java b/src/main/java/com/gxwebsoft/auto/dto/QrLoginGenerateResponse.java deleted file mode 100644 index f0b69e5..0000000 --- a/src/main/java/com/gxwebsoft/auto/dto/QrLoginGenerateResponse.java +++ /dev/null @@ -1,29 +0,0 @@ -package com.gxwebsoft.auto.dto; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.NoArgsConstructor; - -/** - * 扫码登录生成响应 - * - * @author 科技小王子 - * @since 2025-08-31 - */ -@Data -@NoArgsConstructor -@AllArgsConstructor -@Schema(description = "扫码登录生成响应") -public class QrLoginGenerateResponse { - - @Schema(description = "扫码登录token") - private String token; - - @Schema(description = "二维码内容") - private String qrCode; - - @Schema(description = "过期时间(秒)") - private Long expiresIn; - -} diff --git a/src/main/java/com/gxwebsoft/auto/dto/QrLoginStatusResponse.java b/src/main/java/com/gxwebsoft/auto/dto/QrLoginStatusResponse.java deleted file mode 100644 index 1eb0d4a..0000000 --- a/src/main/java/com/gxwebsoft/auto/dto/QrLoginStatusResponse.java +++ /dev/null @@ -1,32 +0,0 @@ -package com.gxwebsoft.auto.dto; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.NoArgsConstructor; - -/** - * 扫码登录状态响应 - * - * @author 科技小王子 - * @since 2025-08-31 - */ -@Data -@NoArgsConstructor -@AllArgsConstructor -@Schema(description = "扫码登录状态响应") -public class QrLoginStatusResponse { - - @Schema(description = "状态: pending-等待扫码, scanned-已扫码, confirmed-已确认, expired-已过期") - private String status; - - @Schema(description = "JWT访问令牌(仅在confirmed状态时返回)") - private String accessToken; - - @Schema(description = "用户信息(仅在confirmed状态时返回)") - private Object userInfo; - - @Schema(description = "剩余过期时间(秒)") - private Long expiresIn; - -} diff --git a/src/main/java/com/gxwebsoft/auto/service/QrLoginService.java b/src/main/java/com/gxwebsoft/auto/service/QrLoginService.java deleted file mode 100644 index 85ed28f..0000000 --- a/src/main/java/com/gxwebsoft/auto/service/QrLoginService.java +++ /dev/null @@ -1,46 +0,0 @@ -package com.gxwebsoft.auto.service; - -import com.gxwebsoft.auto.dto.QrLoginConfirmRequest; -import com.gxwebsoft.auto.dto.QrLoginGenerateResponse; -import com.gxwebsoft.auto.dto.QrLoginStatusResponse; - -/** - * 扫码登录服务接口 - * - * @author 科技小王子 - * @since 2025-08-31 - */ -public interface QrLoginService { - - /** - * 生成扫码登录token - * - * @return QrLoginGenerateResponse - */ - QrLoginGenerateResponse generateQrLoginToken(); - - /** - * 检查扫码登录状态 - * - * @param token 扫码登录token - * @return QrLoginStatusResponse - */ - QrLoginStatusResponse checkQrLoginStatus(String token); - - /** - * 确认扫码登录 - * - * @param request 确认请求 - * @return QrLoginStatusResponse - */ - QrLoginStatusResponse confirmQrLogin(QrLoginConfirmRequest request); - - /** - * 扫码操作(更新状态为已扫码) - * - * @param token 扫码登录token - * @return boolean - */ - boolean scanQrCode(String token); - -} diff --git a/src/main/java/com/gxwebsoft/auto/service/impl/QrLoginServiceImpl.java b/src/main/java/com/gxwebsoft/auto/service/impl/QrLoginServiceImpl.java deleted file mode 100644 index 34658e8..0000000 --- a/src/main/java/com/gxwebsoft/auto/service/impl/QrLoginServiceImpl.java +++ /dev/null @@ -1,239 +0,0 @@ -package com.gxwebsoft.auto.service.impl; - -import cn.hutool.core.lang.UUID; -import cn.hutool.core.util.StrUtil; -import com.gxwebsoft.auto.dto.*; -import com.gxwebsoft.auto.service.QrLoginService; -import com.gxwebsoft.common.core.security.JwtSubject; -import com.gxwebsoft.common.core.security.JwtUtil; -import com.gxwebsoft.common.core.utils.JSONUtil; -import com.gxwebsoft.common.core.utils.RedisUtil; -import com.gxwebsoft.common.system.entity.User; -import com.gxwebsoft.common.system.service.UserService; -import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.stereotype.Service; - -import java.time.LocalDateTime; -import java.time.temporal.ChronoUnit; -import java.util.concurrent.TimeUnit; - -import static com.gxwebsoft.common.core.constants.RedisConstants.*; - -/** - * 扫码登录服务实现 - * - * @author 科技小王子 - * @since 2025-08-31 - */ -@Slf4j -@Service -public class QrLoginServiceImpl implements QrLoginService { - - @Autowired - private RedisUtil redisUtil; - - @Autowired - private UserService userService; - - @Value("${config.jwt.secret:websoft-jwt-secret-key-2025}") - private String jwtSecret; - - @Value("${config.jwt.expire:86400}") - private Long jwtExpire; - - @Override - public QrLoginGenerateResponse generateQrLoginToken() { - // 生成唯一的扫码登录token - String token = UUID.randomUUID().toString(true); - - // 创建扫码登录数据 - QrLoginData qrLoginData = new QrLoginData(); - qrLoginData.setToken(token); - qrLoginData.setStatus(QR_LOGIN_STATUS_PENDING); - qrLoginData.setCreateTime(LocalDateTime.now()); - qrLoginData.setExpireTime(LocalDateTime.now().plusSeconds(QR_LOGIN_TOKEN_TTL)); - - // 存储到Redis,设置过期时间 - String redisKey = QR_LOGIN_TOKEN_KEY + token; - redisUtil.set(redisKey, qrLoginData, QR_LOGIN_TOKEN_TTL, TimeUnit.SECONDS); - - log.info("生成扫码登录token: {}", token); - - // 构造二维码内容(这里可以是前端登录页面的URL + token参数) - String qrCodeContent = "qr-login:" + token; - - return new QrLoginGenerateResponse(token, qrCodeContent, QR_LOGIN_TOKEN_TTL); - } - - @Override - public QrLoginStatusResponse checkQrLoginStatus(String token) { - if (StrUtil.isBlank(token)) { - return new QrLoginStatusResponse(QR_LOGIN_STATUS_EXPIRED, null, null, 0L); - } - - String redisKey = QR_LOGIN_TOKEN_KEY + token; - QrLoginData qrLoginData = redisUtil.get(redisKey, QrLoginData.class); - - if (qrLoginData == null) { - return new QrLoginStatusResponse(QR_LOGIN_STATUS_EXPIRED, null, null, 0L); - } - - // 检查是否过期 - if (LocalDateTime.now().isAfter(qrLoginData.getExpireTime())) { - // 删除过期的token - redisUtil.delete(redisKey); - return new QrLoginStatusResponse(QR_LOGIN_STATUS_EXPIRED, null, null, 0L); - } - - // 计算剩余过期时间 - long expiresIn = ChronoUnit.SECONDS.between(LocalDateTime.now(), qrLoginData.getExpireTime()); - - QrLoginStatusResponse response = new QrLoginStatusResponse(); - response.setStatus(qrLoginData.getStatus()); - response.setExpiresIn(expiresIn); - - // 如果已确认,返回token和用户信息 - if (QR_LOGIN_STATUS_CONFIRMED.equals(qrLoginData.getStatus())) { - response.setAccessToken(qrLoginData.getAccessToken()); - - // 获取用户信息 - if (qrLoginData.getUserId() != null) { - User user = userService.getByIdRel(qrLoginData.getUserId()); - if (user != null) { - // 清除敏感信息 - user.setPassword(null); - response.setUserInfo(user); - } - } - - // 确认后删除token,防止重复使用 - redisUtil.delete(redisKey); - } - - return response; - } - - @Override - public QrLoginStatusResponse confirmQrLogin(QrLoginConfirmRequest request) { - String token = request.getToken(); - Integer userId = request.getUserId(); - String platform = request.getPlatform(); - - if (StrUtil.isBlank(token) || userId == null) { - throw new RuntimeException("参数不能为空"); - } - - String redisKey = QR_LOGIN_TOKEN_KEY + token; - QrLoginData qrLoginData = redisUtil.get(redisKey, QrLoginData.class); - - if (qrLoginData == null) { - throw new RuntimeException("扫码登录token不存在或已过期"); - } - - // 检查是否过期 - if (LocalDateTime.now().isAfter(qrLoginData.getExpireTime())) { - redisUtil.delete(redisKey); - throw new RuntimeException("扫码登录token已过期"); - } - - // 获取用户信息 - User user = userService.getByIdRel(userId); - if (user == null) { - throw new RuntimeException("用户不存在"); - } - - // 检查用户状态 - if (user.getStatus() != null && user.getStatus() != 0) { - throw new RuntimeException("用户已被冻结"); - } - - // 如果是微信小程序登录,处理微信相关信息 - if ("miniprogram".equals(platform) && request.getWechatInfo() != null) { - handleWechatMiniProgramLogin(user, request.getWechatInfo()); - } - - // 生成JWT token - JwtSubject jwtSubject = new JwtSubject(user.getUsername(), user.getTenantId()); - String accessToken = JwtUtil.buildToken(jwtSubject, jwtExpire, jwtSecret); - - // 更新扫码登录数据 - qrLoginData.setStatus(QR_LOGIN_STATUS_CONFIRMED); - qrLoginData.setUserId(userId); - qrLoginData.setUsername(user.getUsername()); - qrLoginData.setAccessToken(accessToken); - - // 更新Redis中的数据 - redisUtil.set(redisKey, qrLoginData, 60L, TimeUnit.SECONDS); // 给前端60秒时间获取token - - log.info("用户 {} 通过 {} 平台确认扫码登录,token: {}", user.getUsername(), - platform != null ? platform : "unknown", token); - - // 清除敏感信息 - user.setPassword(null); - - return new QrLoginStatusResponse(QR_LOGIN_STATUS_CONFIRMED, accessToken, user, 60L); - } - - /** - * 处理微信小程序登录相关逻辑 - */ - private void handleWechatMiniProgramLogin(User user, QrLoginConfirmRequest.WechatMiniProgramInfo wechatInfo) { - // 更新用户的微信信息 - if (StrUtil.isNotBlank(wechatInfo.getOpenid())) { - user.setOpenid(wechatInfo.getOpenid()); - } - if (StrUtil.isNotBlank(wechatInfo.getUnionid())) { - user.setUnionid(wechatInfo.getUnionid()); - } - if (StrUtil.isNotBlank(wechatInfo.getNickname()) && StrUtil.isBlank(user.getNickname())) { - user.setNickname(wechatInfo.getNickname()); - } - if (StrUtil.isNotBlank(wechatInfo.getAvatar()) && StrUtil.isBlank(user.getAvatar())) { - user.setAvatar(wechatInfo.getAvatar()); - } - - // 更新用户信息到数据库 - try { - userService.updateById(user); - log.info("更新用户 {} 的微信小程序信息成功", user.getUsername()); - } catch (Exception e) { - log.warn("更新用户 {} 的微信小程序信息失败: {}", user.getUsername(), e.getMessage()); - } - } - - @Override - public boolean scanQrCode(String token) { - if (StrUtil.isBlank(token)) { - return false; - } - - String redisKey = QR_LOGIN_TOKEN_KEY + token; - QrLoginData qrLoginData = redisUtil.get(redisKey, QrLoginData.class); - - if (qrLoginData == null) { - return false; - } - - // 检查是否过期 - if (LocalDateTime.now().isAfter(qrLoginData.getExpireTime())) { - redisUtil.delete(redisKey); - return false; - } - - // 只有pending状态才能更新为scanned - if (QR_LOGIN_STATUS_PENDING.equals(qrLoginData.getStatus())) { - qrLoginData.setStatus(QR_LOGIN_STATUS_SCANNED); - - // 计算剩余过期时间 - long remainingSeconds = ChronoUnit.SECONDS.between(LocalDateTime.now(), qrLoginData.getExpireTime()); - redisUtil.set(redisKey, qrLoginData, remainingSeconds, TimeUnit.SECONDS); - - log.info("扫码登录token {} 状态更新为已扫码", token); - return true; - } - - return false; - } -} diff --git a/src/main/java/com/gxwebsoft/clinic/controller/ClinicAppointmentController.java b/src/main/java/com/gxwebsoft/clinic/controller/ClinicAppointmentController.java deleted file mode 100644 index cf186e3..0000000 --- a/src/main/java/com/gxwebsoft/clinic/controller/ClinicAppointmentController.java +++ /dev/null @@ -1,122 +0,0 @@ -package com.gxwebsoft.clinic.controller; - -import com.gxwebsoft.common.core.web.BaseController; -import com.gxwebsoft.clinic.service.ClinicAppointmentService; -import com.gxwebsoft.clinic.entity.ClinicAppointment; -import com.gxwebsoft.clinic.param.ClinicAppointmentParam; -import com.gxwebsoft.common.core.web.ApiResult; -import com.gxwebsoft.common.core.web.PageResult; -import com.gxwebsoft.common.core.web.PageParam; -import com.gxwebsoft.common.core.web.BatchParam; -import com.gxwebsoft.common.core.annotation.OperationLog; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.tags.Tag; -import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.web.bind.annotation.*; - -import javax.annotation.Resource; -import java.util.List; - -/** - * 挂号控制器 - * - * @author 科技小王子 - * @since 2025-10-19 09:27:04 - */ -@Tag(name = "挂号管理") -@RestController -@RequestMapping("/api/clinic/clinic-appointment") -public class ClinicAppointmentController extends BaseController { - @Resource - private ClinicAppointmentService clinicAppointmentService; - - @Operation(summary = "分页查询挂号") - @GetMapping("/page") - public ApiResult> page(ClinicAppointmentParam param) { - // 使用关联查询 - return success(clinicAppointmentService.pageRel(param)); - } - - @PreAuthorize("hasAuthority('clinic:clinicAppointment:list')") - @Operation(summary = "查询全部挂号") - @GetMapping() - public ApiResult> list(ClinicAppointmentParam param) { - // 使用关联查询 - return success(clinicAppointmentService.listRel(param)); - } - - @PreAuthorize("hasAuthority('clinic:clinicAppointment:list')") - @Operation(summary = "根据id查询挂号") - @GetMapping("/{id}") - public ApiResult get(@PathVariable("id") Integer id) { - // 使用关联查询 - return success(clinicAppointmentService.getByIdRel(id)); - } - - @PreAuthorize("hasAuthority('clinic:clinicAppointment:save')") - @OperationLog - @Operation(summary = "添加挂号") - @PostMapping() - public ApiResult save(@RequestBody ClinicAppointment clinicAppointment) { - if (clinicAppointmentService.save(clinicAppointment)) { - return success("添加成功"); - } - return fail("添加失败"); - } - - @PreAuthorize("hasAuthority('clinic:clinicAppointment:update')") - @OperationLog - @Operation(summary = "修改挂号") - @PutMapping() - public ApiResult update(@RequestBody ClinicAppointment clinicAppointment) { - if (clinicAppointmentService.updateById(clinicAppointment)) { - return success("修改成功"); - } - return fail("修改失败"); - } - - @PreAuthorize("hasAuthority('clinic:clinicAppointment:remove')") - @OperationLog - @Operation(summary = "删除挂号") - @DeleteMapping("/{id}") - public ApiResult remove(@PathVariable("id") Integer id) { - if (clinicAppointmentService.removeById(id)) { - return success("删除成功"); - } - return fail("删除失败"); - } - - @PreAuthorize("hasAuthority('clinic:clinicAppointment:save')") - @OperationLog - @Operation(summary = "批量添加挂号") - @PostMapping("/batch") - public ApiResult saveBatch(@RequestBody List list) { - if (clinicAppointmentService.saveBatch(list)) { - return success("添加成功"); - } - return fail("添加失败"); - } - - @PreAuthorize("hasAuthority('clinic:clinicAppointment:update')") - @OperationLog - @Operation(summary = "批量修改挂号") - @PutMapping("/batch") - public ApiResult removeBatch(@RequestBody BatchParam batchParam) { - if (batchParam.update(clinicAppointmentService, "id")) { - return success("修改成功"); - } - return fail("修改失败"); - } - - @PreAuthorize("hasAuthority('clinic:clinicAppointment:remove')") - @OperationLog - @Operation(summary = "批量删除挂号") - @DeleteMapping("/batch") - public ApiResult removeBatch(@RequestBody List ids) { - if (clinicAppointmentService.removeByIds(ids)) { - return success("删除成功"); - } - return fail("删除失败"); - } - -} diff --git a/src/main/java/com/gxwebsoft/clinic/controller/ClinicDoctorApplyController.java b/src/main/java/com/gxwebsoft/clinic/controller/ClinicDoctorApplyController.java deleted file mode 100644 index 7471fa7..0000000 --- a/src/main/java/com/gxwebsoft/clinic/controller/ClinicDoctorApplyController.java +++ /dev/null @@ -1,128 +0,0 @@ -package com.gxwebsoft.clinic.controller; - -import com.gxwebsoft.common.core.web.BaseController; -import com.gxwebsoft.clinic.service.ClinicDoctorApplyService; -import com.gxwebsoft.clinic.entity.ClinicDoctorApply; -import com.gxwebsoft.clinic.param.ClinicDoctorApplyParam; -import com.gxwebsoft.common.core.web.ApiResult; -import com.gxwebsoft.common.core.web.PageResult; -import com.gxwebsoft.common.core.web.PageParam; -import com.gxwebsoft.common.core.web.BatchParam; -import com.gxwebsoft.common.core.annotation.OperationLog; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.tags.Tag; -import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.web.bind.annotation.*; - -import javax.annotation.Resource; -import java.util.List; - -/** - * 医生入驻申请控制器 - * - * @author 科技小王子 - * @since 2025-10-19 09:27:04 - */ -@Tag(name = "医生入驻申请管理") -@RestController -@RequestMapping("/api/clinic/clinic-doctor-apply") -public class ClinicDoctorApplyController extends BaseController { - @Resource - private ClinicDoctorApplyService clinicDoctorApplyService; - - @PreAuthorize("hasAuthority('clinic:clinicDoctorApply:list')") - @Operation(summary = "分页查询医生入驻申请") - @GetMapping("/page") - public ApiResult> page(ClinicDoctorApplyParam param) { - // 使用关联查询 - return success(clinicDoctorApplyService.pageRel(param)); - } - - @PreAuthorize("hasAuthority('clinic:clinicDoctorApply:list')") - @Operation(summary = "查询全部医生入驻申请") - @GetMapping() - public ApiResult> list(ClinicDoctorApplyParam param) { - // 使用关联查询 - return success(clinicDoctorApplyService.listRel(param)); - } - - @PreAuthorize("hasAuthority('clinic:clinicDoctorApply:list')") - @Operation(summary = "根据id查询医生入驻申请") - @GetMapping("/{id}") - public ApiResult get(@PathVariable("id") Integer id) { - // 使用关联查询 - return success(clinicDoctorApplyService.getByIdRel(id)); - } - - @PreAuthorize("hasAuthority('clinic:clinicDoctorApply:save')") - @OperationLog - @Operation(summary = "添加医生入驻申请") - @PostMapping() - public ApiResult save(@RequestBody ClinicDoctorApply clinicDoctorApply) { - // 记录当前登录用户id - // User loginUser = getLoginUser(); - // if (loginUser != null) { - // clinicDoctorApply.setUserId(loginUser.getUserId()); - // } - if (clinicDoctorApplyService.save(clinicDoctorApply)) { - return success("添加成功"); - } - return fail("添加失败"); - } - - @PreAuthorize("hasAuthority('clinic:clinicDoctorApply:update')") - @OperationLog - @Operation(summary = "修改医生入驻申请") - @PutMapping() - public ApiResult update(@RequestBody ClinicDoctorApply clinicDoctorApply) { - if (clinicDoctorApplyService.updateById(clinicDoctorApply)) { - return success("修改成功"); - } - return fail("修改失败"); - } - - @PreAuthorize("hasAuthority('clinic:clinicDoctorApply:remove')") - @OperationLog - @Operation(summary = "删除医生入驻申请") - @DeleteMapping("/{id}") - public ApiResult remove(@PathVariable("id") Integer id) { - if (clinicDoctorApplyService.removeById(id)) { - return success("删除成功"); - } - return fail("删除失败"); - } - - @PreAuthorize("hasAuthority('clinic:clinicDoctorApply:save')") - @OperationLog - @Operation(summary = "批量添加医生入驻申请") - @PostMapping("/batch") - public ApiResult saveBatch(@RequestBody List list) { - if (clinicDoctorApplyService.saveBatch(list)) { - return success("添加成功"); - } - return fail("添加失败"); - } - - @PreAuthorize("hasAuthority('clinic:clinicDoctorApply:update')") - @OperationLog - @Operation(summary = "批量修改医生入驻申请") - @PutMapping("/batch") - public ApiResult removeBatch(@RequestBody BatchParam batchParam) { - if (batchParam.update(clinicDoctorApplyService, "apply_id")) { - return success("修改成功"); - } - return fail("修改失败"); - } - - @PreAuthorize("hasAuthority('clinic:clinicDoctorApply:remove')") - @OperationLog - @Operation(summary = "批量删除医生入驻申请") - @DeleteMapping("/batch") - public ApiResult removeBatch(@RequestBody List ids) { - if (clinicDoctorApplyService.removeByIds(ids)) { - return success("删除成功"); - } - return fail("删除失败"); - } - -} diff --git a/src/main/java/com/gxwebsoft/clinic/controller/ClinicDoctorUserController.java b/src/main/java/com/gxwebsoft/clinic/controller/ClinicDoctorUserController.java deleted file mode 100644 index 9f7012e..0000000 --- a/src/main/java/com/gxwebsoft/clinic/controller/ClinicDoctorUserController.java +++ /dev/null @@ -1,128 +0,0 @@ -package com.gxwebsoft.clinic.controller; - -import com.gxwebsoft.clinic.entity.ClinicDoctorUser; -import com.gxwebsoft.clinic.param.ClinicDoctorUserParam; -import com.gxwebsoft.clinic.service.ClinicDoctorUserService; -import com.gxwebsoft.common.core.annotation.OperationLog; -import com.gxwebsoft.common.core.web.ApiResult; -import com.gxwebsoft.common.core.web.BaseController; -import com.gxwebsoft.common.core.web.BatchParam; -import com.gxwebsoft.common.core.web.PageResult; -import com.gxwebsoft.common.system.entity.User; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.tags.Tag; -import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.web.bind.annotation.*; - -import javax.annotation.Resource; -import java.util.List; - -/** - * 分销商用户记录表控制器 - * - * @author 科技小王子 - * @since 2025-10-23 15:58:21 - */ -@Tag(name = "分销商用户记录表管理") -@RestController -@RequestMapping("/api/clinic/clinic-doctor-user") -public class ClinicDoctorUserController extends BaseController { - @Resource - private ClinicDoctorUserService clinicDoctorUserService; - - @PreAuthorize("hasAuthority('clinic:clinicDoctorUser:list')") - @Operation(summary = "分页查询分销商用户记录表") - @GetMapping("/page") - public ApiResult> page(ClinicDoctorUserParam param) { - // 使用关联查询 - return success(clinicDoctorUserService.pageRel(param)); - } - - @PreAuthorize("hasAuthority('clinic:clinicDoctorUser:list')") - @Operation(summary = "查询全部分销商用户记录表") - @GetMapping() - public ApiResult> list(ClinicDoctorUserParam param) { - // 使用关联查询 - return success(clinicDoctorUserService.listRel(param)); - } - - @PreAuthorize("hasAuthority('clinic:clinicDoctorUser:list')") - @Operation(summary = "根据id查询分销商用户记录表") - @GetMapping("/{id}") - public ApiResult get(@PathVariable("id") Integer id) { - // 使用关联查询 - return success(clinicDoctorUserService.getByIdRel(id)); - } - - @PreAuthorize("hasAuthority('clinic:clinicDoctorUser:save')") - @OperationLog - @Operation(summary = "添加分销商用户记录表") - @PostMapping() - public ApiResult save(@RequestBody ClinicDoctorUser clinicDoctorUser) { - // 记录当前登录用户id - User loginUser = getLoginUser(); - if (loginUser != null) { - clinicDoctorUser.setUserId(loginUser.getUserId()); - } - if (clinicDoctorUserService.save(clinicDoctorUser)) { - return success("添加成功"); - } - return fail("添加失败"); - } - - @PreAuthorize("hasAuthority('clinic:clinicDoctorUser:update')") - @OperationLog - @Operation(summary = "修改分销商用户记录表") - @PutMapping() - public ApiResult update(@RequestBody ClinicDoctorUser clinicDoctorUser) { - if (clinicDoctorUserService.updateById(clinicDoctorUser)) { - return success("修改成功"); - } - return fail("修改失败"); - } - - @PreAuthorize("hasAuthority('clinic:clinicDoctorUser:remove')") - @OperationLog - @Operation(summary = "删除分销商用户记录表") - @DeleteMapping("/{id}") - public ApiResult remove(@PathVariable("id") Integer id) { - if (clinicDoctorUserService.removeById(id)) { - return success("删除成功"); - } - return fail("删除失败"); - } - - @PreAuthorize("hasAuthority('clinic:clinicDoctorUser:save')") - @OperationLog - @Operation(summary = "批量添加分销商用户记录表") - @PostMapping("/batch") - public ApiResult saveBatch(@RequestBody List list) { - if (clinicDoctorUserService.saveBatch(list)) { - return success("添加成功"); - } - return fail("添加失败"); - } - - @PreAuthorize("hasAuthority('clinic:clinicDoctorUser:update')") - @OperationLog - @Operation(summary = "批量修改分销商用户记录表") - @PutMapping("/batch") - public ApiResult removeBatch(@RequestBody BatchParam batchParam) { - if (batchParam.update(clinicDoctorUserService, "id")) { - return success("修改成功"); - } - return fail("修改失败"); - } - - @PreAuthorize("hasAuthority('clinic:clinicDoctorUser:remove')") - @OperationLog - @Operation(summary = "批量删除分销商用户记录表") - @DeleteMapping("/batch") - public ApiResult removeBatch(@RequestBody List ids) { - if (clinicDoctorUserService.removeByIds(ids)) { - return success("删除成功"); - } - return fail("删除失败"); - } - -} diff --git a/src/main/java/com/gxwebsoft/clinic/controller/ClinicMedicineController.java b/src/main/java/com/gxwebsoft/clinic/controller/ClinicMedicineController.java deleted file mode 100644 index 28e27c6..0000000 --- a/src/main/java/com/gxwebsoft/clinic/controller/ClinicMedicineController.java +++ /dev/null @@ -1,127 +0,0 @@ -package com.gxwebsoft.clinic.controller; - -import com.gxwebsoft.clinic.entity.ClinicMedicine; -import com.gxwebsoft.clinic.param.ClinicMedicineParam; -import com.gxwebsoft.clinic.service.ClinicMedicineService; -import com.gxwebsoft.common.core.annotation.OperationLog; -import com.gxwebsoft.common.core.web.ApiResult; -import com.gxwebsoft.common.core.web.BaseController; -import com.gxwebsoft.common.core.web.BatchParam; -import com.gxwebsoft.common.core.web.PageResult; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.tags.Tag; -import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.web.bind.annotation.*; - -import javax.annotation.Resource; -import java.util.List; - -/** - * 药品库控制器 - * - * @author 科技小王子 - * @since 2025-10-22 02:06:32 - */ -@Tag(name = "药品库管理") -@RestController -@RequestMapping("/api/clinic/clinic-medicine") -public class ClinicMedicineController extends BaseController { - @Resource - private ClinicMedicineService clinicMedicineService; - - @PreAuthorize("hasAuthority('clinic:clinicMedicine:list')") - @Operation(summary = "分页查询药品库") - @GetMapping("/page") - public ApiResult> page(ClinicMedicineParam param) { - // 使用关联查询 - return success(clinicMedicineService.pageRel(param)); - } - - @PreAuthorize("hasAuthority('clinic:clinicMedicine:list')") - @Operation(summary = "查询全部药品库") - @GetMapping() - public ApiResult> list(ClinicMedicineParam param) { - // 使用关联查询 - return success(clinicMedicineService.listRel(param)); - } - - @PreAuthorize("hasAuthority('clinic:clinicMedicine:list')") - @Operation(summary = "根据id查询药品库") - @GetMapping("/{id}") - public ApiResult get(@PathVariable("id") Integer id) { - // 使用关联查询 - return success(clinicMedicineService.getByIdRel(id)); - } - - @PreAuthorize("hasAuthority('clinic:clinicMedicine:save')") - @OperationLog - @Operation(summary = "添加药品库") - @PostMapping() - public ApiResult save(@RequestBody ClinicMedicine clinicMedicine) { - // 记录当前登录用户id - // User loginUser = getLoginUser(); - // if (loginUser != null) { - // clinicMedicine.setUserId(loginUser.getUserId()); - // } - if (clinicMedicineService.save(clinicMedicine)) { - return success("添加成功"); - } - return fail("添加失败"); - } - - @PreAuthorize("hasAuthority('clinic:clinicMedicine:update')") - @OperationLog - @Operation(summary = "修改药品库") - @PutMapping() - public ApiResult update(@RequestBody ClinicMedicine clinicMedicine) { - if (clinicMedicineService.updateById(clinicMedicine)) { - return success("修改成功"); - } - return fail("修改失败"); - } - - @PreAuthorize("hasAuthority('clinic:clinicMedicine:remove')") - @OperationLog - @Operation(summary = "删除药品库") - @DeleteMapping("/{id}") - public ApiResult remove(@PathVariable("id") Integer id) { - if (clinicMedicineService.removeById(id)) { - return success("删除成功"); - } - return fail("删除失败"); - } - - @PreAuthorize("hasAuthority('clinic:clinicMedicine:save')") - @OperationLog - @Operation(summary = "批量添加药品库") - @PostMapping("/batch") - public ApiResult saveBatch(@RequestBody List list) { - if (clinicMedicineService.saveBatch(list)) { - return success("添加成功"); - } - return fail("添加失败"); - } - - @PreAuthorize("hasAuthority('clinic:clinicMedicine:update')") - @OperationLog - @Operation(summary = "批量修改药品库") - @PutMapping("/batch") - public ApiResult removeBatch(@RequestBody BatchParam batchParam) { - if (batchParam.update(clinicMedicineService, "id")) { - return success("修改成功"); - } - return fail("修改失败"); - } - - @PreAuthorize("hasAuthority('clinic:clinicMedicine:remove')") - @OperationLog - @Operation(summary = "批量删除药品库") - @DeleteMapping("/batch") - public ApiResult removeBatch(@RequestBody List ids) { - if (clinicMedicineService.removeByIds(ids)) { - return success("删除成功"); - } - return fail("删除失败"); - } - -} diff --git a/src/main/java/com/gxwebsoft/clinic/controller/ClinicMedicineInoutController.java b/src/main/java/com/gxwebsoft/clinic/controller/ClinicMedicineInoutController.java deleted file mode 100644 index e1b89a8..0000000 --- a/src/main/java/com/gxwebsoft/clinic/controller/ClinicMedicineInoutController.java +++ /dev/null @@ -1,127 +0,0 @@ -package com.gxwebsoft.clinic.controller; - -import com.gxwebsoft.clinic.entity.ClinicMedicineInout; -import com.gxwebsoft.clinic.param.ClinicMedicineInoutParam; -import com.gxwebsoft.clinic.service.ClinicMedicineInoutService; -import com.gxwebsoft.common.core.annotation.OperationLog; -import com.gxwebsoft.common.core.web.ApiResult; -import com.gxwebsoft.common.core.web.BaseController; -import com.gxwebsoft.common.core.web.BatchParam; -import com.gxwebsoft.common.core.web.PageResult; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.tags.Tag; -import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.web.bind.annotation.*; - -import javax.annotation.Resource; -import java.util.List; - -/** - * 出入库控制器 - * - * @author 科技小王子 - * @since 2025-10-22 02:06:32 - */ -@Tag(name = "出入库管理") -@RestController -@RequestMapping("/api/clinic/clinic-medicine-inout") -public class ClinicMedicineInoutController extends BaseController { - @Resource - private ClinicMedicineInoutService clinicMedicineInoutService; - - @PreAuthorize("hasAuthority('clinic:clinicMedicineInout:list')") - @Operation(summary = "分页查询出入库") - @GetMapping("/page") - public ApiResult> page(ClinicMedicineInoutParam param) { - // 使用关联查询 - return success(clinicMedicineInoutService.pageRel(param)); - } - - @PreAuthorize("hasAuthority('clinic:clinicMedicineInout:list')") - @Operation(summary = "查询全部出入库") - @GetMapping() - public ApiResult> list(ClinicMedicineInoutParam param) { - // 使用关联查询 - return success(clinicMedicineInoutService.listRel(param)); - } - - @PreAuthorize("hasAuthority('clinic:clinicMedicineInout:list')") - @Operation(summary = "根据id查询出入库") - @GetMapping("/{id}") - public ApiResult get(@PathVariable("id") Integer id) { - // 使用关联查询 - return success(clinicMedicineInoutService.getByIdRel(id)); - } - - @PreAuthorize("hasAuthority('clinic:clinicMedicineInout:save')") - @OperationLog - @Operation(summary = "添加出入库") - @PostMapping() - public ApiResult save(@RequestBody ClinicMedicineInout clinicMedicineInout) { - // 记录当前登录用户id - // User loginUser = getLoginUser(); - // if (loginUser != null) { - // clinicMedicineInout.setUserId(loginUser.getUserId()); - // } - if (clinicMedicineInoutService.save(clinicMedicineInout)) { - return success("添加成功"); - } - return fail("添加失败"); - } - - @PreAuthorize("hasAuthority('clinic:clinicMedicineInout:update')") - @OperationLog - @Operation(summary = "修改出入库") - @PutMapping() - public ApiResult update(@RequestBody ClinicMedicineInout clinicMedicineInout) { - if (clinicMedicineInoutService.updateById(clinicMedicineInout)) { - return success("修改成功"); - } - return fail("修改失败"); - } - - @PreAuthorize("hasAuthority('clinic:clinicMedicineInout:remove')") - @OperationLog - @Operation(summary = "删除出入库") - @DeleteMapping("/{id}") - public ApiResult remove(@PathVariable("id") Integer id) { - if (clinicMedicineInoutService.removeById(id)) { - return success("删除成功"); - } - return fail("删除失败"); - } - - @PreAuthorize("hasAuthority('clinic:clinicMedicineInout:save')") - @OperationLog - @Operation(summary = "批量添加出入库") - @PostMapping("/batch") - public ApiResult saveBatch(@RequestBody List list) { - if (clinicMedicineInoutService.saveBatch(list)) { - return success("添加成功"); - } - return fail("添加失败"); - } - - @PreAuthorize("hasAuthority('clinic:clinicMedicineInout:update')") - @OperationLog - @Operation(summary = "批量修改出入库") - @PutMapping("/batch") - public ApiResult removeBatch(@RequestBody BatchParam batchParam) { - if (batchParam.update(clinicMedicineInoutService, "id")) { - return success("修改成功"); - } - return fail("修改失败"); - } - - @PreAuthorize("hasAuthority('clinic:clinicMedicineInout:remove')") - @OperationLog - @Operation(summary = "批量删除出入库") - @DeleteMapping("/batch") - public ApiResult removeBatch(@RequestBody List ids) { - if (clinicMedicineInoutService.removeByIds(ids)) { - return success("删除成功"); - } - return fail("删除失败"); - } - -} diff --git a/src/main/java/com/gxwebsoft/clinic/controller/ClinicMedicineStockController.java b/src/main/java/com/gxwebsoft/clinic/controller/ClinicMedicineStockController.java deleted file mode 100644 index 867b425..0000000 --- a/src/main/java/com/gxwebsoft/clinic/controller/ClinicMedicineStockController.java +++ /dev/null @@ -1,127 +0,0 @@ -package com.gxwebsoft.clinic.controller; - -import com.gxwebsoft.clinic.entity.ClinicMedicineStock; -import com.gxwebsoft.clinic.param.ClinicMedicineStockParam; -import com.gxwebsoft.clinic.service.ClinicMedicineStockService; -import com.gxwebsoft.common.core.annotation.OperationLog; -import com.gxwebsoft.common.core.web.ApiResult; -import com.gxwebsoft.common.core.web.BaseController; -import com.gxwebsoft.common.core.web.BatchParam; -import com.gxwebsoft.common.core.web.PageResult; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.tags.Tag; -import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.web.bind.annotation.*; - -import javax.annotation.Resource; -import java.util.List; - -/** - * 药品库存控制器 - * - * @author 科技小王子 - * @since 2025-10-22 02:06:32 - */ -@Tag(name = "药品库存管理") -@RestController -@RequestMapping("/api/clinic/clinic-medicine-stock") -public class ClinicMedicineStockController extends BaseController { - @Resource - private ClinicMedicineStockService clinicMedicineStockService; - - @PreAuthorize("hasAuthority('clinic:clinicMedicineStock:list')") - @Operation(summary = "分页查询药品库存") - @GetMapping("/page") - public ApiResult> page(ClinicMedicineStockParam param) { - // 使用关联查询 - return success(clinicMedicineStockService.pageRel(param)); - } - - @PreAuthorize("hasAuthority('clinic:clinicMedicineStock:list')") - @Operation(summary = "查询全部药品库存") - @GetMapping() - public ApiResult> list(ClinicMedicineStockParam param) { - // 使用关联查询 - return success(clinicMedicineStockService.listRel(param)); - } - - @PreAuthorize("hasAuthority('clinic:clinicMedicineStock:list')") - @Operation(summary = "根据id查询药品库存") - @GetMapping("/{id}") - public ApiResult get(@PathVariable("id") Integer id) { - // 使用关联查询 - return success(clinicMedicineStockService.getByIdRel(id)); - } - - @PreAuthorize("hasAuthority('clinic:clinicMedicineStock:save')") - @OperationLog - @Operation(summary = "添加药品库存") - @PostMapping() - public ApiResult save(@RequestBody ClinicMedicineStock clinicMedicineStock) { - // 记录当前登录用户id - // User loginUser = getLoginUser(); - // if (loginUser != null) { - // clinicMedicineStock.setUserId(loginUser.getUserId()); - // } - if (clinicMedicineStockService.save(clinicMedicineStock)) { - return success("添加成功"); - } - return fail("添加失败"); - } - - @PreAuthorize("hasAuthority('clinic:clinicMedicineStock:update')") - @OperationLog - @Operation(summary = "修改药品库存") - @PutMapping() - public ApiResult update(@RequestBody ClinicMedicineStock clinicMedicineStock) { - if (clinicMedicineStockService.updateById(clinicMedicineStock)) { - return success("修改成功"); - } - return fail("修改失败"); - } - - @PreAuthorize("hasAuthority('clinic:clinicMedicineStock:remove')") - @OperationLog - @Operation(summary = "删除药品库存") - @DeleteMapping("/{id}") - public ApiResult remove(@PathVariable("id") Integer id) { - if (clinicMedicineStockService.removeById(id)) { - return success("删除成功"); - } - return fail("删除失败"); - } - - @PreAuthorize("hasAuthority('clinic:clinicMedicineStock:save')") - @OperationLog - @Operation(summary = "批量添加药品库存") - @PostMapping("/batch") - public ApiResult saveBatch(@RequestBody List list) { - if (clinicMedicineStockService.saveBatch(list)) { - return success("添加成功"); - } - return fail("添加失败"); - } - - @PreAuthorize("hasAuthority('clinic:clinicMedicineStock:update')") - @OperationLog - @Operation(summary = "批量修改药品库存") - @PutMapping("/batch") - public ApiResult removeBatch(@RequestBody BatchParam batchParam) { - if (batchParam.update(clinicMedicineStockService, "id")) { - return success("修改成功"); - } - return fail("修改失败"); - } - - @PreAuthorize("hasAuthority('clinic:clinicMedicineStock:remove')") - @OperationLog - @Operation(summary = "批量删除药品库存") - @DeleteMapping("/batch") - public ApiResult removeBatch(@RequestBody List ids) { - if (clinicMedicineStockService.removeByIds(ids)) { - return success("删除成功"); - } - return fail("删除失败"); - } - -} diff --git a/src/main/java/com/gxwebsoft/clinic/controller/ClinicPatientUserController.java b/src/main/java/com/gxwebsoft/clinic/controller/ClinicPatientUserController.java deleted file mode 100644 index 87aa108..0000000 --- a/src/main/java/com/gxwebsoft/clinic/controller/ClinicPatientUserController.java +++ /dev/null @@ -1,129 +0,0 @@ -package com.gxwebsoft.clinic.controller; - -import com.gxwebsoft.common.core.web.BaseController; -import com.gxwebsoft.clinic.service.ClinicPatientUserService; -import com.gxwebsoft.clinic.entity.ClinicPatientUser; -import com.gxwebsoft.clinic.param.ClinicPatientUserParam; -import com.gxwebsoft.common.core.web.ApiResult; -import com.gxwebsoft.common.core.web.PageResult; -import com.gxwebsoft.common.core.web.PageParam; -import com.gxwebsoft.common.core.web.BatchParam; -import com.gxwebsoft.common.core.annotation.OperationLog; -import com.gxwebsoft.common.system.entity.User; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.tags.Tag; -import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.web.bind.annotation.*; - -import javax.annotation.Resource; -import java.util.List; - -/** - * 患者控制器 - * - * @author 科技小王子 - * @since 2025-10-19 09:27:04 - */ -@Tag(name = "患者管理") -@RestController -@RequestMapping("/api/clinic/clinic-patient-user") -public class ClinicPatientUserController extends BaseController { - @Resource - private ClinicPatientUserService clinicPatientUserService; - - @PreAuthorize("hasAuthority('clinic:clinicPatientUser:list')") - @Operation(summary = "分页查询患者") - @GetMapping("/page") - public ApiResult> page(ClinicPatientUserParam param) { - // 使用关联查询 - return success(clinicPatientUserService.pageRel(param)); - } - - @PreAuthorize("hasAuthority('clinic:clinicPatientUser:list')") - @Operation(summary = "查询全部患者") - @GetMapping() - public ApiResult> list(ClinicPatientUserParam param) { - // 使用关联查询 - return success(clinicPatientUserService.listRel(param)); - } - - @PreAuthorize("hasAuthority('clinic:clinicPatientUser:list')") - @Operation(summary = "根据id查询患者") - @GetMapping("/{id}") - public ApiResult get(@PathVariable("id") Integer id) { - // 使用关联查询 - return success(clinicPatientUserService.getByIdRel(id)); - } - - @PreAuthorize("hasAuthority('clinic:clinicPatientUser:save')") - @OperationLog - @Operation(summary = "添加患者") - @PostMapping() - public ApiResult save(@RequestBody ClinicPatientUser clinicPatientUser) { - // 记录当前登录用户id - User loginUser = getLoginUser(); - if (loginUser != null) { - clinicPatientUser.setUserId(loginUser.getUserId()); - } - if (clinicPatientUserService.save(clinicPatientUser)) { - return success("添加成功"); - } - return fail("添加失败"); - } - - @PreAuthorize("hasAuthority('clinic:clinicPatientUser:update')") - @OperationLog - @Operation(summary = "修改患者") - @PutMapping() - public ApiResult update(@RequestBody ClinicPatientUser clinicPatientUser) { - if (clinicPatientUserService.updateById(clinicPatientUser)) { - return success("修改成功"); - } - return fail("修改失败"); - } - - @PreAuthorize("hasAuthority('clinic:clinicPatientUser:remove')") - @OperationLog - @Operation(summary = "删除患者") - @DeleteMapping("/{id}") - public ApiResult remove(@PathVariable("id") Integer id) { - if (clinicPatientUserService.removeById(id)) { - return success("删除成功"); - } - return fail("删除失败"); - } - - @PreAuthorize("hasAuthority('clinic:clinicPatientUser:save')") - @OperationLog - @Operation(summary = "批量添加患者") - @PostMapping("/batch") - public ApiResult saveBatch(@RequestBody List list) { - if (clinicPatientUserService.saveBatch(list)) { - return success("添加成功"); - } - return fail("添加失败"); - } - - @PreAuthorize("hasAuthority('clinic:clinicPatientUser:update')") - @OperationLog - @Operation(summary = "批量修改患者") - @PutMapping("/batch") - public ApiResult removeBatch(@RequestBody BatchParam batchParam) { - if (batchParam.update(clinicPatientUserService, "id")) { - return success("修改成功"); - } - return fail("修改失败"); - } - - @PreAuthorize("hasAuthority('clinic:clinicPatientUser:remove')") - @OperationLog - @Operation(summary = "批量删除患者") - @DeleteMapping("/batch") - public ApiResult removeBatch(@RequestBody List ids) { - if (clinicPatientUserService.removeByIds(ids)) { - return success("删除成功"); - } - return fail("删除失败"); - } - -} diff --git a/src/main/java/com/gxwebsoft/clinic/controller/ClinicPrescriptionController.java b/src/main/java/com/gxwebsoft/clinic/controller/ClinicPrescriptionController.java deleted file mode 100644 index f51872e..0000000 --- a/src/main/java/com/gxwebsoft/clinic/controller/ClinicPrescriptionController.java +++ /dev/null @@ -1,191 +0,0 @@ -package com.gxwebsoft.clinic.controller; - -import cn.hutool.core.util.IdUtil; -import com.gxwebsoft.clinic.dto.PrescriptionOrderRequest; -import com.gxwebsoft.clinic.entity.ClinicPrescription; -import com.gxwebsoft.clinic.param.ClinicPrescriptionParam; -import com.gxwebsoft.clinic.service.ClinicPrescriptionService; -import com.gxwebsoft.common.core.annotation.OperationLog; -import com.gxwebsoft.common.core.web.ApiResult; -import com.gxwebsoft.common.core.web.BaseController; -import com.gxwebsoft.common.core.web.BatchParam; -import com.gxwebsoft.common.core.web.PageResult; -import com.gxwebsoft.common.system.entity.User; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.tags.Tag; -import lombok.extern.slf4j.Slf4j; -import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.web.bind.annotation.*; - -import javax.annotation.Resource; -import java.time.LocalDateTime; -import java.util.List; - -/** - * 处方主表 -控制器 - * - * @author 科技小王子 - * @since 2025-10-22 02:01:13 - */ -@Tag(name = "处方主表管理") -@RestController -@RequestMapping("/api/clinic/clinic-prescription") -public class ClinicPrescriptionController extends BaseController { - @Resource - private ClinicPrescriptionService clinicPrescriptionService; - - @Operation(summary = "分页查询处方主表") - @GetMapping("/page") - public ApiResult> page(ClinicPrescriptionParam param) { - // 使用关联查询 - return success(clinicPrescriptionService.pageRel(param)); - } - - @PreAuthorize("hasAuthority('clinic:clinicPrescription:list')") - @Operation(summary = "查询全部处方主表") - @GetMapping() - public ApiResult> list(ClinicPrescriptionParam param) { - // 使用关联查询 - return success(clinicPrescriptionService.listRel(param)); - } - - @Operation(summary = "根据id查询处方主表") - @GetMapping("/{id}") - public ApiResult get(@PathVariable("id") Integer id) { - // 使用关联查询 - return success(clinicPrescriptionService.getByIdRel(id)); - } - - @PreAuthorize("hasAuthority('clinic:clinicPrescription:save')") - @OperationLog - @Operation(summary = "添加处方主表") - @PostMapping() - public ApiResult save(@RequestBody ClinicPrescription clinicPrescription) { - // 记录当前登录用户id - User loginUser = getLoginUser(); - if (loginUser != null) { - clinicPrescription.setDoctorId(loginUser.getUserId()); - // 生成订单号 - String orderNo = Long.toString(IdUtil.getSnowflakeNextId()); - clinicPrescription.setOrderNo(orderNo); - } - if (clinicPrescriptionService.save(clinicPrescription)) { - // 返回处方数据,包含处方ID - return success("添加成功",clinicPrescriptionService.getByLastId(clinicPrescription)); - } - return fail("添加失败"); - } - - @PreAuthorize("hasAuthority('clinic:clinicPrescription:update')") - @OperationLog - @Operation(summary = "修改处方主表") - @PutMapping() - public ApiResult update(@RequestBody ClinicPrescription clinicPrescription) { - if (clinicPrescriptionService.updateById(clinicPrescription)) { - return success("修改成功"); - } - return fail("修改失败"); - } - - @PreAuthorize("hasAuthority('clinic:clinicPrescription:remove')") - @OperationLog - @Operation(summary = "删除处方主表") - @DeleteMapping("/{id}") - public ApiResult remove(@PathVariable("id") Integer id) { - if (clinicPrescriptionService.removeById(id)) { - return success("删除成功"); - } - return fail("删除失败"); - } - - @PreAuthorize("hasAuthority('clinic:clinicPrescription:save')") - @OperationLog - @Operation(summary = "批量添加处方主表") - @PostMapping("/batch") - public ApiResult saveBatch(@RequestBody List list) { - if (clinicPrescriptionService.saveBatch(list)) { - return success("添加成功"); - } - return fail("添加失败"); - } - - @PreAuthorize("hasAuthority('clinic:clinicPrescription:update')") - @OperationLog - @Operation(summary = "批量修改处方主表") - @PutMapping("/batch") - public ApiResult removeBatch(@RequestBody BatchParam batchParam) { - if (batchParam.update(clinicPrescriptionService, "id")) { - return success("修改成功"); - } - return fail("修改失败"); - } - - @PreAuthorize("hasAuthority('clinic:clinicPrescription:remove')") - @OperationLog - @Operation(summary = "批量删除处方主表") - @DeleteMapping("/batch") - public ApiResult removeBatch(@RequestBody List ids) { - if (clinicPrescriptionService.removeByIds(ids)) { - return success("删除成功"); - } - return fail("删除失败"); - } - - @Operation(summary = "创建处方订单") - @PostMapping("/order") - public ApiResult createOrder(@RequestBody PrescriptionOrderRequest request) { - try { - // 1. 参数校验 - if (request.getPrescriptionId() == null) { - return fail("处方ID不能为空"); - } - if (request.getPayType() == null) { - return fail("支付方式不能为空"); - } - - // 2. 查询处方信息 - ClinicPrescription prescription = clinicPrescriptionService.getById(request.getPrescriptionId()); - if (prescription == null) { - return fail("处方不存在"); - } - - // 3. 检查处方状态 - if (prescription.getStatus() != null && prescription.getStatus() == 2) { - return fail("该处方已支付,无需重复支付"); - } - if (prescription.getStatus() != null && prescription.getStatus() == 3) { - return fail("该处方已取消,无法支付"); - } - - // 4. 更新处方订单信息 - ClinicPrescription updatePrescription = new ClinicPrescription(); - updatePrescription.setId(request.getPrescriptionId()); - - // 根据支付类型更新状态 - if (request.getPayType() == 1) { - // 微信支付,状态保持为正常,等待支付回调 - updatePrescription.setStatus(0); - } else if (request.getPayType() == 4 || request.getPayType() == 5) { - // 现金支付或POS机支付,直接标记为已支付 - updatePrescription.setStatus(2); - updatePrescription.setIsSettled(1); - updatePrescription.setSettleTime(LocalDateTime.now()); - } else if (request.getPayType() == 6) { - // 免费,直接标记为已完成 - updatePrescription.setStatus(1); - updatePrescription.setIsSettled(1); - updatePrescription.setSettleTime(LocalDateTime.now()); - } - - if (clinicPrescriptionService.updateById(updatePrescription)) { - return success("订单创建成功", prescription); - } - return fail("订单创建失败"); - - } catch (Exception e) { - return fail("订单创建失败:" + e.getMessage()); - } - } - -} diff --git a/src/main/java/com/gxwebsoft/clinic/controller/ClinicPrescriptionItemController.java b/src/main/java/com/gxwebsoft/clinic/controller/ClinicPrescriptionItemController.java deleted file mode 100644 index eda3050..0000000 --- a/src/main/java/com/gxwebsoft/clinic/controller/ClinicPrescriptionItemController.java +++ /dev/null @@ -1,121 +0,0 @@ -package com.gxwebsoft.clinic.controller; - -import com.gxwebsoft.clinic.entity.ClinicPrescriptionItem; -import com.gxwebsoft.clinic.param.ClinicPrescriptionItemParam; -import com.gxwebsoft.clinic.service.ClinicPrescriptionItemService; -import com.gxwebsoft.common.core.annotation.OperationLog; -import com.gxwebsoft.common.core.web.ApiResult; -import com.gxwebsoft.common.core.web.BaseController; -import com.gxwebsoft.common.core.web.BatchParam; -import com.gxwebsoft.common.core.web.PageResult; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.tags.Tag; -import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.web.bind.annotation.*; - -import javax.annotation.Resource; -import java.util.List; - -/** - * 处方明细表 -控制器 - * - * @author 科技小王子 - * @since 2025-10-22 02:01:13 - */ -@Tag(name = "处方明细表管理") -@RestController -@RequestMapping("/api/clinic/clinic-prescription-item") -public class ClinicPrescriptionItemController extends BaseController { - @Resource - private ClinicPrescriptionItemService clinicPrescriptionItemService; - - @Operation(summary = "分页查询处方明细表") - @GetMapping("/page") - public ApiResult> page(ClinicPrescriptionItemParam param) { - // 使用关联查询 - return success(clinicPrescriptionItemService.pageRel(param)); - } - - @PreAuthorize("hasAuthority('clinic:clinicPrescription:list')") - @Operation(summary = "查询全部处方明细表") - @GetMapping() - public ApiResult> list(ClinicPrescriptionItemParam param) { - // 使用关联查询 - return success(clinicPrescriptionItemService.listRel(param)); - } - - @Operation(summary = "根据id查询处方明细表") - @GetMapping("/{id}") - public ApiResult get(@PathVariable("id") Integer id) { - // 使用关联查询 - return success(clinicPrescriptionItemService.getByIdRel(id)); - } - - @PreAuthorize("hasAuthority('clinic:clinicPrescription:save')") - @OperationLog - @Operation(summary = "添加处方明细表") - @PostMapping() - public ApiResult save(@RequestBody ClinicPrescriptionItem clinicPrescriptionItem) { - if (clinicPrescriptionItemService.save(clinicPrescriptionItem)) { - return success("添加成功"); - } - return fail("添加失败"); - } - - @PreAuthorize("hasAuthority('clinic:clinicPrescription:update')") - @OperationLog - @Operation(summary = "修改处方明细表") - @PutMapping() - public ApiResult update(@RequestBody ClinicPrescriptionItem clinicPrescriptionItem) { - if (clinicPrescriptionItemService.updateById(clinicPrescriptionItem)) { - return success("修改成功"); - } - return fail("修改失败"); - } - - @PreAuthorize("hasAuthority('clinic:clinicPrescription:remove')") - @OperationLog - @Operation(summary = "删除处方明细表") - @DeleteMapping("/{id}") - public ApiResult remove(@PathVariable("id") Integer id) { - if (clinicPrescriptionItemService.removeById(id)) { - return success("删除成功"); - } - return fail("删除失败"); - } - - @PreAuthorize("hasAuthority('clinic:clinicPrescription:save')") - @OperationLog - @Operation(summary = "批量添加处方明细表") - @PostMapping("/batch") - public ApiResult saveBatch(@RequestBody List list) { - if (clinicPrescriptionItemService.saveBatch(list)) { - return success("添加成功"); - } - return fail("添加失败"); - } - - @PreAuthorize("hasAuthority('clinic:clinicPrescription:update')") - @OperationLog - @Operation(summary = "批量修改处方明细表") - @PutMapping("/batch") - public ApiResult removeBatch(@RequestBody BatchParam batchParam) { - if (batchParam.update(clinicPrescriptionItemService, "id")) { - return success("修改成功"); - } - return fail("修改失败"); - } - - @PreAuthorize("hasAuthority('clinic:clinicPrescription:remove')") - @OperationLog - @Operation(summary = "批量删除处方明细表") - @DeleteMapping("/batch") - public ApiResult removeBatch(@RequestBody List ids) { - if (clinicPrescriptionItemService.removeByIds(ids)) { - return success("删除成功"); - } - return fail("删除失败"); - } - -} diff --git a/src/main/java/com/gxwebsoft/clinic/controller/业务中台-排班信息接口对接文档20251114(2).docx b/src/main/java/com/gxwebsoft/clinic/controller/业务中台-排班信息接口对接文档20251114(2).docx deleted file mode 100644 index 8b2171604dfd002142f9d3e93b93d3ed999672cf..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 115163 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

BUn#)QDpH{AE@mWJQ=amO{|@6U}iec#n$0WU(c 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

fKa8_$z64_W6U(m(Od>5yBa16TXOxG80DB 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 BfRO+I diff --git a/src/main/java/com/gxwebsoft/clinic/dto/PrescriptionOrderRequest.java b/src/main/java/com/gxwebsoft/clinic/dto/PrescriptionOrderRequest.java deleted file mode 100644 index 44bc7c2..0000000 --- a/src/main/java/com/gxwebsoft/clinic/dto/PrescriptionOrderRequest.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.gxwebsoft.clinic.dto; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -import java.io.Serializable; - -/** - * 处方订单请求参数 - * - * @author 科技小王子 - * @since 2025-11-03 - */ -@Data -@Schema(name = "PrescriptionOrderRequest", description = "处方订单请求参数") -public class PrescriptionOrderRequest implements Serializable { - private static final long serialVersionUID = 1L; - - @Schema(description = "处方ID", required = true) - private Integer prescriptionId; - - @Schema(description = "支付方式:0余额支付,1微信支付,2支付宝支付,3银联支付,4现金支付,5POS机支付,6免费,7积分支付", required = true) - private Integer payType; -} diff --git a/src/main/java/com/gxwebsoft/clinic/entity/ClinicAppointment.java b/src/main/java/com/gxwebsoft/clinic/entity/ClinicAppointment.java deleted file mode 100644 index c38ae8d..0000000 --- a/src/main/java/com/gxwebsoft/clinic/entity/ClinicAppointment.java +++ /dev/null @@ -1,81 +0,0 @@ -package com.gxwebsoft.clinic.entity; - -import com.baomidou.mybatisplus.annotation.IdType; -import com.baomidou.mybatisplus.annotation.TableField; -import com.baomidou.mybatisplus.annotation.TableId; -import java.time.LocalDateTime; -import java.io.Serializable; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; -import lombok.EqualsAndHashCode; -import com.fasterxml.jackson.annotation.JsonFormat; - -/** - * 挂号 - * - * @author 科技小王子 - * @since 2025-10-19 09:27:03 - */ -@Data -@EqualsAndHashCode(callSuper = false) -@Schema(name = "ClinicAppointment对象", description = "挂号") -public class ClinicAppointment implements Serializable { - private static final long serialVersionUID = 1L; - - @Schema(description = "主键ID") - @TableId(value = "id", type = IdType.AUTO) - private Integer id; - - @Schema(description = "类型") - private Integer type; - - @Schema(description = "就诊原因") - private String reason; - - @Schema(description = "挂号时间") - @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") - private LocalDateTime evaluateTime; - - @Schema(description = "医生") - private Integer doctorId; - - @Schema(description = "医生名称") - @TableField(exist = false) - private String doctorName; - - @Schema(description = "医生职位") - @TableField(exist = false) - private String doctorPosition; - - @Schema(description = "患者") - private Integer userId; - - @Schema(description = "患者名称") - @TableField(exist = false) - private String nickname; - - @Schema(description = "手机") - @TableField(exist = false) - private String phone; - - @Schema(description = "备注") - private String comments; - - @Schema(description = "排序号") - private Integer sortNumber; - - @Schema(description = "是否删除") - private Integer isDelete; - - @Schema(description = "租户id") - private Integer tenantId; - - @Schema(description = "创建时间") - @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") - private LocalDateTime createTime; - - @Schema(description = "修改时间") - @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") - private LocalDateTime updateTime; - -} diff --git a/src/main/java/com/gxwebsoft/clinic/entity/ClinicDoctorApply.java b/src/main/java/com/gxwebsoft/clinic/entity/ClinicDoctorApply.java deleted file mode 100644 index 8d8e173..0000000 --- a/src/main/java/com/gxwebsoft/clinic/entity/ClinicDoctorApply.java +++ /dev/null @@ -1,125 +0,0 @@ -package com.gxwebsoft.clinic.entity; - -import java.math.BigDecimal; -import com.baomidou.mybatisplus.annotation.IdType; -import java.time.LocalDate; -import com.baomidou.mybatisplus.annotation.TableId; -import java.time.LocalDateTime; -import java.io.Serializable; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; -import lombok.EqualsAndHashCode; -import com.fasterxml.jackson.annotation.JsonFormat; - -/** - * 医生入驻申请 - * - * @author 科技小王子 - * @since 2025-10-19 09:27:04 - */ -@Data -@EqualsAndHashCode(callSuper = false) -@Schema(name = "ClinicDoctorApply对象", description = "医生入驻申请") -public class ClinicDoctorApply implements Serializable { - private static final long serialVersionUID = 1L; - - @Schema(description = "主键ID") - @TableId(value = "apply_id", type = IdType.AUTO) - private Integer applyId; - - @Schema(description = "类型 0医生") - private Integer type; - - @Schema(description = "用户ID") - private Integer userId; - - @Schema(description = "姓名") - private String realName; - - @Schema(description = "性别 1男 2女") - private Integer gender; - - @Schema(description = "手机号") - private String mobile; - - @Schema(description = "客户名称") - private String dealerName; - - @Schema(description = "证件号码") - private String idCard; - - @Schema(description = "生日") - @JsonFormat(pattern = "yyyy-MM-dd") - private LocalDate birthDate; - - @Schema(description = "区分职称等级(如主治医师、副主任医师)") - private String professionalTitle; - - @Schema(description = "工作单位") - private String workUnit; - - @Schema(description = "执业资格核心凭证") - private String practiceLicense; - - @Schema(description = "限定可执业科室或疾病类型") - private String practiceScope; - - @Schema(description = "开始工作时间") - @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") - private LocalDateTime startWorkDate; - - @Schema(description = "简历") - private String resume; - - @Schema(description = "使用 JSON 存储多个证件文件路径(如执业证、学历证)") - private String certificationFiles; - - @Schema(description = "详细地址") - private String address; - - @Schema(description = "签约价格") - private BigDecimal money; - - @Schema(description = "推荐人用户ID") - private Integer refereeId; - - @Schema(description = "申请方式(10需后台审核 20无需审核)") - private Integer applyType; - - @Schema(description = "审核状态 (10待审核 20审核通过 30驳回)") - private Integer applyStatus; - - @Schema(description = "申请时间") - @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") - private LocalDateTime applyTime; - - @Schema(description = "审核时间") - @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") - private LocalDateTime auditTime; - - @Schema(description = "合同时间") - @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") - private LocalDateTime contractTime; - - @Schema(description = "过期时间") - @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") - private LocalDateTime expirationTime; - - @Schema(description = "驳回原因") - private String rejectReason; - - @Schema(description = "备注") - private String comments; - - @Schema(description = "商城ID") - private Integer tenantId; - - @Schema(description = "创建时间") - @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") - private LocalDateTime createTime; - - @Schema(description = "修改时间") - @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") - private LocalDateTime updateTime; - -} diff --git a/src/main/java/com/gxwebsoft/clinic/entity/ClinicDoctorUser.java b/src/main/java/com/gxwebsoft/clinic/entity/ClinicDoctorUser.java deleted file mode 100644 index b95c86b..0000000 --- a/src/main/java/com/gxwebsoft/clinic/entity/ClinicDoctorUser.java +++ /dev/null @@ -1,99 +0,0 @@ -package com.gxwebsoft.clinic.entity; - -import com.baomidou.mybatisplus.annotation.IdType; -import com.baomidou.mybatisplus.annotation.TableField; -import com.baomidou.mybatisplus.annotation.TableId; -import com.fasterxml.jackson.annotation.JsonFormat; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; -import lombok.EqualsAndHashCode; - -import java.io.Serializable; -import java.math.BigDecimal; -import java.time.LocalDateTime; - -/** - * 分销商用户记录表 - * - * @author 科技小王子 - * @since 2025-10-23 15:58:20 - */ -@Data -@EqualsAndHashCode(callSuper = false) -@Schema(name = "ClinicDoctorUser对象", description = "分销商用户记录表") -public class ClinicDoctorUser implements Serializable { - private static final long serialVersionUID = 1L; - - @Schema(description = "主键ID") - @TableId(value = "id", type = IdType.AUTO) - private Integer id; - - @Schema(description = "类型 0经销商 1企业 2集团") - private Integer type; - - @Schema(description = "自增ID") - private Integer userId; - - @Schema(description = "昵称") - @TableField(exist = false) - private String nickname; - - @Schema(description = "头像") - @TableField(exist = false) - private String avatar; - - @Schema(description = "姓名") - private String realName; - - @Schema(description = "手机号") - @TableField(exist = false) - private String phone; - - @Schema(description = "部门") - private Integer departmentId; - - @Schema(description = "专业领域") - private String specialty; - - @Schema(description = "职务级别") - private String position; - - @Schema(description = "执业资格") - private String qualification; - - @Schema(description = "医生简介") - private String introduction; - - @Schema(description = "挂号费") - private BigDecimal consultationFee; - - @Schema(description = "工作年限") - private Integer workYears; - - @Schema(description = "问诊人数") - private Integer consultationCount; - - @Schema(description = "专属二维码") - private String qrcode; - - @Schema(description = "备注") - private String comments; - - @Schema(description = "排序号") - private Integer sortNumber; - - @Schema(description = "是否删除") - private Integer isDelete; - - @Schema(description = "租户id") - private Integer tenantId; - - @Schema(description = "创建时间") - @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") - private LocalDateTime createTime; - - @Schema(description = "修改时间") - @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") - private LocalDateTime updateTime; - -} diff --git a/src/main/java/com/gxwebsoft/clinic/entity/ClinicMedicine.java b/src/main/java/com/gxwebsoft/clinic/entity/ClinicMedicine.java deleted file mode 100644 index 43aa6df..0000000 --- a/src/main/java/com/gxwebsoft/clinic/entity/ClinicMedicine.java +++ /dev/null @@ -1,71 +0,0 @@ -package com.gxwebsoft.clinic.entity; - -import com.baomidou.mybatisplus.annotation.IdType; -import com.baomidou.mybatisplus.annotation.TableId; -import com.fasterxml.jackson.annotation.JsonFormat; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; -import lombok.EqualsAndHashCode; - -import java.io.Serializable; -import java.math.BigDecimal; -import java.time.LocalDateTime; - -/** - * 药品库 - * - * @author 科技小王子 - * @since 2025-10-22 02:06:31 - */ -@Data -@EqualsAndHashCode(callSuper = false) -@Schema(name = "ClinicMedicine对象", description = "药品库") -public class ClinicMedicine implements Serializable { - private static final long serialVersionUID = 1L; - - @Schema(description = "主键ID") - @TableId(value = "id", type = IdType.AUTO) - private Integer id; - - @Schema(description = "药名") - private String name; - - @Schema(description = "拼音") - private String pinyin; - - @Schema(description = "分类(如“清热解毒”、“补气养血”)") - private String category; - - @Schema(description = "规格(如“饮片”、“颗粒”)") - private String specification; - - @Schema(description = "单位(如“克”、“袋”)") - private String unit; - - @Schema(description = "描述") - private String content; - - @Schema(description = "单价") - private BigDecimal pricePerUnit; - - @Schema(description = "是否活跃") - private Integer isActive; - - @Schema(description = "买家用户ID") - private Integer userId; - - @Schema(description = "备注") - private String comments; - - @Schema(description = "商城ID") - private Integer tenantId; - - @Schema(description = "创建时间") - @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") - private LocalDateTime createTime; - - @Schema(description = "修改时间") - @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") - private LocalDateTime updateTime; - -} diff --git a/src/main/java/com/gxwebsoft/clinic/entity/ClinicMedicineInout.java b/src/main/java/com/gxwebsoft/clinic/entity/ClinicMedicineInout.java deleted file mode 100644 index 3d3f428..0000000 --- a/src/main/java/com/gxwebsoft/clinic/entity/ClinicMedicineInout.java +++ /dev/null @@ -1,99 +0,0 @@ -package com.gxwebsoft.clinic.entity; - -import com.baomidou.mybatisplus.annotation.IdType; -import com.baomidou.mybatisplus.annotation.TableId; -import com.fasterxml.jackson.annotation.JsonFormat; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; -import lombok.EqualsAndHashCode; - -import java.io.Serializable; -import java.math.BigDecimal; -import java.time.LocalDateTime; - -/** - * 出入库 - * - * @author 科技小王子 - * @since 2025-10-22 02:06:32 - */ -@Data -@EqualsAndHashCode(callSuper = false) -@Schema(name = "ClinicMedicineInout对象", description = "出入库") -public class ClinicMedicineInout implements Serializable { - private static final long serialVersionUID = 1L; - - @Schema(description = "主键ID") - @TableId(value = "id", type = IdType.AUTO) - private Integer id; - - @Schema(description = "买家用户ID") - private Integer userId; - - @Schema(description = "订单编号") - private String orderNo; - - @Schema(description = "分销商用户id(一级)") - private Integer firstUserId; - - @Schema(description = "分销商用户id(二级)") - private Integer secondUserId; - - @Schema(description = "分销商用户id(三级)") - private Integer thirdUserId; - - @Schema(description = "分销佣金(一级)") - private BigDecimal firstMoney; - - @Schema(description = "分销佣金(二级)") - private BigDecimal secondMoney; - - @Schema(description = "分销佣金(三级)") - private BigDecimal thirdMoney; - - @Schema(description = "单价") - private BigDecimal price; - - @Schema(description = "订单总金额") - private BigDecimal orderPrice; - - @Schema(description = "结算金额") - private BigDecimal settledPrice; - - @Schema(description = "换算成度") - private BigDecimal degreePrice; - - @Schema(description = "实发金额") - private BigDecimal payPrice; - - @Schema(description = "税率") - private BigDecimal rate; - - @Schema(description = "结算月份") - private String month; - - @Schema(description = "订单是否失效(0未失效 1已失效)") - private Integer isInvalid; - - @Schema(description = "佣金结算(0未结算 1已结算)") - private Integer isSettled; - - @Schema(description = "结算时间") - @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") - private LocalDateTime settleTime; - - @Schema(description = "备注") - private String comments; - - @Schema(description = "商城ID") - private Integer tenantId; - - @Schema(description = "创建时间") - @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") - private LocalDateTime createTime; - - @Schema(description = "修改时间") - @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") - private LocalDateTime updateTime; - -} diff --git a/src/main/java/com/gxwebsoft/clinic/entity/ClinicMedicineStock.java b/src/main/java/com/gxwebsoft/clinic/entity/ClinicMedicineStock.java deleted file mode 100644 index aa5973a..0000000 --- a/src/main/java/com/gxwebsoft/clinic/entity/ClinicMedicineStock.java +++ /dev/null @@ -1,59 +0,0 @@ -package com.gxwebsoft.clinic.entity; - -import com.baomidou.mybatisplus.annotation.IdType; -import com.baomidou.mybatisplus.annotation.TableId; -import com.fasterxml.jackson.annotation.JsonFormat; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; -import lombok.EqualsAndHashCode; - -import java.io.Serializable; -import java.time.LocalDateTime; - -/** - * 药品库存 - * - * @author 科技小王子 - * @since 2025-10-22 02:06:32 - */ -@Data -@EqualsAndHashCode(callSuper = false) -@Schema(name = "ClinicMedicineStock对象", description = "药品库存") -public class ClinicMedicineStock implements Serializable { - private static final long serialVersionUID = 1L; - - @Schema(description = "主键ID") - @TableId(value = "id", type = IdType.AUTO) - private Integer id; - - @Schema(description = "药品") - private Integer medicineId; - - @Schema(description = "库存数量") - private Integer stockQuantity; - - @Schema(description = "最小库存预警") - private Integer minStockLevel; - - @Schema(description = "上次更新时间") - @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") - private LocalDateTime lastUpdated; - - @Schema(description = "买家用户ID") - private Integer userId; - - @Schema(description = "备注") - private String comments; - - @Schema(description = "商城ID") - private Integer tenantId; - - @Schema(description = "创建时间") - @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") - private LocalDateTime createTime; - - @Schema(description = "修改时间") - @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") - private LocalDateTime updateTime; - -} diff --git a/src/main/java/com/gxwebsoft/clinic/entity/ClinicPatientUser.java b/src/main/java/com/gxwebsoft/clinic/entity/ClinicPatientUser.java deleted file mode 100644 index 1b99fcb..0000000 --- a/src/main/java/com/gxwebsoft/clinic/entity/ClinicPatientUser.java +++ /dev/null @@ -1,85 +0,0 @@ -package com.gxwebsoft.clinic.entity; - -import java.math.BigDecimal; -import com.baomidou.mybatisplus.annotation.IdType; -import com.baomidou.mybatisplus.annotation.TableField; -import com.baomidou.mybatisplus.annotation.TableId; -import java.time.LocalDateTime; -import java.io.Serializable; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; -import lombok.EqualsAndHashCode; -import com.fasterxml.jackson.annotation.JsonFormat; - -/** - * 患者 - * - * @author 科技小王子 - * @since 2025-10-19 09:27:04 - */ -@Data -@EqualsAndHashCode(callSuper = false) -@Schema(name = "ClinicPatientUser对象", description = "患者") -public class ClinicPatientUser implements Serializable { - private static final long serialVersionUID = 1L; - - @Schema(description = "主键ID") - @TableId(value = "id", type = IdType.AUTO) - private Integer id; - - @Schema(description = "类型 0经销商 1企业 2集团") - private Integer type; - - @Schema(description = "自增ID") - private Integer userId; - - @Schema(description = "姓名") - private String realName; - - @Schema(description = "头像") - @TableField(exist = false) - private String avatar; - - @Schema(description = "手机号") - @TableField(exist = false) - private String phone; - - @Schema(description = "性别 0未知 1男 2女") - private Integer sex; - - @Schema(description = "年龄") - private Integer age; - - @Schema(description = "身高") - private String height; - - @Schema(description = "体重") - private String weight; - - @Schema(description = "过敏史") - private String allergyHistory; - - @Schema(description = "专属二维码") - private String qrcode; - - @Schema(description = "备注") - private String comments; - - @Schema(description = "排序号") - private Integer sortNumber; - - @Schema(description = "是否删除") - private Integer isDelete; - - @Schema(description = "租户id") - private Integer tenantId; - - @Schema(description = "创建时间") - @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") - private LocalDateTime createTime; - - @Schema(description = "修改时间") - @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") - private LocalDateTime updateTime; - -} diff --git a/src/main/java/com/gxwebsoft/clinic/entity/ClinicPrescription.java b/src/main/java/com/gxwebsoft/clinic/entity/ClinicPrescription.java deleted file mode 100644 index 688adfd..0000000 --- a/src/main/java/com/gxwebsoft/clinic/entity/ClinicPrescription.java +++ /dev/null @@ -1,133 +0,0 @@ -package com.gxwebsoft.clinic.entity; - -import com.baomidou.mybatisplus.annotation.IdType; -import com.baomidou.mybatisplus.annotation.TableField; -import com.baomidou.mybatisplus.annotation.TableId; -import com.fasterxml.jackson.annotation.JsonFormat; -import com.gxwebsoft.shop.entity.ShopOrder; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; -import lombok.EqualsAndHashCode; - -import java.io.Serializable; -import java.math.BigDecimal; -import java.time.LocalDateTime; -import java.util.List; - -/** - * 处方主表 - - * - * @author 科技小王子 - * @since 2025-10-22 02:01:13 - */ -@Data -@EqualsAndHashCode(callSuper = false) -@Schema(name = "ClinicPrescription对象", description = "处方主表") -public class ClinicPrescription implements Serializable { - private static final long serialVersionUID = 1L; - - @Schema(description = "主键ID") - @TableId(value = "id", type = IdType.AUTO) - private Integer id; - - @Schema(description = "患者") - private Integer userId; - - @Schema(description = "患者名称") - @TableField(exist = false) - private String realName; - - @Schema(description = "年龄") - @TableField(exist = false) - private String age; - - @Schema(description = "身高") - @TableField(exist = false) - private String height; - - @Schema(description = "体重") - @TableField(exist = false) - private String weight; - - @Schema(description = "医生") - private Integer doctorId; - - @Schema(description = "医生名称") - @TableField(exist = false) - private String doctorName; - - @Schema(description = "医生资格") - @TableField(exist = false) - private String qualification; - - @Schema(description = "订单编号") - private String orderNo; - - @Schema(description = "0未付款,1已付款") - @TableField(exist = false) - private Boolean payStatus; - - @Schema(description = "0未使用,1已完成,2已取消,3取消中,4退款申请中,5退款被拒绝,6退款成功,7客户端申请退款") - @TableField(exist = false) - private Integer orderStatus; - - @Schema(description = "关联就诊表") - private Integer visitRecordId; - - @Schema(description = "处方类型 0中药 1西药") - private Integer prescriptionType; - - @Schema(description = "诊断结果") - private String diagnosis; - - @Schema(description = "治疗方案") - private String treatmentPlan; - - @Schema(description = "煎药说明") - private String decoctionInstructions; - - @Schema(description = "上传附件") - private String image; - - @Schema(description = "订单总金额") - private BigDecimal orderPrice; - - @Schema(description = "单价") - private BigDecimal price; - - @Schema(description = "实付金额") - private BigDecimal payPrice; - - @Schema(description = "订单是否失效(0未失效 1已失效)") - private Integer isInvalid; - - @Schema(description = "结算(0未结算 1已结算)") - private Integer isSettled; - - @Schema(description = "结算时间") - @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") - private LocalDateTime settleTime; - - @Schema(description = "状态, 0正常, 1已完成,2已支付,3已取消") - private Integer status; - - @Schema(description = "备注") - private String comments; - - @Schema(description = "商城ID") - private Integer tenantId; - - @Schema(description = "创建时间") - @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") - private LocalDateTime createTime; - - @Schema(description = "修改时间") - @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") - private LocalDateTime updateTime; - - @Schema(description = "处方明细") - @TableField(exist = false) - private List items; - -} diff --git a/src/main/java/com/gxwebsoft/clinic/entity/ClinicPrescriptionItem.java b/src/main/java/com/gxwebsoft/clinic/entity/ClinicPrescriptionItem.java deleted file mode 100644 index 78329d0..0000000 --- a/src/main/java/com/gxwebsoft/clinic/entity/ClinicPrescriptionItem.java +++ /dev/null @@ -1,99 +0,0 @@ -package com.gxwebsoft.clinic.entity; - -import com.baomidou.mybatisplus.annotation.IdType; -import com.baomidou.mybatisplus.annotation.TableField; -import com.baomidou.mybatisplus.annotation.TableId; -import com.fasterxml.jackson.annotation.JsonFormat; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; -import lombok.EqualsAndHashCode; - -import java.io.Serializable; -import java.math.BigDecimal; -import java.time.LocalDateTime; - -/** - * 处方明细表 - - * - * @author 科技小王子 - * @since 2025-10-22 02:01:13 - */ -@Data -@EqualsAndHashCode(callSuper = false) -@Schema(name = "ClinicPrescriptionItem对象", description = "处方明细表") -public class ClinicPrescriptionItem implements Serializable { - private static final long serialVersionUID = 1L; - - @Schema(description = "自增ID") - @TableId(value = "id", type = IdType.AUTO) - private Integer id; - - @Schema(description = "关联处方") - private Integer prescriptionId; - - @Schema(description = "订单编号") - private String prescriptionNo; - - @Schema(description = "关联药品") - private Integer medicineId; - - @Schema(description = "药品名称") - @TableField(exist = false) - private String medicineName; - - @Schema(description = "规格") - @TableField(exist = false) - private String specification; - - @Schema(description = "单位") - @TableField(exist = false) - private String unit; - - @Schema(description = "单价") - @TableField(exist = false) - private BigDecimal pricePerUnit; - - @Schema(description = "药品") - @TableField(exist = false) - private ClinicMedicine clinicMedicine; - - @Schema(description = "剂量(如“10g”)") - private String dosage; - - @Schema(description = "用法频率(如“每日三次”)") - private String usageFrequency; - - @Schema(description = "服用天数") - private Integer days; - - @Schema(description = "购买数量") - private Integer amount; - - @Schema(description = "单价") - private BigDecimal unitPrice; - - @Schema(description = "数量") - private Integer quantity; - - @Schema(description = "排序号") - private Integer sortNumber; - - @Schema(description = "备注") - private String comments; - - @Schema(description = "用户id") - private Integer userId; - - @Schema(description = "租户id") - private Integer tenantId; - - @Schema(description = "更新时间") - @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") - private LocalDateTime updateTime; - - @Schema(description = "创建时间") - @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") - private LocalDateTime createTime; - -} diff --git a/src/main/java/com/gxwebsoft/clinic/mapper/ClinicAppointmentMapper.java b/src/main/java/com/gxwebsoft/clinic/mapper/ClinicAppointmentMapper.java deleted file mode 100644 index adcec99..0000000 --- a/src/main/java/com/gxwebsoft/clinic/mapper/ClinicAppointmentMapper.java +++ /dev/null @@ -1,37 +0,0 @@ -package com.gxwebsoft.clinic.mapper; - -import com.baomidou.mybatisplus.core.mapper.BaseMapper; -import com.baomidou.mybatisplus.core.metadata.IPage; -import com.gxwebsoft.clinic.entity.ClinicAppointment; -import com.gxwebsoft.clinic.param.ClinicAppointmentParam; -import org.apache.ibatis.annotations.Param; - -import java.util.List; - -/** - * 挂号Mapper - * - * @author 科技小王子 - * @since 2025-10-19 09:27:03 - */ -public interface ClinicAppointmentMapper extends BaseMapper { - - /** - * 分页查询 - * - * @param page 分页对象 - * @param param 查询参数 - * @return List - */ - List selectPageRel(@Param("page") IPage page, - @Param("param") ClinicAppointmentParam param); - - /** - * 查询全部 - * - * @param param 查询参数 - * @return List - */ - List selectListRel(@Param("param") ClinicAppointmentParam param); - -} diff --git a/src/main/java/com/gxwebsoft/clinic/mapper/ClinicDoctorApplyMapper.java b/src/main/java/com/gxwebsoft/clinic/mapper/ClinicDoctorApplyMapper.java deleted file mode 100644 index 2a08473..0000000 --- a/src/main/java/com/gxwebsoft/clinic/mapper/ClinicDoctorApplyMapper.java +++ /dev/null @@ -1,37 +0,0 @@ -package com.gxwebsoft.clinic.mapper; - -import com.baomidou.mybatisplus.core.mapper.BaseMapper; -import com.baomidou.mybatisplus.core.metadata.IPage; -import com.gxwebsoft.clinic.entity.ClinicDoctorApply; -import com.gxwebsoft.clinic.param.ClinicDoctorApplyParam; -import org.apache.ibatis.annotations.Param; - -import java.util.List; - -/** - * 医生入驻申请Mapper - * - * @author 科技小王子 - * @since 2025-10-19 09:27:04 - */ -public interface ClinicDoctorApplyMapper extends BaseMapper { - - /** - * 分页查询 - * - * @param page 分页对象 - * @param param 查询参数 - * @return List - */ - List selectPageRel(@Param("page") IPage page, - @Param("param") ClinicDoctorApplyParam param); - - /** - * 查询全部 - * - * @param param 查询参数 - * @return List - */ - List selectListRel(@Param("param") ClinicDoctorApplyParam param); - -} diff --git a/src/main/java/com/gxwebsoft/clinic/mapper/ClinicDoctorUserMapper.java b/src/main/java/com/gxwebsoft/clinic/mapper/ClinicDoctorUserMapper.java deleted file mode 100644 index ffda343..0000000 --- a/src/main/java/com/gxwebsoft/clinic/mapper/ClinicDoctorUserMapper.java +++ /dev/null @@ -1,37 +0,0 @@ -package com.gxwebsoft.clinic.mapper; - -import com.baomidou.mybatisplus.core.mapper.BaseMapper; -import com.baomidou.mybatisplus.core.metadata.IPage; -import com.gxwebsoft.clinic.entity.ClinicDoctorUser; -import com.gxwebsoft.clinic.param.ClinicDoctorUserParam; -import org.apache.ibatis.annotations.Param; - -import java.util.List; - -/** - * 分销商用户记录表Mapper - * - * @author 科技小王子 - * @since 2025-10-19 09:27:04 - */ -public interface ClinicDoctorUserMapper extends BaseMapper { - - /** - * 分页查询 - * - * @param page 分页对象 - * @param param 查询参数 - * @return List - */ - List selectPageRel(@Param("page") IPage page, - @Param("param") ClinicDoctorUserParam param); - - /** - * 查询全部 - * - * @param param 查询参数 - * @return List - */ - List selectListRel(@Param("param") ClinicDoctorUserParam param); - -} diff --git a/src/main/java/com/gxwebsoft/clinic/mapper/ClinicMedicineInoutMapper.java b/src/main/java/com/gxwebsoft/clinic/mapper/ClinicMedicineInoutMapper.java deleted file mode 100644 index 9454319..0000000 --- a/src/main/java/com/gxwebsoft/clinic/mapper/ClinicMedicineInoutMapper.java +++ /dev/null @@ -1,37 +0,0 @@ -package com.gxwebsoft.clinic.mapper; - -import com.baomidou.mybatisplus.core.mapper.BaseMapper; -import com.baomidou.mybatisplus.core.metadata.IPage; -import com.gxwebsoft.clinic.entity.ClinicMedicineInout; -import com.gxwebsoft.clinic.param.ClinicMedicineInoutParam; -import org.apache.ibatis.annotations.Param; - -import java.util.List; - -/** - * 出入库Mapper - * - * @author 科技小王子 - * @since 2025-10-22 02:06:32 - */ -public interface ClinicMedicineInoutMapper extends BaseMapper { - - /** - * 分页查询 - * - * @param page 分页对象 - * @param param 查询参数 - * @return List - */ - List selectPageRel(@Param("page") IPage page, - @Param("param") ClinicMedicineInoutParam param); - - /** - * 查询全部 - * - * @param param 查询参数 - * @return List - */ - List selectListRel(@Param("param") ClinicMedicineInoutParam param); - -} diff --git a/src/main/java/com/gxwebsoft/clinic/mapper/ClinicMedicineMapper.java b/src/main/java/com/gxwebsoft/clinic/mapper/ClinicMedicineMapper.java deleted file mode 100644 index 8fca0aa..0000000 --- a/src/main/java/com/gxwebsoft/clinic/mapper/ClinicMedicineMapper.java +++ /dev/null @@ -1,37 +0,0 @@ -package com.gxwebsoft.clinic.mapper; - -import com.baomidou.mybatisplus.core.mapper.BaseMapper; -import com.baomidou.mybatisplus.core.metadata.IPage; -import com.gxwebsoft.clinic.entity.ClinicMedicine; -import com.gxwebsoft.clinic.param.ClinicMedicineParam; -import org.apache.ibatis.annotations.Param; - -import java.util.List; - -/** - * 药品库Mapper - * - * @author 科技小王子 - * @since 2025-10-22 02:06:31 - */ -public interface ClinicMedicineMapper extends BaseMapper { - - /** - * 分页查询 - * - * @param page 分页对象 - * @param param 查询参数 - * @return List - */ - List selectPageRel(@Param("page") IPage page, - @Param("param") ClinicMedicineParam param); - - /** - * 查询全部 - * - * @param param 查询参数 - * @return List - */ - List selectListRel(@Param("param") ClinicMedicineParam param); - -} diff --git a/src/main/java/com/gxwebsoft/clinic/mapper/ClinicMedicineStockMapper.java b/src/main/java/com/gxwebsoft/clinic/mapper/ClinicMedicineStockMapper.java deleted file mode 100644 index 9a95139..0000000 --- a/src/main/java/com/gxwebsoft/clinic/mapper/ClinicMedicineStockMapper.java +++ /dev/null @@ -1,37 +0,0 @@ -package com.gxwebsoft.clinic.mapper; - -import com.baomidou.mybatisplus.core.mapper.BaseMapper; -import com.baomidou.mybatisplus.core.metadata.IPage; -import com.gxwebsoft.clinic.entity.ClinicMedicineStock; -import com.gxwebsoft.clinic.param.ClinicMedicineStockParam; -import org.apache.ibatis.annotations.Param; - -import java.util.List; - -/** - * 药品库存Mapper - * - * @author 科技小王子 - * @since 2025-10-22 02:06:32 - */ -public interface ClinicMedicineStockMapper extends BaseMapper { - - /** - * 分页查询 - * - * @param page 分页对象 - * @param param 查询参数 - * @return List - */ - List selectPageRel(@Param("page") IPage page, - @Param("param") ClinicMedicineStockParam param); - - /** - * 查询全部 - * - * @param param 查询参数 - * @return List - */ - List selectListRel(@Param("param") ClinicMedicineStockParam param); - -} diff --git a/src/main/java/com/gxwebsoft/clinic/mapper/ClinicPatientUserMapper.java b/src/main/java/com/gxwebsoft/clinic/mapper/ClinicPatientUserMapper.java deleted file mode 100644 index bf25805..0000000 --- a/src/main/java/com/gxwebsoft/clinic/mapper/ClinicPatientUserMapper.java +++ /dev/null @@ -1,37 +0,0 @@ -package com.gxwebsoft.clinic.mapper; - -import com.baomidou.mybatisplus.core.mapper.BaseMapper; -import com.baomidou.mybatisplus.core.metadata.IPage; -import com.gxwebsoft.clinic.entity.ClinicPatientUser; -import com.gxwebsoft.clinic.param.ClinicPatientUserParam; -import org.apache.ibatis.annotations.Param; - -import java.util.List; - -/** - * 患者Mapper - * - * @author 科技小王子 - * @since 2025-10-19 09:27:04 - */ -public interface ClinicPatientUserMapper extends BaseMapper { - - /** - * 分页查询 - * - * @param page 分页对象 - * @param param 查询参数 - * @return List - */ - List selectPageRel(@Param("page") IPage page, - @Param("param") ClinicPatientUserParam param); - - /** - * 查询全部 - * - * @param param 查询参数 - * @return List - */ - List selectListRel(@Param("param") ClinicPatientUserParam param); - -} diff --git a/src/main/java/com/gxwebsoft/clinic/mapper/ClinicPrescriptionItemMapper.java b/src/main/java/com/gxwebsoft/clinic/mapper/ClinicPrescriptionItemMapper.java deleted file mode 100644 index 1a21b01..0000000 --- a/src/main/java/com/gxwebsoft/clinic/mapper/ClinicPrescriptionItemMapper.java +++ /dev/null @@ -1,38 +0,0 @@ -package com.gxwebsoft.clinic.mapper; - -import com.baomidou.mybatisplus.core.mapper.BaseMapper; -import com.baomidou.mybatisplus.core.metadata.IPage; -import com.gxwebsoft.clinic.entity.ClinicPrescriptionItem; -import com.gxwebsoft.clinic.param.ClinicPrescriptionItemParam; -import org.apache.ibatis.annotations.Param; - -import java.util.List; - -/** - * 处方明细表 -Mapper - * - * @author 科技小王子 - * @since 2025-10-22 02:01:13 - */ -public interface ClinicPrescriptionItemMapper extends BaseMapper { - - /** - * 分页查询 - * - * @param page 分页对象 - * @param param 查询参数 - * @return List - */ - List selectPageRel(@Param("page") IPage page, - @Param("param") ClinicPrescriptionItemParam param); - - /** - * 查询全部 - * - * @param param 查询参数 - * @return List - */ - List selectListRel(@Param("param") ClinicPrescriptionItemParam param); - -} diff --git a/src/main/java/com/gxwebsoft/clinic/mapper/ClinicPrescriptionMapper.java b/src/main/java/com/gxwebsoft/clinic/mapper/ClinicPrescriptionMapper.java deleted file mode 100644 index 6eeefda..0000000 --- a/src/main/java/com/gxwebsoft/clinic/mapper/ClinicPrescriptionMapper.java +++ /dev/null @@ -1,38 +0,0 @@ -package com.gxwebsoft.clinic.mapper; - -import com.baomidou.mybatisplus.core.mapper.BaseMapper; -import com.baomidou.mybatisplus.core.metadata.IPage; -import com.gxwebsoft.clinic.entity.ClinicPrescription; -import com.gxwebsoft.clinic.param.ClinicPrescriptionParam; -import org.apache.ibatis.annotations.Param; - -import java.util.List; - -/** - * 处方主表 -Mapper - * - * @author 科技小王子 - * @since 2025-10-22 02:01:13 - */ -public interface ClinicPrescriptionMapper extends BaseMapper { - - /** - * 分页查询 - * - * @param page 分页对象 - * @param param 查询参数 - * @return List - */ - List selectPageRel(@Param("page") IPage page, - @Param("param") ClinicPrescriptionParam param); - - /** - * 查询全部 - * - * @param param 查询参数 - * @return List - */ - List selectListRel(@Param("param") ClinicPrescriptionParam param); - -} diff --git a/src/main/java/com/gxwebsoft/clinic/mapper/xml/ClinicAppointmentMapper.xml b/src/main/java/com/gxwebsoft/clinic/mapper/xml/ClinicAppointmentMapper.xml deleted file mode 100644 index 5aee909..0000000 --- a/src/main/java/com/gxwebsoft/clinic/mapper/xml/ClinicAppointmentMapper.xml +++ /dev/null @@ -1,62 +0,0 @@ - - - - - - - SELECT a.*, b.nickname, b.phone, c.real_name as doctorName, c.position as doctorPosition - FROM clinic_appointment a - LEFT JOIN gxwebsoft_core.sys_user b ON a.user_id = b.user_id - LEFT JOIN clinic_doctor_user c ON a.doctor_id = c.user_id - - - AND a.id = #{param.id} - - - AND a.type = #{param.type} - - - AND a.reason LIKE CONCAT('%', #{param.reason}, '%') - - - AND a.evaluate_time LIKE CONCAT('%', #{param.evaluateTime}, '%') - - - AND a.doctor_id = #{param.doctorId} - - - AND a.user_id = #{param.userId} - - - AND a.comments LIKE CONCAT('%', #{param.comments}, '%') - - - AND a.sort_number = #{param.sortNumber} - - - AND a.is_delete = #{param.isDelete} - - - AND a.create_time >= #{param.createTimeStart} - - - AND a.create_time <= #{param.createTimeEnd} - - - AND (a.comments LIKE CONCAT('%', #{param.keywords}, '%') - ) - - - - - - - - - - - diff --git a/src/main/java/com/gxwebsoft/clinic/mapper/xml/ClinicDoctorApplyMapper.xml b/src/main/java/com/gxwebsoft/clinic/mapper/xml/ClinicDoctorApplyMapper.xml deleted file mode 100644 index c701860..0000000 --- a/src/main/java/com/gxwebsoft/clinic/mapper/xml/ClinicDoctorApplyMapper.xml +++ /dev/null @@ -1,114 +0,0 @@ - - - - - - - SELECT a.* - FROM clinic_doctor_apply a - - - AND a.apply_id = #{param.applyId} - - - AND a.type = #{param.type} - - - AND a.user_id = #{param.userId} - - - AND a.real_name LIKE CONCAT('%', #{param.realName}, '%') - - - AND a.gender = #{param.gender} - - - AND a.mobile LIKE CONCAT('%', #{param.mobile}, '%') - - - AND a.dealer_name LIKE CONCAT('%', #{param.dealerName}, '%') - - - AND a.id_card LIKE CONCAT('%', #{param.idCard}, '%') - - - AND a.birth_date LIKE CONCAT('%', #{param.birthDate}, '%') - - - AND a.professional_title LIKE CONCAT('%', #{param.professionalTitle}, '%') - - - AND a.work_unit LIKE CONCAT('%', #{param.workUnit}, '%') - - - AND a.practice_license LIKE CONCAT('%', #{param.practiceLicense}, '%') - - - AND a.practice_scope LIKE CONCAT('%', #{param.practiceScope}, '%') - - - AND a.start_work_date LIKE CONCAT('%', #{param.startWorkDate}, '%') - - - AND a.resume LIKE CONCAT('%', #{param.resume}, '%') - - - AND a.certification_files LIKE CONCAT('%', #{param.certificationFiles}, '%') - - - AND a.address LIKE CONCAT('%', #{param.address}, '%') - - - AND a.money = #{param.money} - - - AND a.referee_id = #{param.refereeId} - - - AND a.apply_type = #{param.applyType} - - - AND a.apply_status = #{param.applyStatus} - - - AND a.apply_time LIKE CONCAT('%', #{param.applyTime}, '%') - - - AND a.audit_time LIKE CONCAT('%', #{param.auditTime}, '%') - - - AND a.contract_time LIKE CONCAT('%', #{param.contractTime}, '%') - - - AND a.expiration_time LIKE CONCAT('%', #{param.expirationTime}, '%') - - - AND a.reject_reason LIKE CONCAT('%', #{param.rejectReason}, '%') - - - AND a.comments LIKE CONCAT('%', #{param.comments}, '%') - - - AND a.create_time >= #{param.createTimeStart} - - - AND a.create_time <= #{param.createTimeEnd} - - - AND (a.comments LIKE CONCAT('%', #{param.keywords}, '%') - ) - - - - - - - - - - - diff --git a/src/main/java/com/gxwebsoft/clinic/mapper/xml/ClinicDoctorUserMapper.xml b/src/main/java/com/gxwebsoft/clinic/mapper/xml/ClinicDoctorUserMapper.xml deleted file mode 100644 index 61e787f..0000000 --- a/src/main/java/com/gxwebsoft/clinic/mapper/xml/ClinicDoctorUserMapper.xml +++ /dev/null @@ -1,86 +0,0 @@ - - - - - - - SELECT a.*, b.nickname, b.phone, b.avatar - FROM clinic_doctor_user a - LEFT JOIN gxwebsoft_core.sys_user b ON a.user_id = b.user_id - - - AND a.id = #{param.id} - - - AND a.type = #{param.type} - - - AND a.user_id = #{param.userId} - - - AND a.real_name LIKE CONCAT('%', #{param.realName}, '%') - - - AND a.phone LIKE CONCAT('%', #{param.phone}, '%') - - - AND a.department_id = #{param.departmentId} - - - AND a.specialty LIKE CONCAT('%', #{param.specialty}, '%') - - - AND a.position LIKE CONCAT('%', #{param.position}, '%') - - - AND a.qualification LIKE CONCAT('%', #{param.qualification}, '%') - - - AND a.introduction LIKE CONCAT('%', #{param.introduction}, '%') - - - AND a.consultation_fee = #{param.consultationFee} - - - AND a.work_years = #{param.workYears} - - - AND a.consultation_count = #{param.consultationCount} - - - AND a.qrcode LIKE CONCAT('%', #{param.qrcode}, '%') - - - AND a.comments LIKE CONCAT('%', #{param.comments}, '%') - - - AND a.sort_number = #{param.sortNumber} - - - AND a.is_delete = #{param.isDelete} - - - AND a.create_time >= #{param.createTimeStart} - - - AND a.create_time <= #{param.createTimeEnd} - - - AND (a.comments LIKE CONCAT('%', #{param.keywords}, '%') - OR a.real_name = #{param.keywords} - ) - - - - - - - - - - - diff --git a/src/main/java/com/gxwebsoft/clinic/mapper/xml/ClinicMedicineInoutMapper.xml b/src/main/java/com/gxwebsoft/clinic/mapper/xml/ClinicMedicineInoutMapper.xml deleted file mode 100644 index 0e49aac..0000000 --- a/src/main/java/com/gxwebsoft/clinic/mapper/xml/ClinicMedicineInoutMapper.xml +++ /dev/null @@ -1,93 +0,0 @@ - - - - - - - SELECT a.* - FROM clinic_medicine_inout a - - - AND a.id = #{param.id} - - - AND a.user_id = #{param.userId} - - - AND a.order_no LIKE CONCAT('%', #{param.orderNo}, '%') - - - AND a.first_user_id = #{param.firstUserId} - - - AND a.second_user_id = #{param.secondUserId} - - - AND a.third_user_id = #{param.thirdUserId} - - - AND a.first_money = #{param.firstMoney} - - - AND a.second_money = #{param.secondMoney} - - - AND a.third_money = #{param.thirdMoney} - - - AND a.price = #{param.price} - - - AND a.order_price = #{param.orderPrice} - - - AND a.settled_price = #{param.settledPrice} - - - AND a.degree_price = #{param.degreePrice} - - - AND a.pay_price = #{param.payPrice} - - - AND a.rate = #{param.rate} - - - AND a.month LIKE CONCAT('%', #{param.month}, '%') - - - AND a.is_invalid = #{param.isInvalid} - - - AND a.is_settled = #{param.isSettled} - - - AND a.settle_time LIKE CONCAT('%', #{param.settleTime}, '%') - - - AND a.comments LIKE CONCAT('%', #{param.comments}, '%') - - - AND a.create_time >= #{param.createTimeStart} - - - AND a.create_time <= #{param.createTimeEnd} - - - AND (a.comments LIKE CONCAT('%', #{param.keywords}, '%') - ) - - - - - - - - - - - diff --git a/src/main/java/com/gxwebsoft/clinic/mapper/xml/ClinicMedicineMapper.xml b/src/main/java/com/gxwebsoft/clinic/mapper/xml/ClinicMedicineMapper.xml deleted file mode 100644 index 644a454..0000000 --- a/src/main/java/com/gxwebsoft/clinic/mapper/xml/ClinicMedicineMapper.xml +++ /dev/null @@ -1,66 +0,0 @@ - - - - - - - SELECT a.* - FROM clinic_medicine a - - - AND a.id = #{param.id} - - - AND a.name LIKE CONCAT('%', #{param.name}, '%') - - - AND a.pinyin LIKE CONCAT('%', #{param.pinyin}, '%') - - - AND a.category LIKE CONCAT('%', #{param.category}, '%') - - - AND a.specification LIKE CONCAT('%', #{param.specification}, '%') - - - AND a.unit LIKE CONCAT('%', #{param.unit}, '%') - - - AND a.content LIKE CONCAT('%', #{param.content}, '%') - - - AND a.price_per_unit = #{param.pricePerUnit} - - - AND a.is_active = #{param.isActive} - - - AND a.user_id = #{param.userId} - - - AND a.comments LIKE CONCAT('%', #{param.comments}, '%') - - - AND a.create_time >= #{param.createTimeStart} - - - AND a.create_time <= #{param.createTimeEnd} - - - AND (a.comments LIKE CONCAT('%', #{param.keywords}, '%') - ) - - - - - - - - - - - diff --git a/src/main/java/com/gxwebsoft/clinic/mapper/xml/ClinicMedicineStockMapper.xml b/src/main/java/com/gxwebsoft/clinic/mapper/xml/ClinicMedicineStockMapper.xml deleted file mode 100644 index 93ffcd9..0000000 --- a/src/main/java/com/gxwebsoft/clinic/mapper/xml/ClinicMedicineStockMapper.xml +++ /dev/null @@ -1,54 +0,0 @@ - - - - - - - SELECT a.* - FROM clinic_medicine_stock a - - - AND a.id = #{param.id} - - - AND a.medicine_id = #{param.medicineId} - - - AND a.stock_quantity = #{param.stockQuantity} - - - AND a.min_stock_level = #{param.minStockLevel} - - - AND a.last_updated LIKE CONCAT('%', #{param.lastUpdated}, '%') - - - AND a.user_id = #{param.userId} - - - AND a.comments LIKE CONCAT('%', #{param.comments}, '%') - - - AND a.create_time >= #{param.createTimeStart} - - - AND a.create_time <= #{param.createTimeEnd} - - - AND (a.comments LIKE CONCAT('%', #{param.keywords}, '%') - ) - - - - - - - - - - - diff --git a/src/main/java/com/gxwebsoft/clinic/mapper/xml/ClinicPatientUserMapper.xml b/src/main/java/com/gxwebsoft/clinic/mapper/xml/ClinicPatientUserMapper.xml deleted file mode 100644 index d2124a7..0000000 --- a/src/main/java/com/gxwebsoft/clinic/mapper/xml/ClinicPatientUserMapper.xml +++ /dev/null @@ -1,71 +0,0 @@ - - - - - - - SELECT a.*, b.phone, b.avatar - FROM clinic_patient_user a - LEFT JOIN gxwebsoft_core.sys_user b ON a.user_id = b.user_id - - - AND a.id = #{param.id} - - - AND a.type = #{param.type} - - - AND a.user_id = #{param.userId} - - - AND a.real_name LIKE CONCAT('%', #{param.realName}, '%') - - - AND a.age LIKE CONCAT('%', #{param.age}, '%') - - - AND a.qrcode LIKE CONCAT('%', #{param.qrcode}, '%') - - - AND a.height LIKE CONCAT('%', #{param.height}, '%') - - - AND a.weight LIKE CONCAT('%', #{param.weight}, '%') - - - AND a.allergy_history LIKE CONCAT('%', #{param.allergyHistory}, '%') - - - AND a.comments LIKE CONCAT('%', #{param.comments}, '%') - - - AND a.sort_number = #{param.sortNumber} - - - AND a.is_delete = #{param.isDelete} - - - AND a.create_time >= #{param.createTimeStart} - - - AND a.create_time <= #{param.createTimeEnd} - - - AND (a.comments LIKE CONCAT('%', #{param.keywords}, '%') - OR a.real_name = #{param.keywords} - ) - - - - - - - - - - - diff --git a/src/main/java/com/gxwebsoft/clinic/mapper/xml/ClinicPrescriptionItemMapper.xml b/src/main/java/com/gxwebsoft/clinic/mapper/xml/ClinicPrescriptionItemMapper.xml deleted file mode 100644 index bd5f01c..0000000 --- a/src/main/java/com/gxwebsoft/clinic/mapper/xml/ClinicPrescriptionItemMapper.xml +++ /dev/null @@ -1,73 +0,0 @@ - - - - - - - SELECT a.*, b.name AS medicineName, b.specification, b.unit, b.price_per_unit AS pricePerUnit - FROM clinic_prescription_item a - LEFT JOIN clinic_medicine b ON a.id = b.id - - - AND a.id = #{param.id} - - - AND a.prescription_id = #{param.prescriptionId} - - - AND a.prescription_no LIKE CONCAT('%', #{param.prescriptionNo}, '%') - - - AND a.medicine_id = #{param.medicineId} - - - AND a.dosage LIKE CONCAT('%', #{param.dosage}, '%') - - - AND a.usage_frequency LIKE CONCAT('%', #{param.usageFrequency}, '%') - - - AND a.days = #{param.days} - - - AND a.amount = #{param.amount} - - - AND a.unit_price = #{param.unitPrice} - - - AND a.quantity = #{param.quantity} - - - AND a.sort_number = #{param.sortNumber} - - - AND a.comments LIKE CONCAT('%', #{param.comments}, '%') - - - AND a.user_id = #{param.userId} - - - AND a.create_time >= #{param.createTimeStart} - - - AND a.create_time <= #{param.createTimeEnd} - - - AND (a.comments LIKE CONCAT('%', #{param.keywords}, '%') - ) - - - - - - - - - - - diff --git a/src/main/java/com/gxwebsoft/clinic/mapper/xml/ClinicPrescriptionMapper.xml b/src/main/java/com/gxwebsoft/clinic/mapper/xml/ClinicPrescriptionMapper.xml deleted file mode 100644 index 877387d..0000000 --- a/src/main/java/com/gxwebsoft/clinic/mapper/xml/ClinicPrescriptionMapper.xml +++ /dev/null @@ -1,132 +0,0 @@ - - - - - - - SELECT a.*, b.real_name, b.age, b.sex, b.height, b.weight, c.real_name as doctorName, c.qualification, d.order_status as orderStatus, d.pay_status as payStatus - FROM clinic_prescription a - LEFT JOIN clinic_patient_user b ON a.user_id = b.user_id - LEFT JOIN clinic_doctor_user c ON a.doctor_id = c.user_id - LEFT JOIN shop_order d ON a.order_no = d.order_no - - - AND a.id = #{param.id} - - - AND a.user_id = #{param.userId} - - - AND a.doctor_id = #{param.doctorId} - - - AND a.order_no LIKE CONCAT('%', #{param.orderNo}, '%') - - - AND a.visit_record_id = #{param.visitRecordId} - - - AND a.prescription_type = #{param.prescriptionType} - - - AND a.diagnosis LIKE CONCAT('%', #{param.diagnosis}, '%') - - - AND a.treatment_plan LIKE CONCAT('%', #{param.treatmentPlan}, '%') - - - AND a.decoction_instructions LIKE CONCAT('%', #{param.decoctionInstructions}, '%') - - - AND a.order_price = #{param.orderPrice} - - - AND a.price = #{param.price} - - - AND a.pay_price = #{param.payPrice} - - - AND a.is_invalid = #{param.isInvalid} - - - AND a.is_settled = #{param.isSettled} - - - AND a.id IN - - #{item} - - - - AND a.settle_time LIKE CONCAT('%', #{param.settleTime}, '%') - - - AND a.status = #{param.status} - - - AND a.comments LIKE CONCAT('%', #{param.comments}, '%') - - - AND a.create_time >= #{param.createTimeStart} - - - AND a.create_time <= #{param.createTimeEnd} - - - AND (a.comments LIKE CONCAT('%', #{param.keywords}, '%') - ) - - - - - - AND d.pay_status = 0 AND d.order_status = 0 - - - - AND d.pay_status = 1 AND d.delivery_status = 10 AND d.order_status = 0 - - - - AND d.pay_status = 1 AND d.order_status = 0 - - - - AND d.delivery_status = 20 AND d.order_status != 1 - - - - AND d.order_status = 1 AND d.evaluate_status = 0 - - - - AND d.order_status = 1 - - - - AND (d.order_status = 4 OR d.order_status = 5 OR d.order_status = 6 OR d.order_status = 7) - - - - AND d.deleted = 1 - - - - AND a.order_status = 2 - - - - - - - - - - - - diff --git a/src/main/java/com/gxwebsoft/clinic/param/ClinicAppointmentParam.java b/src/main/java/com/gxwebsoft/clinic/param/ClinicAppointmentParam.java deleted file mode 100644 index a6e6a17..0000000 --- a/src/main/java/com/gxwebsoft/clinic/param/ClinicAppointmentParam.java +++ /dev/null @@ -1,58 +0,0 @@ -package com.gxwebsoft.clinic.param; - -import java.math.BigDecimal; -import com.gxwebsoft.common.core.annotation.QueryField; -import com.gxwebsoft.common.core.annotation.QueryType; -import com.gxwebsoft.common.core.web.BaseParam; -import com.fasterxml.jackson.annotation.JsonInclude; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; -import lombok.EqualsAndHashCode; - -/** - * 挂号查询参数 - * - * @author 科技小王子 - * @since 2025-10-19 09:27:03 - */ -@Data -@EqualsAndHashCode(callSuper = false) -@JsonInclude(JsonInclude.Include.NON_NULL) -@Schema(name = "ClinicAppointmentParam对象", description = "挂号查询参数") -public class ClinicAppointmentParam extends BaseParam { - private static final long serialVersionUID = 1L; - - @Schema(description = "主键ID") - @QueryField(type = QueryType.EQ) - private Integer id; - - @Schema(description = "类型") - @QueryField(type = QueryType.EQ) - private Integer type; - - @Schema(description = "就诊原因") - private String reason; - - @Schema(description = "挂号时间") - private String evaluateTime; - - @Schema(description = "医生") - @QueryField(type = QueryType.EQ) - private Integer doctorId; - - @Schema(description = "患者") - @QueryField(type = QueryType.EQ) - private Integer userId; - - @Schema(description = "备注") - private String comments; - - @Schema(description = "排序号") - @QueryField(type = QueryType.EQ) - private Integer sortNumber; - - @Schema(description = "是否删除") - @QueryField(type = QueryType.EQ) - private Integer isDelete; - -} diff --git a/src/main/java/com/gxwebsoft/clinic/param/ClinicDoctorApplyParam.java b/src/main/java/com/gxwebsoft/clinic/param/ClinicDoctorApplyParam.java deleted file mode 100644 index e9863da..0000000 --- a/src/main/java/com/gxwebsoft/clinic/param/ClinicDoctorApplyParam.java +++ /dev/null @@ -1,114 +0,0 @@ -package com.gxwebsoft.clinic.param; - -import java.math.BigDecimal; -import com.gxwebsoft.common.core.annotation.QueryField; -import com.gxwebsoft.common.core.annotation.QueryType; -import com.gxwebsoft.common.core.web.BaseParam; -import com.fasterxml.jackson.annotation.JsonInclude; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; -import lombok.EqualsAndHashCode; - -/** - * 医生入驻申请查询参数 - * - * @author 科技小王子 - * @since 2025-10-19 09:27:04 - */ -@Data -@EqualsAndHashCode(callSuper = false) -@JsonInclude(JsonInclude.Include.NON_NULL) -@Schema(name = "ClinicDoctorApplyParam对象", description = "医生入驻申请查询参数") -public class ClinicDoctorApplyParam extends BaseParam { - private static final long serialVersionUID = 1L; - - @Schema(description = "主键ID") - @QueryField(type = QueryType.EQ) - private Integer applyId; - - @Schema(description = "类型 0医生") - @QueryField(type = QueryType.EQ) - private Integer type; - - @Schema(description = "用户ID") - @QueryField(type = QueryType.EQ) - private Integer userId; - - @Schema(description = "姓名") - private String realName; - - @Schema(description = "性别 1男 2女") - @QueryField(type = QueryType.EQ) - private Integer gender; - - @Schema(description = "手机号") - private String mobile; - - @Schema(description = "客户名称") - private String dealerName; - - @Schema(description = "证件号码") - private String idCard; - - @Schema(description = "生日") - private String birthDate; - - @Schema(description = "区分职称等级(如主治医师、副主任医师)") - private String professionalTitle; - - @Schema(description = "工作单位") - private String workUnit; - - @Schema(description = "执业资格核心凭证") - private String practiceLicense; - - @Schema(description = "限定可执业科室或疾病类型") - private String practiceScope; - - @Schema(description = "开始工作时间") - private String startWorkDate; - - @Schema(description = "简历") - private String resume; - - @Schema(description = "使用 JSON 存储多个证件文件路径(如执业证、学历证)") - private String certificationFiles; - - @Schema(description = "详细地址") - private String address; - - @Schema(description = "签约价格") - @QueryField(type = QueryType.EQ) - private BigDecimal money; - - @Schema(description = "推荐人用户ID") - @QueryField(type = QueryType.EQ) - private Integer refereeId; - - @Schema(description = "申请方式(10需后台审核 20无需审核)") - @QueryField(type = QueryType.EQ) - private Integer applyType; - - @Schema(description = "审核状态 (10待审核 20审核通过 30驳回)") - @QueryField(type = QueryType.EQ) - private Integer applyStatus; - - @Schema(description = "申请时间") - private String applyTime; - - @Schema(description = "审核时间") - private String auditTime; - - @Schema(description = "合同时间") - private String contractTime; - - @Schema(description = "过期时间") - private String expirationTime; - - @Schema(description = "驳回原因") - private String rejectReason; - - @Schema(description = "备注") - private String comments; - -} diff --git a/src/main/java/com/gxwebsoft/clinic/param/ClinicDoctorUserParam.java b/src/main/java/com/gxwebsoft/clinic/param/ClinicDoctorUserParam.java deleted file mode 100644 index 5157044..0000000 --- a/src/main/java/com/gxwebsoft/clinic/param/ClinicDoctorUserParam.java +++ /dev/null @@ -1,86 +0,0 @@ -package com.gxwebsoft.clinic.param; - -import com.fasterxml.jackson.annotation.JsonInclude; -import com.gxwebsoft.common.core.annotation.QueryField; -import com.gxwebsoft.common.core.annotation.QueryType; -import com.gxwebsoft.common.core.web.BaseParam; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; -import lombok.EqualsAndHashCode; - -import java.math.BigDecimal; - -/** - * 分销商用户记录表查询参数 - * - * @author 科技小王子 - * @since 2025-10-23 15:58:20 - */ -@Data -@EqualsAndHashCode(callSuper = false) -@JsonInclude(JsonInclude.Include.NON_NULL) -@Schema(name = "ClinicDoctorUserParam对象", description = "分销商用户记录表查询参数") -public class ClinicDoctorUserParam extends BaseParam { - private static final long serialVersionUID = 1L; - - @Schema(description = "主键ID") - @QueryField(type = QueryType.EQ) - private Integer id; - - @Schema(description = "类型 0经销商 1企业 2集团") - @QueryField(type = QueryType.EQ) - private Integer type; - - @Schema(description = "自增ID") - @QueryField(type = QueryType.EQ) - private Integer userId; - - @Schema(description = "姓名") - private String realName; - - @Schema(description = "手机号") - private String phone; - - @Schema(description = "部门") - @QueryField(type = QueryType.EQ) - private Integer departmentId; - - @Schema(description = "专业领域") - private String specialty; - - @Schema(description = "职务级别") - private String position; - - @Schema(description = "执业资格") - private String qualification; - - @Schema(description = "医生简介") - private String introduction; - - @Schema(description = "挂号费") - @QueryField(type = QueryType.EQ) - private BigDecimal consultationFee; - - @Schema(description = "工作年限") - @QueryField(type = QueryType.EQ) - private Integer workYears; - - @Schema(description = "问诊人数") - @QueryField(type = QueryType.EQ) - private Integer consultationCount; - - @Schema(description = "专属二维码") - private String qrcode; - - @Schema(description = "备注") - private String comments; - - @Schema(description = "排序号") - @QueryField(type = QueryType.EQ) - private Integer sortNumber; - - @Schema(description = "是否删除") - @QueryField(type = QueryType.EQ) - private Integer isDelete; - -} diff --git a/src/main/java/com/gxwebsoft/clinic/param/ClinicMedicineInoutParam.java b/src/main/java/com/gxwebsoft/clinic/param/ClinicMedicineInoutParam.java deleted file mode 100644 index a438209..0000000 --- a/src/main/java/com/gxwebsoft/clinic/param/ClinicMedicineInoutParam.java +++ /dev/null @@ -1,102 +0,0 @@ -package com.gxwebsoft.clinic.param; - -import com.fasterxml.jackson.annotation.JsonInclude; -import com.gxwebsoft.common.core.annotation.QueryField; -import com.gxwebsoft.common.core.annotation.QueryType; -import com.gxwebsoft.common.core.web.BaseParam; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; -import lombok.EqualsAndHashCode; - -import java.math.BigDecimal; - -/** - * 出入库查询参数 - * - * @author 科技小王子 - * @since 2025-10-22 02:06:32 - */ -@Data -@EqualsAndHashCode(callSuper = false) -@JsonInclude(JsonInclude.Include.NON_NULL) -@Schema(name = "ClinicMedicineInoutParam对象", description = "出入库查询参数") -public class ClinicMedicineInoutParam extends BaseParam { - private static final long serialVersionUID = 1L; - - @Schema(description = "主键ID") - @QueryField(type = QueryType.EQ) - private Integer id; - - @Schema(description = "买家用户ID") - @QueryField(type = QueryType.EQ) - private Integer userId; - - @Schema(description = "订单编号") - private String orderNo; - - @Schema(description = "分销商用户id(一级)") - @QueryField(type = QueryType.EQ) - private Integer firstUserId; - - @Schema(description = "分销商用户id(二级)") - @QueryField(type = QueryType.EQ) - private Integer secondUserId; - - @Schema(description = "分销商用户id(三级)") - @QueryField(type = QueryType.EQ) - private Integer thirdUserId; - - @Schema(description = "分销佣金(一级)") - @QueryField(type = QueryType.EQ) - private BigDecimal firstMoney; - - @Schema(description = "分销佣金(二级)") - @QueryField(type = QueryType.EQ) - private BigDecimal secondMoney; - - @Schema(description = "分销佣金(三级)") - @QueryField(type = QueryType.EQ) - private BigDecimal thirdMoney; - - @Schema(description = "单价") - @QueryField(type = QueryType.EQ) - private BigDecimal price; - - @Schema(description = "订单总金额") - @QueryField(type = QueryType.EQ) - private BigDecimal orderPrice; - - @Schema(description = "结算金额") - @QueryField(type = QueryType.EQ) - private BigDecimal settledPrice; - - @Schema(description = "换算成度") - @QueryField(type = QueryType.EQ) - private BigDecimal degreePrice; - - @Schema(description = "实发金额") - @QueryField(type = QueryType.EQ) - private BigDecimal payPrice; - - @Schema(description = "税率") - @QueryField(type = QueryType.EQ) - private BigDecimal rate; - - @Schema(description = "结算月份") - private String month; - - @Schema(description = "订单是否失效(0未失效 1已失效)") - @QueryField(type = QueryType.EQ) - private Integer isInvalid; - - @Schema(description = "佣金结算(0未结算 1已结算)") - @QueryField(type = QueryType.EQ) - private Integer isSettled; - - @Schema(description = "结算时间") - private String settleTime; - - @Schema(description = "备注") - private String comments; - -} diff --git a/src/main/java/com/gxwebsoft/clinic/param/ClinicMedicineParam.java b/src/main/java/com/gxwebsoft/clinic/param/ClinicMedicineParam.java deleted file mode 100644 index 71f4744..0000000 --- a/src/main/java/com/gxwebsoft/clinic/param/ClinicMedicineParam.java +++ /dev/null @@ -1,63 +0,0 @@ -package com.gxwebsoft.clinic.param; - -import com.fasterxml.jackson.annotation.JsonInclude; -import com.gxwebsoft.common.core.annotation.QueryField; -import com.gxwebsoft.common.core.annotation.QueryType; -import com.gxwebsoft.common.core.web.BaseParam; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; -import lombok.EqualsAndHashCode; - -import java.math.BigDecimal; - -/** - * 药品库查询参数 - * - * @author 科技小王子 - * @since 2025-10-22 02:06:31 - */ -@Data -@EqualsAndHashCode(callSuper = false) -@JsonInclude(JsonInclude.Include.NON_NULL) -@Schema(name = "ClinicMedicineParam对象", description = "药品库查询参数") -public class ClinicMedicineParam extends BaseParam { - private static final long serialVersionUID = 1L; - - @Schema(description = "主键ID") - @QueryField(type = QueryType.EQ) - private Integer id; - - @Schema(description = "药名") - private String name; - - @Schema(description = "拼音") - private String pinyin; - - @Schema(description = "分类(如“清热解毒”、“补气养血”)") - private String category; - - @Schema(description = "规格(如“饮片”、“颗粒”)") - private String specification; - - @Schema(description = "单位(如“克”、“袋”)") - private String unit; - - @Schema(description = "描述") - private String content; - - @Schema(description = "单价") - @QueryField(type = QueryType.EQ) - private BigDecimal pricePerUnit; - - @Schema(description = "是否活跃") - @QueryField(type = QueryType.EQ) - private Integer isActive; - - @Schema(description = "买家用户ID") - @QueryField(type = QueryType.EQ) - private Integer userId; - - @Schema(description = "备注") - private String comments; - -} diff --git a/src/main/java/com/gxwebsoft/clinic/param/ClinicMedicineStockParam.java b/src/main/java/com/gxwebsoft/clinic/param/ClinicMedicineStockParam.java deleted file mode 100644 index a784c67..0000000 --- a/src/main/java/com/gxwebsoft/clinic/param/ClinicMedicineStockParam.java +++ /dev/null @@ -1,50 +0,0 @@ -package com.gxwebsoft.clinic.param; - -import com.fasterxml.jackson.annotation.JsonInclude; -import com.gxwebsoft.common.core.annotation.QueryField; -import com.gxwebsoft.common.core.annotation.QueryType; -import com.gxwebsoft.common.core.web.BaseParam; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; -import lombok.EqualsAndHashCode; - -/** - * 药品库存查询参数 - * - * @author 科技小王子 - * @since 2025-10-22 02:06:32 - */ -@Data -@EqualsAndHashCode(callSuper = false) -@JsonInclude(JsonInclude.Include.NON_NULL) -@Schema(name = "ClinicMedicineStockParam对象", description = "药品库存查询参数") -public class ClinicMedicineStockParam extends BaseParam { - private static final long serialVersionUID = 1L; - - @Schema(description = "主键ID") - @QueryField(type = QueryType.EQ) - private Integer id; - - @Schema(description = "药品") - @QueryField(type = QueryType.EQ) - private Integer medicineId; - - @Schema(description = "库存数量") - @QueryField(type = QueryType.EQ) - private Integer stockQuantity; - - @Schema(description = "最小库存预警") - @QueryField(type = QueryType.EQ) - private Integer minStockLevel; - - @Schema(description = "上次更新时间") - private String lastUpdated; - - @Schema(description = "买家用户ID") - @QueryField(type = QueryType.EQ) - private Integer userId; - - @Schema(description = "备注") - private String comments; - -} diff --git a/src/main/java/com/gxwebsoft/clinic/param/ClinicPatientUserParam.java b/src/main/java/com/gxwebsoft/clinic/param/ClinicPatientUserParam.java deleted file mode 100644 index 79373df..0000000 --- a/src/main/java/com/gxwebsoft/clinic/param/ClinicPatientUserParam.java +++ /dev/null @@ -1,66 +0,0 @@ -package com.gxwebsoft.clinic.param; - -import java.math.BigDecimal; -import com.gxwebsoft.common.core.annotation.QueryField; -import com.gxwebsoft.common.core.annotation.QueryType; -import com.gxwebsoft.common.core.web.BaseParam; -import com.fasterxml.jackson.annotation.JsonInclude; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; -import lombok.EqualsAndHashCode; - -/** - * 患者查询参数 - * - * @author 科技小王子 - * @since 2025-10-23 15:27:17 - */ -@Data -@EqualsAndHashCode(callSuper = false) -@JsonInclude(JsonInclude.Include.NON_NULL) -@Schema(name = "ClinicPatientUserParam对象", description = "患者查询参数") -public class ClinicPatientUserParam extends BaseParam { - private static final long serialVersionUID = 1L; - - @Schema(description = "主键ID") - @QueryField(type = QueryType.EQ) - private Integer id; - - @Schema(description = "类型 0经销商 1企业 2集团") - @QueryField(type = QueryType.EQ) - private Integer type; - - @Schema(description = "自增ID") - @QueryField(type = QueryType.EQ) - private Integer userId; - - @Schema(description = "姓名") - private String realName; - - @Schema(description = "年龄") - private String age; - - @Schema(description = "专属二维码") - private String qrcode; - - @Schema(description = "身高") - private String height; - - @Schema(description = "体重") - private String weight; - - @Schema(description = "过敏史") - private String allergyHistory; - - @Schema(description = "备注") - private String comments; - - @Schema(description = "排序号") - @QueryField(type = QueryType.EQ) - private Integer sortNumber; - - @Schema(description = "是否删除") - @QueryField(type = QueryType.EQ) - private Integer isDelete; - -} diff --git a/src/main/java/com/gxwebsoft/clinic/param/ClinicPrescriptionItemParam.java b/src/main/java/com/gxwebsoft/clinic/param/ClinicPrescriptionItemParam.java deleted file mode 100644 index 5f83762..0000000 --- a/src/main/java/com/gxwebsoft/clinic/param/ClinicPrescriptionItemParam.java +++ /dev/null @@ -1,81 +0,0 @@ -package com.gxwebsoft.clinic.param; - -import com.baomidou.mybatisplus.annotation.TableField; -import com.fasterxml.jackson.annotation.JsonInclude; -import com.gxwebsoft.common.core.annotation.QueryField; -import com.gxwebsoft.common.core.annotation.QueryType; -import com.gxwebsoft.common.core.web.BaseParam; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; -import lombok.EqualsAndHashCode; - -import java.math.BigDecimal; -import java.util.Set; - -/** - * 处方明细表 -查询参数 - * - * @author 科技小王子 - * @since 2025-10-22 02:01:13 - */ -@Data -@EqualsAndHashCode(callSuper = false) -@JsonInclude(JsonInclude.Include.NON_NULL) -@Schema(name = "ClinicPrescriptionItemParam对象", description = "处方明细表 查询参数") -public class ClinicPrescriptionItemParam extends BaseParam { - private static final long serialVersionUID = 1L; - - @Schema(description = "自增ID") - @QueryField(type = QueryType.EQ) - private Integer id; - - @Schema(description = "关联处方") - @QueryField(type = QueryType.EQ) - private Integer prescriptionId; - - @Schema(description = "订单编号") - private String prescriptionNo; - - @Schema(description = "关联药品") - @QueryField(type = QueryType.EQ) - private Integer medicineId; - - @Schema(description = "剂量(如“10g”)") - private String dosage; - - @Schema(description = "用法频率(如“每日三次”)") - private String usageFrequency; - - @Schema(description = "服用天数") - @QueryField(type = QueryType.EQ) - private Integer days; - - @Schema(description = "购买数量") - @QueryField(type = QueryType.EQ) - private Integer amount; - - @Schema(description = "单价") - @QueryField(type = QueryType.EQ) - private BigDecimal unitPrice; - - @Schema(description = "数量") - @QueryField(type = QueryType.EQ) - private Integer quantity; - - @Schema(description = "排序号") - @QueryField(type = QueryType.EQ) - private Integer sortNumber; - - @Schema(description = "备注") - private String comments; - - @Schema(description = "用户id") - @QueryField(type = QueryType.EQ) - private Integer userId; - - @Schema(description = "处方ID集查询") - @TableField(exist = false) - private Set prescriptionIds; - -} diff --git a/src/main/java/com/gxwebsoft/clinic/param/ClinicPrescriptionParam.java b/src/main/java/com/gxwebsoft/clinic/param/ClinicPrescriptionParam.java deleted file mode 100644 index e8c0a81..0000000 --- a/src/main/java/com/gxwebsoft/clinic/param/ClinicPrescriptionParam.java +++ /dev/null @@ -1,101 +0,0 @@ -package com.gxwebsoft.clinic.param; - -import com.baomidou.mybatisplus.annotation.TableField; -import com.fasterxml.jackson.annotation.JsonInclude; -import com.gxwebsoft.common.core.annotation.QueryField; -import com.gxwebsoft.common.core.annotation.QueryType; -import com.gxwebsoft.common.core.web.BaseParam; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; -import lombok.EqualsAndHashCode; - -import java.math.BigDecimal; -import java.util.Set; - -/** - * 处方主表 -查询参数 - * - * @author 科技小王子 - * @since 2025-10-22 02:01:12 - */ -@Data -@EqualsAndHashCode(callSuper = false) -@JsonInclude(JsonInclude.Include.NON_NULL) -@Schema(name = "ClinicPrescriptionParam对象", description = "处方主表查询参数") -public class ClinicPrescriptionParam extends BaseParam { - private static final long serialVersionUID = 1L; - - @Schema(description = "主键ID") - @QueryField(type = QueryType.EQ) - private Integer id; - - @Schema(description = "患者") - @QueryField(type = QueryType.EQ) - private Integer userId; - - @Schema(description = "医生") - @QueryField(type = QueryType.EQ) - private Integer doctorId; - - @Schema(description = "订单编号") - private String orderNo; - - @Schema(description = "订单类型 0商城订单 1处方订单") - private Integer type; - - @Schema(description = "关联就诊表") - @QueryField(type = QueryType.EQ) - private Integer visitRecordId; - - @Schema(description = "处方类型 0中药 1西药") - @QueryField(type = QueryType.EQ) - private Integer prescriptionType; - - @Schema(description = "诊断结果") - private String diagnosis; - - @Schema(description = "治疗方案") - private String treatmentPlan; - - @Schema(description = "煎药说明") - private String decoctionInstructions; - - @Schema(description = "订单总金额") - @QueryField(type = QueryType.EQ) - private BigDecimal orderPrice; - - @Schema(description = "单价") - @QueryField(type = QueryType.EQ) - private BigDecimal price; - - @Schema(description = "实付金额") - @QueryField(type = QueryType.EQ) - private BigDecimal payPrice; - - @Schema(description = "订单是否失效(0未失效 1已失效)") - @QueryField(type = QueryType.EQ) - private Integer isInvalid; - - @Schema(description = "结算(0未结算 1已结算)") - @QueryField(type = QueryType.EQ) - private Integer isSettled; - - @Schema(description = "结算时间") - private String settleTime; - - @Schema(description = "状态, 0正常, 1已完成,2已支付,3已取消") - @QueryField(type = QueryType.EQ) - private Integer status; - - @Schema(description = "备注") - private String comments; - - @Schema(description = "处方ID集查询") - @TableField(exist = false) - private Set ids; - - @Schema(description = "订单状态筛选:-1全部,0待支付,1待发货,2待核销,3待收货,4待评价,5已完成,6已退款,7已删除") - private Integer statusFilter; - -} diff --git a/src/main/java/com/gxwebsoft/clinic/service/ClinicAppointmentService.java b/src/main/java/com/gxwebsoft/clinic/service/ClinicAppointmentService.java deleted file mode 100644 index 28c7ac1..0000000 --- a/src/main/java/com/gxwebsoft/clinic/service/ClinicAppointmentService.java +++ /dev/null @@ -1,42 +0,0 @@ -package com.gxwebsoft.clinic.service; - -import com.baomidou.mybatisplus.extension.service.IService; -import com.gxwebsoft.common.core.web.PageResult; -import com.gxwebsoft.clinic.entity.ClinicAppointment; -import com.gxwebsoft.clinic.param.ClinicAppointmentParam; - -import java.util.List; - -/** - * 挂号Service - * - * @author 科技小王子 - * @since 2025-10-19 09:27:04 - */ -public interface ClinicAppointmentService extends IService { - - /** - * 分页关联查询 - * - * @param param 查询参数 - * @return PageResult - */ - PageResult pageRel(ClinicAppointmentParam param); - - /** - * 关联查询全部 - * - * @param param 查询参数 - * @return List - */ - List listRel(ClinicAppointmentParam param); - - /** - * 根据id查询 - * - * @param id 主键ID - * @return ClinicAppointment - */ - ClinicAppointment getByIdRel(Integer id); - -} diff --git a/src/main/java/com/gxwebsoft/clinic/service/ClinicDoctorApplyService.java b/src/main/java/com/gxwebsoft/clinic/service/ClinicDoctorApplyService.java deleted file mode 100644 index 228ac95..0000000 --- a/src/main/java/com/gxwebsoft/clinic/service/ClinicDoctorApplyService.java +++ /dev/null @@ -1,42 +0,0 @@ -package com.gxwebsoft.clinic.service; - -import com.baomidou.mybatisplus.extension.service.IService; -import com.gxwebsoft.common.core.web.PageResult; -import com.gxwebsoft.clinic.entity.ClinicDoctorApply; -import com.gxwebsoft.clinic.param.ClinicDoctorApplyParam; - -import java.util.List; - -/** - * 医生入驻申请Service - * - * @author 科技小王子 - * @since 2025-10-19 09:27:04 - */ -public interface ClinicDoctorApplyService extends IService { - - /** - * 分页关联查询 - * - * @param param 查询参数 - * @return PageResult - */ - PageResult pageRel(ClinicDoctorApplyParam param); - - /** - * 关联查询全部 - * - * @param param 查询参数 - * @return List - */ - List listRel(ClinicDoctorApplyParam param); - - /** - * 根据id查询 - * - * @param applyId 主键ID - * @return ClinicDoctorApply - */ - ClinicDoctorApply getByIdRel(Integer applyId); - -} diff --git a/src/main/java/com/gxwebsoft/clinic/service/ClinicDoctorUserService.java b/src/main/java/com/gxwebsoft/clinic/service/ClinicDoctorUserService.java deleted file mode 100644 index a417b1d..0000000 --- a/src/main/java/com/gxwebsoft/clinic/service/ClinicDoctorUserService.java +++ /dev/null @@ -1,42 +0,0 @@ -package com.gxwebsoft.clinic.service; - -import com.baomidou.mybatisplus.extension.service.IService; -import com.gxwebsoft.common.core.web.PageResult; -import com.gxwebsoft.clinic.entity.ClinicDoctorUser; -import com.gxwebsoft.clinic.param.ClinicDoctorUserParam; - -import java.util.List; - -/** - * 分销商用户记录表Service - * - * @author 科技小王子 - * @since 2025-10-19 09:27:04 - */ -public interface ClinicDoctorUserService extends IService { - - /** - * 分页关联查询 - * - * @param param 查询参数 - * @return PageResult - */ - PageResult pageRel(ClinicDoctorUserParam param); - - /** - * 关联查询全部 - * - * @param param 查询参数 - * @return List - */ - List listRel(ClinicDoctorUserParam param); - - /** - * 根据id查询 - * - * @param id 主键ID - * @return ClinicDoctorUser - */ - ClinicDoctorUser getByIdRel(Integer id); - -} diff --git a/src/main/java/com/gxwebsoft/clinic/service/ClinicMedicineInoutService.java b/src/main/java/com/gxwebsoft/clinic/service/ClinicMedicineInoutService.java deleted file mode 100644 index f3c55fa..0000000 --- a/src/main/java/com/gxwebsoft/clinic/service/ClinicMedicineInoutService.java +++ /dev/null @@ -1,42 +0,0 @@ -package com.gxwebsoft.clinic.service; - -import com.baomidou.mybatisplus.extension.service.IService; -import com.gxwebsoft.clinic.entity.ClinicMedicineInout; -import com.gxwebsoft.clinic.param.ClinicMedicineInoutParam; -import com.gxwebsoft.common.core.web.PageResult; - -import java.util.List; - -/** - * 出入库Service - * - * @author 科技小王子 - * @since 2025-10-22 02:06:32 - */ -public interface ClinicMedicineInoutService extends IService { - - /** - * 分页关联查询 - * - * @param param 查询参数 - * @return PageResult - */ - PageResult pageRel(ClinicMedicineInoutParam param); - - /** - * 关联查询全部 - * - * @param param 查询参数 - * @return List - */ - List listRel(ClinicMedicineInoutParam param); - - /** - * 根据id查询 - * - * @param id 主键ID - * @return ClinicMedicineInout - */ - ClinicMedicineInout getByIdRel(Integer id); - -} diff --git a/src/main/java/com/gxwebsoft/clinic/service/ClinicMedicineService.java b/src/main/java/com/gxwebsoft/clinic/service/ClinicMedicineService.java deleted file mode 100644 index e393701..0000000 --- a/src/main/java/com/gxwebsoft/clinic/service/ClinicMedicineService.java +++ /dev/null @@ -1,42 +0,0 @@ -package com.gxwebsoft.clinic.service; - -import com.baomidou.mybatisplus.extension.service.IService; -import com.gxwebsoft.clinic.entity.ClinicMedicine; -import com.gxwebsoft.clinic.param.ClinicMedicineParam; -import com.gxwebsoft.common.core.web.PageResult; - -import java.util.List; - -/** - * 药品库Service - * - * @author 科技小王子 - * @since 2025-10-22 02:06:31 - */ -public interface ClinicMedicineService extends IService { - - /** - * 分页关联查询 - * - * @param param 查询参数 - * @return PageResult - */ - PageResult pageRel(ClinicMedicineParam param); - - /** - * 关联查询全部 - * - * @param param 查询参数 - * @return List - */ - List listRel(ClinicMedicineParam param); - - /** - * 根据id查询 - * - * @param id 主键ID - * @return ClinicMedicine - */ - ClinicMedicine getByIdRel(Integer id); - -} diff --git a/src/main/java/com/gxwebsoft/clinic/service/ClinicMedicineStockService.java b/src/main/java/com/gxwebsoft/clinic/service/ClinicMedicineStockService.java deleted file mode 100644 index 04e792a..0000000 --- a/src/main/java/com/gxwebsoft/clinic/service/ClinicMedicineStockService.java +++ /dev/null @@ -1,42 +0,0 @@ -package com.gxwebsoft.clinic.service; - -import com.baomidou.mybatisplus.extension.service.IService; -import com.gxwebsoft.clinic.entity.ClinicMedicineStock; -import com.gxwebsoft.clinic.param.ClinicMedicineStockParam; -import com.gxwebsoft.common.core.web.PageResult; - -import java.util.List; - -/** - * 药品库存Service - * - * @author 科技小王子 - * @since 2025-10-22 02:06:32 - */ -public interface ClinicMedicineStockService extends IService { - - /** - * 分页关联查询 - * - * @param param 查询参数 - * @return PageResult - */ - PageResult pageRel(ClinicMedicineStockParam param); - - /** - * 关联查询全部 - * - * @param param 查询参数 - * @return List - */ - List listRel(ClinicMedicineStockParam param); - - /** - * 根据id查询 - * - * @param id 主键ID - * @return ClinicMedicineStock - */ - ClinicMedicineStock getByIdRel(Integer id); - -} diff --git a/src/main/java/com/gxwebsoft/clinic/service/ClinicPatientUserService.java b/src/main/java/com/gxwebsoft/clinic/service/ClinicPatientUserService.java deleted file mode 100644 index df37cc3..0000000 --- a/src/main/java/com/gxwebsoft/clinic/service/ClinicPatientUserService.java +++ /dev/null @@ -1,42 +0,0 @@ -package com.gxwebsoft.clinic.service; - -import com.baomidou.mybatisplus.extension.service.IService; -import com.gxwebsoft.common.core.web.PageResult; -import com.gxwebsoft.clinic.entity.ClinicPatientUser; -import com.gxwebsoft.clinic.param.ClinicPatientUserParam; - -import java.util.List; - -/** - * 患者Service - * - * @author 科技小王子 - * @since 2025-10-19 09:27:04 - */ -public interface ClinicPatientUserService extends IService { - - /** - * 分页关联查询 - * - * @param param 查询参数 - * @return PageResult - */ - PageResult pageRel(ClinicPatientUserParam param); - - /** - * 关联查询全部 - * - * @param param 查询参数 - * @return List - */ - List listRel(ClinicPatientUserParam param); - - /** - * 根据id查询 - * - * @param id 主键ID - * @return ClinicPatientUser - */ - ClinicPatientUser getByIdRel(Integer id); - -} diff --git a/src/main/java/com/gxwebsoft/clinic/service/ClinicPrescriptionItemService.java b/src/main/java/com/gxwebsoft/clinic/service/ClinicPrescriptionItemService.java deleted file mode 100644 index 42b8687..0000000 --- a/src/main/java/com/gxwebsoft/clinic/service/ClinicPrescriptionItemService.java +++ /dev/null @@ -1,43 +0,0 @@ -package com.gxwebsoft.clinic.service; - -import com.baomidou.mybatisplus.extension.service.IService; -import com.gxwebsoft.clinic.entity.ClinicPrescriptionItem; -import com.gxwebsoft.clinic.param.ClinicPrescriptionItemParam; -import com.gxwebsoft.common.core.web.PageResult; - -import java.util.List; - -/** - * 处方明细表 -Service - * - * @author 科技小王子 - * @since 2025-10-22 02:01:13 - */ -public interface ClinicPrescriptionItemService extends IService { - - /** - * 分页关联查询 - * - * @param param 查询参数 - * @return PageResult - */ - PageResult pageRel(ClinicPrescriptionItemParam param); - - /** - * 关联查询全部 - * - * @param param 查询参数 - * @return List - */ - List listRel(ClinicPrescriptionItemParam param); - - /** - * 根据id查询 - * - * @param id 自增ID - * @return ClinicPrescriptionItem - */ - ClinicPrescriptionItem getByIdRel(Integer id); - -} diff --git a/src/main/java/com/gxwebsoft/clinic/service/ClinicPrescriptionService.java b/src/main/java/com/gxwebsoft/clinic/service/ClinicPrescriptionService.java deleted file mode 100644 index 5e6d4c0..0000000 --- a/src/main/java/com/gxwebsoft/clinic/service/ClinicPrescriptionService.java +++ /dev/null @@ -1,45 +0,0 @@ -package com.gxwebsoft.clinic.service; - -import com.baomidou.mybatisplus.extension.service.IService; -import com.gxwebsoft.clinic.entity.ClinicPrescription; -import com.gxwebsoft.clinic.param.ClinicPrescriptionParam; -import com.gxwebsoft.common.core.web.PageResult; - -import java.util.List; - -/** - * 处方主表 -Service - * - * @author 科技小王子 - * @since 2025-10-22 02:01:13 - */ -public interface ClinicPrescriptionService extends IService { - - /** - * 分页关联查询 - * - * @param param 查询参数 - * @return PageResult - */ - PageResult pageRel(ClinicPrescriptionParam param); - - /** - * 关联查询全部 - * - * @param param 查询参数 - * @return List - */ - List listRel(ClinicPrescriptionParam param); - - /** - * 根据id查询 - * - * @param id 主键ID - * @return ClinicPrescription - */ - ClinicPrescription getByIdRel(Integer id); - - // 添加成功后返回数据 - ClinicPrescription getByLastId(ClinicPrescription clinicPrescription); -} diff --git a/src/main/java/com/gxwebsoft/clinic/service/impl/ClinicAppointmentServiceImpl.java b/src/main/java/com/gxwebsoft/clinic/service/impl/ClinicAppointmentServiceImpl.java deleted file mode 100644 index 2af3f86..0000000 --- a/src/main/java/com/gxwebsoft/clinic/service/impl/ClinicAppointmentServiceImpl.java +++ /dev/null @@ -1,47 +0,0 @@ -package com.gxwebsoft.clinic.service.impl; - -import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; -import com.gxwebsoft.clinic.mapper.ClinicAppointmentMapper; -import com.gxwebsoft.clinic.service.ClinicAppointmentService; -import com.gxwebsoft.clinic.entity.ClinicAppointment; -import com.gxwebsoft.clinic.param.ClinicAppointmentParam; -import com.gxwebsoft.common.core.web.PageParam; -import com.gxwebsoft.common.core.web.PageResult; -import org.springframework.stereotype.Service; - -import java.util.List; - -/** - * 挂号Service实现 - * - * @author 科技小王子 - * @since 2025-10-19 09:27:04 - */ -@Service -public class ClinicAppointmentServiceImpl extends ServiceImpl implements ClinicAppointmentService { - - @Override - public PageResult pageRel(ClinicAppointmentParam param) { - PageParam page = new PageParam<>(param); - page.setDefaultOrder("sort_number asc, create_time desc"); - List list = baseMapper.selectPageRel(page, param); - return new PageResult<>(list, page.getTotal()); - } - - @Override - public List listRel(ClinicAppointmentParam param) { - List list = baseMapper.selectListRel(param); - // 排序 - PageParam page = new PageParam<>(); - page.setDefaultOrder("sort_number asc, create_time desc"); - return page.sortRecords(list); - } - - @Override - public ClinicAppointment getByIdRel(Integer id) { - ClinicAppointmentParam param = new ClinicAppointmentParam(); - param.setId(id); - return param.getOne(baseMapper.selectListRel(param)); - } - -} diff --git a/src/main/java/com/gxwebsoft/clinic/service/impl/ClinicDoctorApplyServiceImpl.java b/src/main/java/com/gxwebsoft/clinic/service/impl/ClinicDoctorApplyServiceImpl.java deleted file mode 100644 index 9ba52d1..0000000 --- a/src/main/java/com/gxwebsoft/clinic/service/impl/ClinicDoctorApplyServiceImpl.java +++ /dev/null @@ -1,47 +0,0 @@ -package com.gxwebsoft.clinic.service.impl; - -import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; -import com.gxwebsoft.clinic.mapper.ClinicDoctorApplyMapper; -import com.gxwebsoft.clinic.service.ClinicDoctorApplyService; -import com.gxwebsoft.clinic.entity.ClinicDoctorApply; -import com.gxwebsoft.clinic.param.ClinicDoctorApplyParam; -import com.gxwebsoft.common.core.web.PageParam; -import com.gxwebsoft.common.core.web.PageResult; -import org.springframework.stereotype.Service; - -import java.util.List; - -/** - * 医生入驻申请Service实现 - * - * @author 科技小王子 - * @since 2025-10-19 09:27:04 - */ -@Service -public class ClinicDoctorApplyServiceImpl extends ServiceImpl implements ClinicDoctorApplyService { - - @Override - public PageResult pageRel(ClinicDoctorApplyParam param) { - PageParam page = new PageParam<>(param); - page.setDefaultOrder("sort_number asc, create_time desc"); - List list = baseMapper.selectPageRel(page, param); - return new PageResult<>(list, page.getTotal()); - } - - @Override - public List listRel(ClinicDoctorApplyParam param) { - List list = baseMapper.selectListRel(param); - // 排序 - PageParam page = new PageParam<>(); - page.setDefaultOrder("sort_number asc, create_time desc"); - return page.sortRecords(list); - } - - @Override - public ClinicDoctorApply getByIdRel(Integer applyId) { - ClinicDoctorApplyParam param = new ClinicDoctorApplyParam(); - param.setApplyId(applyId); - return param.getOne(baseMapper.selectListRel(param)); - } - -} diff --git a/src/main/java/com/gxwebsoft/clinic/service/impl/ClinicDoctorUserServiceImpl.java b/src/main/java/com/gxwebsoft/clinic/service/impl/ClinicDoctorUserServiceImpl.java deleted file mode 100644 index 5b65655..0000000 --- a/src/main/java/com/gxwebsoft/clinic/service/impl/ClinicDoctorUserServiceImpl.java +++ /dev/null @@ -1,47 +0,0 @@ -package com.gxwebsoft.clinic.service.impl; - -import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; -import com.gxwebsoft.clinic.mapper.ClinicDoctorUserMapper; -import com.gxwebsoft.clinic.service.ClinicDoctorUserService; -import com.gxwebsoft.clinic.entity.ClinicDoctorUser; -import com.gxwebsoft.clinic.param.ClinicDoctorUserParam; -import com.gxwebsoft.common.core.web.PageParam; -import com.gxwebsoft.common.core.web.PageResult; -import org.springframework.stereotype.Service; - -import java.util.List; - -/** - * 分销商用户记录表Service实现 - * - * @author 科技小王子 - * @since 2025-10-19 09:27:04 - */ -@Service -public class ClinicDoctorUserServiceImpl extends ServiceImpl implements ClinicDoctorUserService { - - @Override - public PageResult pageRel(ClinicDoctorUserParam param) { - PageParam page = new PageParam<>(param); - page.setDefaultOrder("sort_number asc, create_time desc"); - List list = baseMapper.selectPageRel(page, param); - return new PageResult<>(list, page.getTotal()); - } - - @Override - public List listRel(ClinicDoctorUserParam param) { - List list = baseMapper.selectListRel(param); - // 排序 - PageParam page = new PageParam<>(); - page.setDefaultOrder("sort_number asc, create_time desc"); - return page.sortRecords(list); - } - - @Override - public ClinicDoctorUser getByIdRel(Integer id) { - ClinicDoctorUserParam param = new ClinicDoctorUserParam(); - param.setUserId(id); - return param.getOne(baseMapper.selectListRel(param)); - } - -} diff --git a/src/main/java/com/gxwebsoft/clinic/service/impl/ClinicMedicineInoutServiceImpl.java b/src/main/java/com/gxwebsoft/clinic/service/impl/ClinicMedicineInoutServiceImpl.java deleted file mode 100644 index fbd1caf..0000000 --- a/src/main/java/com/gxwebsoft/clinic/service/impl/ClinicMedicineInoutServiceImpl.java +++ /dev/null @@ -1,47 +0,0 @@ -package com.gxwebsoft.clinic.service.impl; - -import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; -import com.gxwebsoft.clinic.entity.ClinicMedicineInout; -import com.gxwebsoft.clinic.mapper.ClinicMedicineInoutMapper; -import com.gxwebsoft.clinic.param.ClinicMedicineInoutParam; -import com.gxwebsoft.clinic.service.ClinicMedicineInoutService; -import com.gxwebsoft.common.core.web.PageParam; -import com.gxwebsoft.common.core.web.PageResult; -import org.springframework.stereotype.Service; - -import java.util.List; - -/** - * 出入库Service实现 - * - * @author 科技小王子 - * @since 2025-10-22 02:06:32 - */ -@Service -public class ClinicMedicineInoutServiceImpl extends ServiceImpl implements ClinicMedicineInoutService { - - @Override - public PageResult pageRel(ClinicMedicineInoutParam param) { - PageParam page = new PageParam<>(param); - page.setDefaultOrder("sort_number asc, create_time desc"); - List list = baseMapper.selectPageRel(page, param); - return new PageResult<>(list, page.getTotal()); - } - - @Override - public List listRel(ClinicMedicineInoutParam param) { - List list = baseMapper.selectListRel(param); - // 排序 - PageParam page = new PageParam<>(); - page.setDefaultOrder("sort_number asc, create_time desc"); - return page.sortRecords(list); - } - - @Override - public ClinicMedicineInout getByIdRel(Integer id) { - ClinicMedicineInoutParam param = new ClinicMedicineInoutParam(); - param.setId(id); - return param.getOne(baseMapper.selectListRel(param)); - } - -} diff --git a/src/main/java/com/gxwebsoft/clinic/service/impl/ClinicMedicineServiceImpl.java b/src/main/java/com/gxwebsoft/clinic/service/impl/ClinicMedicineServiceImpl.java deleted file mode 100644 index cc6deba..0000000 --- a/src/main/java/com/gxwebsoft/clinic/service/impl/ClinicMedicineServiceImpl.java +++ /dev/null @@ -1,47 +0,0 @@ -package com.gxwebsoft.clinic.service.impl; - -import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; -import com.gxwebsoft.clinic.entity.ClinicMedicine; -import com.gxwebsoft.clinic.mapper.ClinicMedicineMapper; -import com.gxwebsoft.clinic.param.ClinicMedicineParam; -import com.gxwebsoft.clinic.service.ClinicMedicineService; -import com.gxwebsoft.common.core.web.PageParam; -import com.gxwebsoft.common.core.web.PageResult; -import org.springframework.stereotype.Service; - -import java.util.List; - -/** - * 药品库Service实现 - * - * @author 科技小王子 - * @since 2025-10-22 02:06:31 - */ -@Service -public class ClinicMedicineServiceImpl extends ServiceImpl implements ClinicMedicineService { - - @Override - public PageResult pageRel(ClinicMedicineParam param) { - PageParam page = new PageParam<>(param); - page.setDefaultOrder("sort_number asc, create_time desc"); - List list = baseMapper.selectPageRel(page, param); - return new PageResult<>(list, page.getTotal()); - } - - @Override - public List listRel(ClinicMedicineParam param) { - List list = baseMapper.selectListRel(param); - // 排序 - PageParam page = new PageParam<>(); - page.setDefaultOrder("sort_number asc, create_time desc"); - return page.sortRecords(list); - } - - @Override - public ClinicMedicine getByIdRel(Integer id) { - ClinicMedicineParam param = new ClinicMedicineParam(); - param.setId(id); - return param.getOne(baseMapper.selectListRel(param)); - } - -} diff --git a/src/main/java/com/gxwebsoft/clinic/service/impl/ClinicMedicineStockServiceImpl.java b/src/main/java/com/gxwebsoft/clinic/service/impl/ClinicMedicineStockServiceImpl.java deleted file mode 100644 index 2364578..0000000 --- a/src/main/java/com/gxwebsoft/clinic/service/impl/ClinicMedicineStockServiceImpl.java +++ /dev/null @@ -1,47 +0,0 @@ -package com.gxwebsoft.clinic.service.impl; - -import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; -import com.gxwebsoft.clinic.entity.ClinicMedicineStock; -import com.gxwebsoft.clinic.mapper.ClinicMedicineStockMapper; -import com.gxwebsoft.clinic.param.ClinicMedicineStockParam; -import com.gxwebsoft.clinic.service.ClinicMedicineStockService; -import com.gxwebsoft.common.core.web.PageParam; -import com.gxwebsoft.common.core.web.PageResult; -import org.springframework.stereotype.Service; - -import java.util.List; - -/** - * 药品库存Service实现 - * - * @author 科技小王子 - * @since 2025-10-22 02:06:32 - */ -@Service -public class ClinicMedicineStockServiceImpl extends ServiceImpl implements ClinicMedicineStockService { - - @Override - public PageResult pageRel(ClinicMedicineStockParam param) { - PageParam page = new PageParam<>(param); - page.setDefaultOrder("sort_number asc, create_time desc"); - List list = baseMapper.selectPageRel(page, param); - return new PageResult<>(list, page.getTotal()); - } - - @Override - public List listRel(ClinicMedicineStockParam param) { - List list = baseMapper.selectListRel(param); - // 排序 - PageParam page = new PageParam<>(); - page.setDefaultOrder("sort_number asc, create_time desc"); - return page.sortRecords(list); - } - - @Override - public ClinicMedicineStock getByIdRel(Integer id) { - ClinicMedicineStockParam param = new ClinicMedicineStockParam(); - param.setId(id); - return param.getOne(baseMapper.selectListRel(param)); - } - -} diff --git a/src/main/java/com/gxwebsoft/clinic/service/impl/ClinicPatientUserServiceImpl.java b/src/main/java/com/gxwebsoft/clinic/service/impl/ClinicPatientUserServiceImpl.java deleted file mode 100644 index a3968b2..0000000 --- a/src/main/java/com/gxwebsoft/clinic/service/impl/ClinicPatientUserServiceImpl.java +++ /dev/null @@ -1,47 +0,0 @@ -package com.gxwebsoft.clinic.service.impl; - -import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; -import com.gxwebsoft.clinic.mapper.ClinicPatientUserMapper; -import com.gxwebsoft.clinic.service.ClinicPatientUserService; -import com.gxwebsoft.clinic.entity.ClinicPatientUser; -import com.gxwebsoft.clinic.param.ClinicPatientUserParam; -import com.gxwebsoft.common.core.web.PageParam; -import com.gxwebsoft.common.core.web.PageResult; -import org.springframework.stereotype.Service; - -import java.util.List; - -/** - * 患者Service实现 - * - * @author 科技小王子 - * @since 2025-10-19 09:27:04 - */ -@Service -public class ClinicPatientUserServiceImpl extends ServiceImpl implements ClinicPatientUserService { - - @Override - public PageResult pageRel(ClinicPatientUserParam param) { - PageParam page = new PageParam<>(param); - page.setDefaultOrder("sort_number asc, create_time desc"); - List list = baseMapper.selectPageRel(page, param); - return new PageResult<>(list, page.getTotal()); - } - - @Override - public List listRel(ClinicPatientUserParam param) { - List list = baseMapper.selectListRel(param); - // 排序 - PageParam page = new PageParam<>(); - page.setDefaultOrder("sort_number asc, create_time desc"); - return page.sortRecords(list); - } - - @Override - public ClinicPatientUser getByIdRel(Integer id) { - ClinicPatientUserParam param = new ClinicPatientUserParam(); - param.setUserId(id); - return param.getOne(baseMapper.selectListRel(param)); - } - -} diff --git a/src/main/java/com/gxwebsoft/clinic/service/impl/ClinicPrescriptionItemServiceImpl.java b/src/main/java/com/gxwebsoft/clinic/service/impl/ClinicPrescriptionItemServiceImpl.java deleted file mode 100644 index 916b716..0000000 --- a/src/main/java/com/gxwebsoft/clinic/service/impl/ClinicPrescriptionItemServiceImpl.java +++ /dev/null @@ -1,48 +0,0 @@ -package com.gxwebsoft.clinic.service.impl; - -import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; -import com.gxwebsoft.clinic.entity.ClinicPrescriptionItem; -import com.gxwebsoft.clinic.mapper.ClinicPrescriptionItemMapper; -import com.gxwebsoft.clinic.param.ClinicPrescriptionItemParam; -import com.gxwebsoft.clinic.service.ClinicPrescriptionItemService; -import com.gxwebsoft.common.core.web.PageParam; -import com.gxwebsoft.common.core.web.PageResult; -import org.springframework.stereotype.Service; - -import java.util.List; - -/** - * 处方明细表 -Service实现 - * - * @author 科技小王子 - * @since 2025-10-22 02:01:13 - */ -@Service -public class ClinicPrescriptionItemServiceImpl extends ServiceImpl implements ClinicPrescriptionItemService { - - @Override - public PageResult pageRel(ClinicPrescriptionItemParam param) { - PageParam page = new PageParam<>(param); - page.setDefaultOrder("sort_number asc, create_time desc"); - List list = baseMapper.selectPageRel(page, param); - return new PageResult<>(list, page.getTotal()); - } - - @Override - public List listRel(ClinicPrescriptionItemParam param) { - List list = baseMapper.selectListRel(param); - // 排序 - PageParam page = new PageParam<>(); - page.setDefaultOrder("sort_number asc, create_time desc"); - return page.sortRecords(list); - } - - @Override - public ClinicPrescriptionItem getByIdRel(Integer id) { - ClinicPrescriptionItemParam param = new ClinicPrescriptionItemParam(); - param.setId(id); - return param.getOne(baseMapper.selectListRel(param)); - } - -} diff --git a/src/main/java/com/gxwebsoft/clinic/service/impl/ClinicPrescriptionServiceImpl.java b/src/main/java/com/gxwebsoft/clinic/service/impl/ClinicPrescriptionServiceImpl.java deleted file mode 100644 index 742f741..0000000 --- a/src/main/java/com/gxwebsoft/clinic/service/impl/ClinicPrescriptionServiceImpl.java +++ /dev/null @@ -1,75 +0,0 @@ -package com.gxwebsoft.clinic.service.impl; - -import cn.hutool.core.util.StrUtil; -import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; -import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; -import com.gxwebsoft.clinic.entity.ClinicPrescription; -import com.gxwebsoft.clinic.entity.ClinicPrescriptionItem; -import com.gxwebsoft.clinic.mapper.ClinicPrescriptionMapper; -import com.gxwebsoft.clinic.param.ClinicPrescriptionItemParam; -import com.gxwebsoft.clinic.param.ClinicPrescriptionParam; -import com.gxwebsoft.clinic.service.ClinicPrescriptionItemService; -import com.gxwebsoft.clinic.service.ClinicPrescriptionService; -import com.gxwebsoft.common.core.web.PageParam; -import com.gxwebsoft.common.core.web.PageResult; -import com.gxwebsoft.shop.entity.ShopOrder; -import com.gxwebsoft.shop.entity.ShopOrderGoods; -import org.springframework.stereotype.Service; - -import javax.annotation.Resource; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.stream.Collectors; - -/** - * 处方主表 -Service实现 - * - * @author 科技小王子 - * @since 2025-10-22 02:01:13 - */ -@Service -public class ClinicPrescriptionServiceImpl extends ServiceImpl implements ClinicPrescriptionService { - - @Resource - private ClinicPrescriptionItemService clinicPrescriptionItemService; - @Override - public PageResult pageRel(ClinicPrescriptionParam param) { - PageParam page = new PageParam<>(param); - page.setDefaultOrder("sort_number asc, create_time desc"); - List list = baseMapper.selectPageRel(page, param); - // 查询处方明细 - Set collectIds = list.stream().map(ClinicPrescription::getId).collect(Collectors.toSet()); - final ClinicPrescriptionItemParam itemParam = new ClinicPrescriptionItemParam(); - itemParam.setPrescriptionIds(collectIds); - final List clinicPrescriptionItems = clinicPrescriptionItemService.listRel(itemParam); - final Map> collect = clinicPrescriptionItems.stream().collect(Collectors.groupingBy(ClinicPrescriptionItem::getPrescriptionId)); - list.forEach(d -> { - d.setItems(collect.get(d.getId())); - }); - return new PageResult<>(list, page.getTotal()); - } - - @Override - public List listRel(ClinicPrescriptionParam param) { - List list = baseMapper.selectListRel(param); - // 排序 - PageParam page = new PageParam<>(); - page.setDefaultOrder("sort_number asc, create_time desc"); - return page.sortRecords(list); - } - - @Override - public ClinicPrescription getByIdRel(Integer id) { - ClinicPrescriptionParam param = new ClinicPrescriptionParam(); - param.setId(id); - return param.getOne(baseMapper.selectListRel(param)); - } - - @Override - public ClinicPrescription getByLastId(ClinicPrescription clinicPrescription) { - return getOne(new LambdaQueryWrapper().orderByDesc(ClinicPrescription::getId).eq(ClinicPrescription::getUserId, clinicPrescription.getUserId()).last("limit 1")); - } - -} diff --git a/src/main/java/com/gxwebsoft/cms/controller/CmsAppController.java b/src/main/java/com/gxwebsoft/cms/controller/CmsAppController.java new file mode 100644 index 0000000..5aeb8f0 --- /dev/null +++ b/src/main/java/com/gxwebsoft/cms/controller/CmsAppController.java @@ -0,0 +1,646 @@ +package com.gxwebsoft.cms.controller; + +import cn.hutool.core.date.DateTime; +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.gxwebsoft.cms.entity.*; +import com.gxwebsoft.cms.param.CmsNavigationParam; +import com.gxwebsoft.cms.service.CmsNavigationService; +import com.gxwebsoft.cms.service.CmsAppFieldService; +import com.gxwebsoft.cms.service.CmsAppSettingService; +import com.gxwebsoft.common.core.utils.CommonUtil; +import com.gxwebsoft.common.core.utils.RedisUtil; +import com.gxwebsoft.common.core.web.BaseController; +import com.gxwebsoft.cms.service.CmsAppService; +import com.gxwebsoft.cms.param.CmsAppParam; +import com.gxwebsoft.common.core.web.ApiResult; +import com.gxwebsoft.common.core.web.PageResult; +import com.gxwebsoft.common.core.web.BatchParam; +import com.gxwebsoft.common.system.entity.User; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.extern.slf4j.Slf4j; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.*; + +import javax.annotation.Resource; +import java.util.*; + +import java.util.concurrent.TimeUnit; + +/** + * 网站信息记录表控制器 + * + * @author 科技小王子 + * @since 2024-09-10 20:36:14 + */ +@Slf4j +@Tag(name = "网站信息记录表管理") +@RestController +@RequestMapping("/api/cms/cms-app") +public class CmsAppController extends BaseController { + @Resource + private CmsAppService cmsAppService; + @Resource + private RedisUtil redisUtil; + @Resource + private CmsAppFieldService cmsAppFieldService; + @Resource + private CmsNavigationService cmsNavigationService; + @Resource + private CmsAppSettingService cmsAppSettingService; + + private static final String SITE_INFO_KEY_PREFIX = "SiteInfo:"; + private static final String MP_INFO_KEY_PREFIX = "MpInfo:"; + private static final String SELECT_PAYMENT_KEY_PREFIX = "SelectPayment:"; + private static final String SYS_DOMAIN_SUFFIX = ".websoft.top"; + private static final String DOMAIN_SUFFIX = ".wsdns.cn"; + + @Operation(summary = "分页查询网站信息记录表") + @GetMapping("/page") + public ApiResult> page(CmsAppParam param) { + // 使用关联查询 + return success(cmsAppService.pageRel(param)); + } + + @Operation(summary = "查询全部网站信息记录表") + @GetMapping() + public ApiResult> list(CmsAppParam param) { + // 使用关联查询 + return success(cmsAppService.listRel(param)); + } + + @Operation(summary = "分页查询网站信息记录表") + @GetMapping("/pageAll") + public ApiResult> pageAll(CmsAppParam param) { + return success(cmsAppService.pageRelAll(param)); + } + + @Operation(summary = "根据id查询网站信息记录表") + @GetMapping("/{id}") + public ApiResult get(@PathVariable("id") Integer id) { + // 使用关联查询 + return success(cmsAppService.getByIdRel(id)); + } + + @Operation(summary = "根据id查询网站信息记录表") + @GetMapping("/getAll/{id}") + public ApiResult getAll(@PathVariable("id") Integer id) { + // 使用关联查询 + return success(cmsAppService.getByIdRelAll(id)); + } + + @PreAuthorize("hasAuthority('cms:website:save')") + @Operation(summary = "添加网站信息记录表") + @PostMapping() + public ApiResult save(@RequestBody CmsApp cmsApp) { + // 前端若指定了 appCode,先做唯一性校验 + if (StrUtil.isNotBlank(cmsApp.getAppCode())) { + long cnt = cmsAppService.count( + new LambdaQueryWrapper() + .eq(CmsApp::getAppCode, cmsApp.getAppCode()) + ); + if (cnt > 0) { + return fail("应用标识 [" + cmsApp.getAppCode() + "] 已存在,请更换"); + } + } + // 记录当前登录用户id + User loginUser = getLoginUser(); + if (loginUser != null) { + cmsApp.setLoginUser(loginUser); + return success("创建成功", cmsAppService.create(cmsApp)); + } + return fail("创建失败"); + } + + @PreAuthorize("hasAuthority('cms:website:update')") + @Operation(summary = "修改网站信息记录表") + @PutMapping() + public ApiResult update(@RequestBody CmsApp cmsApp) { + // appCode 全局唯一,有值时校验是否与其他记录冲突(排除自身) + if (StrUtil.isNotBlank(cmsApp.getAppCode())) { + long cnt = cmsAppService.count( + new LambdaQueryWrapper() + .eq(CmsApp::getAppCode, cmsApp.getAppCode()) + .ne(CmsApp::getAppId, cmsApp.getAppId()) + ); + if (cnt > 0) { + return fail("应用标识 [" + cmsApp.getAppCode() + "] 已存在,请更换"); + } + } else { + // 不允许通过此接口清空或修改 appCode(设为 null 则 MP 不更新该字段) + cmsApp.setAppCode(null); + } + if (cmsAppService.updateById(cmsApp)) { + return success("修改成功"); + } + return fail("修改失败"); + } + + @PreAuthorize("hasAuthority('cms:website:update')") + @Operation(summary = "修改网站信息记录表") + @PutMapping("/updateAll") + public ApiResult updateAll(@RequestBody CmsApp cmsApp) { + if (cmsAppService.updateByIdAll(cmsApp)) { + return success("修改成功"); + } + return fail("修改失败"); + } + + @PreAuthorize("hasAuthority('cms:website:remove')") + @Operation(summary = "删除网站信息记录表") + @DeleteMapping("/{id}") + public ApiResult remove(@PathVariable("id") Integer id) { + if (cmsAppService.removeById(id)) { + return success("删除成功"); + } + return fail("删除失败"); + } + + @PreAuthorize("hasAuthority('cms:website:remove')") + @Operation(summary = "删除网站信息记录表") + @DeleteMapping("/removeAll/{id}") + public ApiResult removeAll(@PathVariable("id") Integer id) { + if (cmsAppService.removeByIdAll(id)) { + return success("删除成功"); + } + return fail("删除失败"); + } + + @PreAuthorize("hasAuthority('cms:website:save')") + @Operation(summary = "批量添加网站信息记录表") + @PostMapping("/batch") + public ApiResult saveBatch(@RequestBody List list) { + if (cmsAppService.saveBatch(list)) { + return success("添加成功"); + } + return fail("添加失败"); + } + + @PreAuthorize("hasAuthority('cms:website:update')") + @Operation(summary = "批量修改网站信息记录表") + @PutMapping("/batch") + public ApiResult removeBatch(@RequestBody BatchParam batchParam) { + if (batchParam.update(cmsAppService, "app_id")) { + return success("修改成功"); + } + return fail("修改失败"); + } + + @PreAuthorize("hasAuthority('cms:website:remove')") + @Operation(summary = "批量删除网站信息记录表") + @DeleteMapping("/batch") + public ApiResult removeBatch(@RequestBody List ids) { + if (cmsAppService.removeByIds(ids)) { + return success("删除成功"); + } + return fail("删除失败"); + } + + @Operation(summary = "网站基本信息") + @GetMapping("/getSiteInfo") + public ApiResult getSiteInfo() { + log.info("开始获取网站基本信息..."); + try { + Integer tenantId = getTenantId(); + log.info("获取到租户ID: {}", tenantId); + if (ObjectUtil.isEmpty(tenantId)) { + log.warn("租户ID为空"); + return fail("租户ID不能为空", null); + } + + String key = SITE_INFO_KEY_PREFIX + tenantId; + + // 尝试从缓存获取 + try { + final String siteInfo = redisUtil.get(key); + if (StrUtil.isNotBlank(siteInfo)) { + log.info("从缓存获取网站信息: = {}", key); + // 可以启用缓存返回,但先注释掉确保数据最新 + // return success(JSONUtil.parseObject(siteInfo, CmsApp.class)); + } + } catch (Exception e) { + log.warn("获取缓存失败: {}", e.getMessage()); + } + + // 获取站点信息 + CmsApp website = null; + try { + log.info("开始查询租户{}的站点信息", tenantId); + website = cmsAppService.getOne(new LambdaQueryWrapper() + .eq(CmsApp::getTenantId, tenantId) + .eq(CmsApp::getDeleted, 0) + .last("limit 1")); + log.info("查询站点信息完成, website: {}", website != null ? "存在" : "不存在"); + } catch (Exception e) { + log.error("查询站点信息失败: {}", e.getMessage(), e); + return fail("查询站点信息失败: " + e.getMessage(), null); + } + + // 创建默认站点 + if (ObjectUtil.isEmpty(website)) { + return success("请先创建站点...", null); + } + + // 安全地构建网站信息 + try { + buildSafeWebsiteInfo(website); + } catch (Exception e) { + log.error("构建网站信息失败: {}", e.getMessage(), e); + return fail("构建网站信息失败", null); + } + + // 缓存结果 + try { + redisUtil.set(key, website, 1L, TimeUnit.DAYS); + } catch (Exception e) { + log.warn("缓存网站信息失败: {}", e.getMessage()); + } + + return success(website); + + } catch (Exception e) { + log.error("获取网站信息异常: {}", e.getMessage(), e); + return fail("获取网站信息失败: " + e.getMessage(), null); + } + } + + /** + * 安全地构建网站信息 + */ + private void buildSafeWebsiteInfo(CmsApp website) { + if (website == null) { + throw new IllegalArgumentException("网站对象不能为空"); + } + + // 1. 设置网站状态 + try { + setWebsiteStatus(website); + } catch (Exception e) { + log.warn("设置网站状态失败: {}", e.getMessage()); + website.setStatus(0); // 默认状态 + website.setStatusText("状态未知"); + } + + // 2. 构建配置信息 + try { + HashMap config = buildSafeWebsiteConfig(website); + website.setConfig(config); + } catch (Exception e) { + log.warn("构建网站配置失败: {}", e.getMessage()); + website.setConfig(new HashMap<>()); + } + + // 3. 设置导航信息 + try { + setSafeWebsiteNavigation(website); + } catch (Exception e) { + log.warn("设置网站导航失败: {}", e.getMessage()); + website.setTopNavs(new ArrayList<>()); + website.setBottomNavs(new ArrayList<>()); + } + + // 4. 设置网站设置信息 + try { + setWebsiteSetting(website); + } catch (Exception e) { + log.warn("设置网站设置失败: {}", e.getMessage()); + // 设置为null,让前端知道没有设置信息 + } + + // 5. 构建服务器时间(使用LocalDateTime) + try { + HashMap serverTime = buildSafeServerTime(); + website.setServerTime(serverTime); + } catch (Exception e) { + log.warn("构建服务器时间失败: {}", e.getMessage()); + HashMap defaultTime = new HashMap<>(); + defaultTime.put("now", java.time.LocalDateTime.now().toString()); + website.setServerTime(defaultTime); + } + } + + private void setWebsiteStatus(CmsApp website) { + // 空值检查,避免NullPointerException + Integer running = website.getRunning(); + if (running == null) { + // 默认状态:未开通 + website.setStatusIcon("error"); + website.setStatusText("状态未知"); + return; + } + + if (!running.equals(1)) { + // 未开通 + if (running.equals(0)) { + website.setStatusIcon("error"); + website.setStatusText("该站点未开通"); + } + // 维护中 + if (running.equals(2)) { + website.setStatusIcon("warning"); + } + // 已关闭 + if (running.equals(3)) { + website.setStatusIcon("error"); + website.setStatusText("已关闭"); + } + // 已欠费停机 + if (running.equals(4)) { + website.setStatusIcon("error"); + website.setStatusText("已欠费停机"); + } + // 违规关停 + if (running.equals(5)) { + website.setStatusIcon("error"); + website.setStatusText("违规关停"); + } + } + } + + private HashMap buildWebsiteConfig(CmsApp website) { + return buildSafeWebsiteConfig(website); + } + + private HashMap buildSafeWebsiteConfig(CmsApp website) { + HashMap config = new HashMap<>(); + + try { + // 获取网站字段配置 + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.eq(CmsAppField::getDeleted, 0); + final List fields = cmsAppFieldService.list(wrapper); + + if (fields != null && !fields.isEmpty()) { + fields.forEach(field -> { + if (field != null && StrUtil.isNotBlank(field.getName())) { + config.put(field.getName(), field.getValue() != null ? field.getValue() : ""); + } + }); + } + } catch (Exception e) { + log.warn("获取网站字段配置失败: {}", e.getMessage()); + } + + // 安全地设置域名信息 + try { + config.put("SysDomain", getSafeSysDomain(website)); + config.put("Domain", getSafeDomain(website)); + } catch (Exception e) { + log.warn("设置域名信息失败: {}", e.getMessage()); + config.put("SysDomain", website.getTenantId() + ".websoft.top"); + config.put("Domain", website.getTenantId() + ".wsdns.cn"); + } + + return config; + } + + private String getSysDomain(CmsApp website) { + return getSafeSysDomain(website); + } + + private String getDomain(CmsApp website) { + return getSafeDomain(website); + } + + private String getSafeSysDomain(CmsApp website) { + if (website == null || website.getTenantId() == null) { + return "unknown.websoft.top"; + } + return StrUtil.isNotBlank(website.getAppCode()) ? + website.getAppCode() + SYS_DOMAIN_SUFFIX : + website.getTenantId() + SYS_DOMAIN_SUFFIX; + } + + private String getSafeDomain(CmsApp website) { + if (website == null || website.getTenantId() == null) { + return "unknown.wsdns.cn"; + } + + if (StrUtil.isNotBlank(website.getDomain())) { + return website.getDomain(); + } + if (StrUtil.isNotBlank(website.getAppCode())) { + return website.getAppCode() + DOMAIN_SUFFIX; + } + return website.getTenantId() + DOMAIN_SUFFIX; + } + + private void setWebsiteNavigation(CmsApp website) { + setSafeWebsiteNavigation(website); + } + + private void setSafeWebsiteNavigation(CmsApp website) { + if (website == null) { + return; + } + + // 设置顶部导航 + try { + final CmsNavigationParam topParam = new CmsNavigationParam(); + topParam.setHide(0); + topParam.setTop(0); + topParam.setBottom(null); + final List topNavs = cmsNavigationService.listRel(topParam); + + if (topNavs != null && !topNavs.isEmpty()) { + try { + website.setTopNavs(CommonUtil.toTreeData(topNavs, 0, + CmsNavigation::getParentId, + CmsNavigation::getNavigationId, + CmsNavigation::setChildren)); + } catch (Exception e) { + log.warn("构建顶部导航树失败: {}", e.getMessage()); + website.setTopNavs(new ArrayList<>(topNavs)); + } + } else { + website.setTopNavs(new ArrayList<>()); + } + } catch (Exception e) { + log.warn("获取顶部导航失败: {}", e.getMessage()); + website.setTopNavs(new ArrayList<>()); + } + + // 设置底部导航 + try { + final CmsNavigationParam bottomParam = new CmsNavigationParam(); + bottomParam.setHide(0); + bottomParam.setTop(null); + bottomParam.setBottom(0); + final List bottomNavs = cmsNavigationService.listRel(bottomParam); + + if (bottomNavs != null && !bottomNavs.isEmpty()) { + try { + website.setBottomNavs(CommonUtil.toTreeData(bottomNavs, 0, + CmsNavigation::getParentId, + CmsNavigation::getNavigationId, + CmsNavigation::setChildren)); + } catch (Exception e) { + log.warn("构建底部导航树失败: {}", e.getMessage()); + website.setBottomNavs(new ArrayList<>(bottomNavs)); + } + } else { + website.setBottomNavs(new ArrayList<>()); + } + } catch (Exception e) { + log.warn("获取底部导航失败: {}", e.getMessage()); + website.setBottomNavs(new ArrayList<>()); + } + } + + private void setWebsiteSetting(CmsApp website) { + final CmsAppSetting setting = cmsAppSettingService.getOne(new LambdaQueryWrapper().eq(CmsAppSetting::getAppId, website.getAppId())); + if (ObjectUtil.isNotEmpty(setting)) { + website.setSetting(setting); + } + } + + private HashMap buildServerTime() { + return buildSafeServerTime(); + } + + private HashMap buildSafeServerTime() { + HashMap serverTime = new HashMap<>(); + + try { + // 使用 LocalDateTime 替代 DateTime + java.time.LocalDateTime now = java.time.LocalDateTime.now(); + java.time.LocalDate today = java.time.LocalDate.now(); + + // 当前时间 + serverTime.put("now", now.toString()); + + // 今天日期 + serverTime.put("today", today.toString()); + + // 明天日期 + java.time.LocalDate tomorrow = today.plusDays(1); + serverTime.put("tomorrow", tomorrow.toString()); + + // 后天日期 + java.time.LocalDate afterDay = today.plusDays(2); + serverTime.put("afterDay", afterDay.toString()); + + // 今天星期几 (1=Monday, 7=Sunday) + int week = today.getDayOfWeek().getValue(); + serverTime.put("week", week); + + // 下周同一天 + java.time.LocalDate nextWeek = today.plusWeeks(1); + serverTime.put("nextWeek", nextWeek.toString()); + + // 时间戳 + serverTime.put("timestamp", System.currentTimeMillis()); + + } catch (Exception e) { + log.error("构建服务器时间失败: {}", e.getMessage(), e); + // 提供最基本的时间信息 + serverTime.put("now", java.time.LocalDateTime.now().toString()); + serverTime.put("today", java.time.LocalDate.now().toString()); + serverTime.put("timestamp", System.currentTimeMillis()); + } + + return serverTime; + } + + @Operation(summary = "清除缓存") + @DeleteMapping("/clearSiteInfo/{key}") + public ApiResult clearSiteInfo(@PathVariable("key") String key) { + log.info("清除缓存开始,key: {}", key); + System.out.println("清除缓存开始,key = " + key); + // 清除指定key + redisUtil.delete(key); + // 清除缓存 + redisUtil.delete(SITE_INFO_KEY_PREFIX.concat(getTenantId().toString())); + log.info("清除缓存结束,key: {}", SITE_INFO_KEY_PREFIX.concat(getTenantId().toString())); + // 清除小程序缓存 + redisUtil.delete(MP_INFO_KEY_PREFIX.concat(getTenantId().toString())); + // 选择支付方式 + redisUtil.delete(SELECT_PAYMENT_KEY_PREFIX.concat(getTenantId().toString())); + return success("清除成功"); + } + + // ─── 发布管理 ──────────────────────────────────────────────────────── + + @Operation(summary = "提交上架审核") + @PostMapping("/submitReview") + public ApiResult submitReview(@RequestBody Map body) { + Integer appId = body.get("appId") instanceof Number ? ((Number) body.get("appId")).intValue() : null; + if (appId == null) return fail("appId 不能为空"); + String priceType = (String) body.get("priceType"); + Integer price = body.get("price") instanceof Number ? ((Number) body.get("price")).intValue() : 0; + String subscriptionPeriod = (String) body.get("subscriptionPeriod"); + String appDescription = (String) body.get("appDescription"); + String detailDescription = (String) body.get("detailDescription"); + String screenshots = (String) body.get("screenshots"); + Integer userId = getLoginUserId(); + try { + cmsAppService.submitReview(appId, priceType, price, subscriptionPeriod, + appDescription, detailDescription, screenshots, userId); + return success("上架申请提交成功,等待审核"); + } catch (Exception e) { + return fail(e.getMessage()); + } + } + + @Operation(summary = "撤回审核申请") + @PostMapping("/withdrawReview/{appId}") + public ApiResult withdrawReview(@PathVariable Integer appId) { + try { + cmsAppService.withdrawReview(appId, getLoginUserId()); + return success("已撤回审核申请"); + } catch (Exception e) { + return fail(e.getMessage()); + } + } + + @Operation(summary = "下架应用") + @PostMapping("/unpublish/{appId}") + public ApiResult unpublish(@PathVariable Integer appId) { + try { + cmsAppService.unpublish(appId, getLoginUserId()); + return success("已下架"); + } catch (Exception e) { + return fail(e.getMessage()); + } + } + + @PreAuthorize("hasAuthority('cms:website:update')") + @Operation(summary = "管理员审核通过") + @PostMapping("/approveReview/{appId}") + public ApiResult approveReview(@PathVariable Integer appId) { + try { + cmsAppService.approveReview(appId, getLoginUserId()); + return success("审核通过,应用已上架"); + } catch (Exception e) { + return fail(e.getMessage()); + } + } + + @PreAuthorize("hasAuthority('cms:website:update')") + @Operation(summary = "管理员审核拒绝") + @PostMapping("/rejectReview") + public ApiResult rejectReview(@RequestBody Map body) { + Integer appId = body.get("appId") instanceof Number ? ((Number) body.get("appId")).intValue() : null; + String rejectReason = (String) body.get("rejectReason"); + if (appId == null) return fail("appId 不能为空"); + if (rejectReason == null || rejectReason.isBlank()) return fail("请填写拒绝原因"); + try { + cmsAppService.rejectReview(appId, rejectReason, getLoginUserId()); + return success("已拒绝该应用上架申请"); + } catch (Exception e) { + return fail(e.getMessage()); + } + } + + @PreAuthorize("hasAuthority('cms:website:update')") + @Operation(summary = "获取审核列表(管理员)") + @GetMapping("/pageReviews") + public ApiResult> pageReviews(CmsAppParam param) { + return success(cmsAppService.pageReviews(param)); + } + +} + diff --git a/src/main/java/com/gxwebsoft/cms/controller/CmsAppFieldController.java b/src/main/java/com/gxwebsoft/cms/controller/CmsAppFieldController.java new file mode 100644 index 0000000..c60110a --- /dev/null +++ b/src/main/java/com/gxwebsoft/cms/controller/CmsAppFieldController.java @@ -0,0 +1,188 @@ +package com.gxwebsoft.cms.controller; + +import cn.afterturn.easypoi.excel.ExcelImportUtil; +import cn.afterturn.easypoi.excel.entity.ImportParams; +import cn.hutool.core.util.ObjectUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import com.gxwebsoft.common.core.web.BaseController; +import com.gxwebsoft.cms.service.CmsAppFieldService; +import com.gxwebsoft.cms.entity.CmsAppField; +import com.gxwebsoft.cms.param.CmsAppFieldParam; +import com.gxwebsoft.cms.param.CmsAppFieldImportParam; +import com.gxwebsoft.common.core.utils.JSONUtil; +import com.gxwebsoft.common.core.web.ApiResult; +import com.gxwebsoft.common.core.web.PageResult; +import com.gxwebsoft.common.core.web.BatchParam; +import io.swagger.v3.oas.annotations.tags.Tag; +import io.swagger.v3.oas.annotations.Operation; +import org.checkerframework.checker.units.qual.A; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; + +import javax.annotation.Resource; +import java.util.HashMap; +import java.util.List; + +/** + * 应用参数控制器 + * + * @author 科技小王子 + * @since 2024-09-10 20:36:14 + */ +@Tag(name = "应用参数管理") +@RestController +@RequestMapping("/api/cms/cms-app-field") +public class CmsAppFieldController extends BaseController { + @Resource + private CmsAppFieldService cmsAppFieldService; + + @Operation(summary = "分页查询应用参数") + @GetMapping("/page") + public ApiResult> page(CmsAppFieldParam param) { + // 使用关联查询 + return success(cmsAppFieldService.pageRel(param)); + } + + @Operation(summary = "查询全部应用参数") + @GetMapping() + public ApiResult> list(CmsAppFieldParam param) { + // 使用关联查询 + return success(cmsAppFieldService.listRel(param)); + } + + @Operation(summary = "根据id查询应用参数") + @GetMapping("/{id}") + public ApiResult get(@PathVariable("id") Integer id) { + // 使用关联查询 + return success(cmsAppFieldService.getByIdRel(id)); + } + + @Operation(summary = "根据code查询应用参数") + @GetMapping("/getByCode/{code}") + public ApiResult getByCode(@PathVariable("code") String code) { + // 使用关联查询 + return success(cmsAppFieldService.getByCodeRel(code)); + } + + @PreAuthorize("hasAuthority('cms:cmsAppField:save')") + @Operation(summary = "添加应用参数") + @PostMapping() + public ApiResult save(@RequestBody CmsAppField cmsAppField) { + if (cmsAppFieldService.save(cmsAppField)) { + return success("添加成功"); + } + return fail("添加失败"); + } + + @PreAuthorize("hasAuthority('cms:cmsAppField:update')") + @Operation(summary = "修改应用参数") + @PutMapping() + public ApiResult update(@RequestBody CmsAppField cmsAppField) { + if (cmsAppFieldService.updateById(cmsAppField)) { + return success("修改成功"); + } + return fail("修改失败"); + } + + @PreAuthorize("hasAuthority('cms:cmsAppField:remove')") + @Operation(summary = "删除应用参数") + @DeleteMapping("/{id}") + public ApiResult remove(@PathVariable("id") Integer id) { + if (cmsAppFieldService.removeById(id)) { + return success("删除成功"); + } + return fail("删除失败"); + } + + @PreAuthorize("hasAuthority('cms:cmsAppField:save')") + @Operation(summary = "批量添加应用参数") + @PostMapping("/batch") + public ApiResult saveBatch(@RequestBody List list) { + if (cmsAppFieldService.saveBatch(list)) { + return success("添加成功"); + } + return fail("添加失败"); + } + + @PreAuthorize("hasAuthority('cms:cmsAppField:update')") + @Operation(summary = "批量修改应用参数") + @PutMapping("/batch") + public ApiResult removeBatch(@RequestBody BatchParam batchParam) { + if (batchParam.update(cmsAppFieldService, "id")) { + return success("修改成功"); + } + return fail("修改失败"); + } + + @PreAuthorize("hasAuthority('cms:cmsAppField:remove')") + @Operation(summary = "批量删除应用参数") + @DeleteMapping("/batch") + public ApiResult removeBatch(@RequestBody List ids) { + if (cmsAppFieldService.removeByIds(ids)) { + return success("删除成功"); + } + return fail("删除失败"); + } + + @Operation(summary = "获取网站配置参数-对象形式") + @GetMapping("/config") + public ApiResult getConfig(CmsAppFieldParam param) { + // 使用关联查询 + final List fields = cmsAppFieldService.listRel(param); + + HashMap config = new HashMap<>(); + fields.forEach(d -> { + config.put(d.getName(), d.getValue()); + }); + return success(config); + } + + /** + * excel批量导入应用参数 + */ + @PreAuthorize("hasAuthority('cms:cmsAppField:save')") + @Operation(summary = "批量导入应用参数") + @Transactional(rollbackFor = {Exception.class}) + @PostMapping("/import") + public ApiResult> importBatch(MultipartFile file) { + ImportParams importParams = new ImportParams(); + try { + // 第一步:永久删除已标记为 deleted=1 的记录 + cmsAppFieldService.remove(new LambdaQueryWrapper().eq(CmsAppField::getDeleted, 1)); + + // 第二步:将现有未删除的记录(deleted=0)标记为 deleted=1 + LambdaUpdateWrapper updateWrapper = new LambdaUpdateWrapper<>(); + updateWrapper.eq(CmsAppField::getDeleted, 0); + updateWrapper.set(CmsAppField::getDeleted, 1); + cmsAppFieldService.update(updateWrapper); + + // 第三步:导入XLS文件的内容 + List list = ExcelImportUtil.importExcel(file.getInputStream(), CmsAppFieldImportParam.class, importParams); + list.forEach(d -> { + CmsAppField item = JSONUtil.parseObject(JSONUtil.toJSONString(d), CmsAppField.class); + assert item != null; + if (ObjectUtil.isNotEmpty(item)) { + System.out.println("item = " + item); + // 设置默认值 + if (item.getDeleted() == null) { + item.setDeleted(0); // 新导入的数据deleted设为0 + } + if (item.getSortNumber() == null) { + item.setSortNumber(100); + } + if (item.getEncrypted() == null) { + item.setEncrypted(false); + } + cmsAppFieldService.save(item); + } + }); + return success("成功导入" + list.size() + "条", null); + } catch (Exception e) { + e.printStackTrace(); + } + return fail("导入失败", null); + } +} diff --git a/src/main/java/com/gxwebsoft/cms/controller/CmsAppSettingController.java b/src/main/java/com/gxwebsoft/cms/controller/CmsAppSettingController.java new file mode 100644 index 0000000..e722791 --- /dev/null +++ b/src/main/java/com/gxwebsoft/cms/controller/CmsAppSettingController.java @@ -0,0 +1,121 @@ +package com.gxwebsoft.cms.controller; + +import cn.hutool.core.util.ObjectUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.gxwebsoft.common.core.web.BaseController; +import com.gxwebsoft.cms.service.CmsAppSettingService; +import com.gxwebsoft.cms.entity.CmsAppSetting; +import com.gxwebsoft.cms.param.CmsAppSettingParam; +import com.gxwebsoft.common.core.web.ApiResult; +import com.gxwebsoft.common.core.web.PageResult; +import com.gxwebsoft.common.core.web.BatchParam; +import io.swagger.v3.oas.annotations.tags.Tag; +import io.swagger.v3.oas.annotations.Operation; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.*; + +import javax.annotation.Resource; +import java.util.List; + +/** + * 网站设置控制器 + * + * @author 科技小王子 + * @since 2025-02-19 01:35:44 + */ +@Tag(name = "网站设置管理") +@RestController +@RequestMapping("/api/cms/cms-app-setting") +public class CmsAppSettingController extends BaseController { + @Resource + private CmsAppSettingService cmsAppSettingService; + + @Operation(summary = "分页查询网站设置") + @GetMapping("/page") + public ApiResult> page(CmsAppSettingParam param) { + // 使用关联查询 + return success(cmsAppSettingService.pageRel(param)); + } + + @Operation(summary = "查询全部网站设置") + @GetMapping() + public ApiResult> list(CmsAppSettingParam param) { + // 使用关联查询 + return success(cmsAppSettingService.listRel(param)); + } + + @Operation(summary = "根据id查询网站设置") + @GetMapping("/{id}") + public ApiResult get(@PathVariable("id") Integer id) { + // 使用关联查询 + final CmsAppSetting cmsAppSetting = cmsAppSettingService.getOne(new LambdaQueryWrapper().eq(CmsAppSetting::getAppId, id)); + if(ObjectUtil.isEmpty(cmsAppSetting)){ + final CmsAppSetting setting = new CmsAppSetting(); + setting.setAppId(id); + cmsAppSettingService.save(setting); + return success(cmsAppSettingService.getOne(new LambdaQueryWrapper().eq(CmsAppSetting::getAppId, id))); + } + return success(cmsAppSetting); + } + + @PreAuthorize("hasAuthority('cms:cmsAppSetting:save')") + @Operation(summary = "添加网站设置") + @PostMapping() + public ApiResult save(@RequestBody CmsAppSetting cmsAppSetting) { + if (cmsAppSettingService.save(cmsAppSetting)) { + return success("添加成功"); + } + return fail("添加失败"); + } + + @PreAuthorize("hasAuthority('cms:website:update')") + @Operation(summary = "修改网站设置") + @PutMapping() + public ApiResult update(@RequestBody CmsAppSetting cmsAppSetting) { + if (cmsAppSettingService.updateById(cmsAppSetting)) { + return success("修改成功"); + } + return fail("修改失败"); + } + + @PreAuthorize("hasAuthority('cms:cmsAppSetting:remove')") + @Operation(summary = "删除网站设置") + @DeleteMapping("/{id}") + public ApiResult remove(@PathVariable("id") Integer id) { + if (cmsAppSettingService.removeById(id)) { + return success("删除成功"); + } + return fail("删除失败"); + } + + @PreAuthorize("hasAuthority('cms:cmsAppSetting:save')") + @Operation(summary = "批量添加网站设置") + @PostMapping("/batch") + public ApiResult saveBatch(@RequestBody List list) { + if (cmsAppSettingService.saveBatch(list)) { + return success("添加成功"); + } + return fail("添加失败"); + } + + @PreAuthorize("hasAuthority('cms:cmsAppSetting:update')") + @Operation(summary = "批量修改网站设置") + @PutMapping("/batch") + public ApiResult removeBatch(@RequestBody BatchParam batchParam) { + if (batchParam.update(cmsAppSettingService, "id")) { + return success("修改成功"); + } + return fail("修改失败"); + } + + @PreAuthorize("hasAuthority('cms:cmsAppSetting:remove')") + @Operation(summary = "批量删除网站设置") + @DeleteMapping("/batch") + public ApiResult removeBatch(@RequestBody List ids) { + if (cmsAppSettingService.removeByIds(ids)) { + return success("删除成功"); + } + return fail("删除失败"); + } + +} diff --git a/src/main/java/com/gxwebsoft/cms/entity/CmsApp.java b/src/main/java/com/gxwebsoft/cms/entity/CmsApp.java new file mode 100644 index 0000000..31f1eaf --- /dev/null +++ b/src/main/java/com/gxwebsoft/cms/entity/CmsApp.java @@ -0,0 +1,368 @@ +package com.gxwebsoft.cms.entity; + +import cn.hutool.core.util.DesensitizedUtil; +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; + +import java.math.BigDecimal; +import com.baomidou.mybatisplus.annotation.TableLogic; +import com.fasterxml.jackson.annotation.JsonFormat; +import java.io.Serializable; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import com.gxwebsoft.common.system.entity.User; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * 网站信息记录表 + * + * @author 科技小王子 + * @since 2024-09-10 20:36:14 + */ +@Data +@EqualsAndHashCode(callSuper = false) +@Schema(name = "CmsApp对象", description = "网站信息记录表") +public class CmsApp implements Serializable { + private static final long serialVersionUID = 1L; + + @Schema(description = "站点ID") + @TableId(value = "app_id", type = IdType.AUTO) + private Integer appId; + + @Schema(description = "网站名称") + private String appName; + + @Schema(description = "网站标识") + private String appCode; + + @Schema(description = "网站密钥") + private String websiteSecret; + + @Schema(description = "网站LOGO") + private String appIcon; + + @Schema(description = "网站LOGO") + private String websiteLogo; + + @Schema(description = "网站LOGO(深色模式)") + private String websiteDarkLogo; + + @Schema(description = "网站类型") + private String appType; + + @Schema(description = "栏目ID") + private Integer categoryId; + + + @Schema(description = "网站截图") + private String files; + + @Schema(description = "应用类型 10=web(Web应用) 20=miniprogram(小程序) 30=mobile(移动App) 40=api(API服务) 50=internal(内部工具)") + private Integer type; + + @Schema(description = "网站关键词") + private String keywords; + + @Schema(description = "域名前缀") + private String prefix; + + @Schema(description = "绑定域名") + private String domain; + + @Schema(description = "全局样式") + private String style; + + @Schema(description = "后台管理地址") + private String adminUrl; + + @Schema(description = "自定义API接口") + private String apiUrl; + + @Schema(description = "应用版本 10免费版 20授权版 30永久授权") + private Integer version; + + @Schema(description = "服务到期时间") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date expirationTime; + + @Schema(description = "是否到期") + @TableField(exist = false) + private Integer expired; + + @Schema(description = "剩余天数") + @TableField(exist = false) + private Long expiredDays; + + @Schema(description = "服务器ID") + private Integer assetsId; + + @Schema(description = "服务器ID") + private String assetsName; + + @Schema(description = "模版ID(存克隆的租户UID)") + private Integer templateId; + + @Schema(description = "模版名称") + @TableField(exist = false) + private String templateName; + + @Schema(description = "行业类型(父级)") + private String industryParent; + + @Schema(description = "行业类型(子级)") + private String industryChild; + + @Schema(description = "企业ID") + private Integer companyId; + + @Schema(description = "开发者名称") + private String developer; + + @Schema(description = "所在国家") + private String country; + + @Schema(description = "所在省份") + private String province; + + @Schema(description = "所在城市") + private String city; + + @Schema(description = "所在辖区") + private String region; + + @Schema(description = "经度") + private String longitude; + + @Schema(description = "纬度") + private String latitude; + + @Schema(description = "街道地址") + private String address; + + @Schema(description = "联系电话") + private String phone; + + @Schema(description = "电子邮箱") + private String email; + + @Schema(description = "ICP备案号") + private String icpNo; + + @Schema(description = "公安备案") + private String policeNo; + + @Schema(description = "网站描述") + private String content; + + @Schema(description = "备注") + private String comments; + + @Schema(description = "管理员备注") + private String remarks; + + @Schema(description = "是否推荐") + private Integer recommend; + + @Schema(description = "是否官方产品") + private Boolean official; + + @Schema(description = "允许展示到插件市场") + private Boolean market; + + @Schema(description = "是否插件类型 0应用 1插件") + private Boolean plugin; + + @Schema(description = "允许被搜索") + private Boolean search; + + @Schema(description = "主题色") + private String color; + + @Schema(description = "运行状态 0运行中 1已关闭 2维护中") + private Integer running; + + @Schema(description = "即将过期") + private Integer soon; + + @Schema(description = "评分") + private BigDecimal rate; + + @Schema(description = "点赞数量") + private Integer likes; + + @Schema(description = "点击数量") + private Integer clicks; + + @Schema(description = "购买数量") + private Integer buys; + + @Schema(description = "下载数量") + private Integer downloads; + + @Schema(description = "销售价格") + private BigDecimal price; + + @Schema(description = "交付方式") + private Integer deliveryMethod; + + @Schema(description = "计费方式") + private Integer chargingMethod; + + @Schema(description = "状态 0未开通 1运行中 2维护中 3已关闭 4已欠费停机 5违规关停") + private Integer status; + + @Schema(description = "状态图标") + @TableField(exist = false) + private String statusIcon; + + @Schema(description = "维护说明") + private String statusText; + + @Schema(description = "关闭说明") + private String statusClose; + + @Schema(description = "全局样式") + private String styles; + + @Schema(description = "排序号") + private Integer sortNumber; + + @Schema(description = "用户ID") + private Integer userId; + + @Schema(description = "商户ID") + private Long merchantId; + + @Schema(description = "是否删除, 0否, 1是") + @TableLogic + private Integer deleted; + + @Schema(description = "租户id") + private Integer tenantId; + + @Schema(description = "租户名称") + @TableField(exist = false) + private String tenantName; + + @Schema(description = "创建时间") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date createTime; + + @Schema(description = "修改时间") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date updateTime; + + @Schema(description = "预设字段") + @TableField(exist = false) + private List fields; + + @Schema(description = "小程序导航图标") + @TableField(exist = false) + private Map> mpMenus; + + @Schema(description = "网站导航栏") + @TableField(exist = false) + private List navigations; + + @Schema(description = "顶部菜单") + @TableField(exist = false) + private List topNavs; + + @Schema(description = "底部菜单") + @TableField(exist = false) + private List bottomNavs; + + @Schema(description = "幻灯片广告") + @TableField(exist = false) + private CmsAd slide; + + @Schema(description = "站点广告") + @TableField(exist = false) + private List ads; + + @Schema(description = "首页布局") + @TableField(exist = false) + private String layout; + + @Schema(description = "友情链接") + @TableField(exist = false) + private List links; + + @Schema(description = "配置信息") + @TableField(exist = false) + private Object config; + + @Schema(description = "服务器时间") + @TableField(exist = false) + private HashMap serverTime; + + @Schema(description = "当前登录用户") + @TableField(exist = false) + private User loginUser; + + @Schema(description = "超管账号") + @TableField(exist = false) + private String superAdminPhone; + + @Schema(description = "是否登录") + @TableField(exist = false) + private Boolean isLogin; + + @Schema(description = "网站设置") + @TableField(exist = false) + private CmsAppSetting setting; + + // ─── 发布管理字段 ────────────────────────────────────────────────── + + @Schema(description = "发布状态: developing开发中 pending_review待审核 published已上架 rejected审核未通过 deprecated已下架") + private String publishStatus; + + @Schema(description = "定价模式: free免费 one_time一次性 subscription订阅") + private String priceType; + + @Schema(description = "订阅周期: month按月 year按年") + private String subscriptionPeriod; + + @Schema(description = "应用简介(市场展示用)") + private String appDescription; + + @Schema(description = "详细说明(富文本)") + private String detailDescription; + + @Schema(description = "应用截图(JSON数组字符串)") + private String screenshots; + + @Schema(description = "安装/使用次数") + private Integer installCount; + + @Schema(description = "评分(1-5)") + private java.math.BigDecimal rating; + + @Schema(description = "审核拒绝原因") + private String rejectReason; + + @Schema(description = "提交审核时间") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private java.util.Date publishApplyTime; + + @Schema(description = "正式发布上架时间") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private java.util.Date publishTime; + + @Schema(description = "审核人用户ID") + private Integer reviewerId; + + @Schema(description = "审核操作时间") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private java.util.Date reviewTime; + + public String getPhone(){ + return DesensitizedUtil.mobilePhone(this.phone); + } +} diff --git a/src/main/java/com/gxwebsoft/cms/entity/CmsAppField.java b/src/main/java/com/gxwebsoft/cms/entity/CmsAppField.java new file mode 100644 index 0000000..b876afb --- /dev/null +++ b/src/main/java/com/gxwebsoft/cms/entity/CmsAppField.java @@ -0,0 +1,75 @@ +package com.gxwebsoft.cms.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import java.time.LocalDateTime; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.baomidou.mybatisplus.annotation.TableLogic; +import java.io.Serializable; + +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * 应用参数 + * + * @author 科技小王子 + * @since 2024-09-10 20:36:14 + */ +@Data +@EqualsAndHashCode(callSuper = false) +@Schema(name = "CmsAppField对象", description = "应用参数") +public class CmsAppField implements Serializable { + private static final long serialVersionUID = 1L; + + @Schema(description = "自增ID") + @TableId(value = "id", type = IdType.AUTO) + private Integer id; + + @Schema(description = "类型,0文本 1图片 2其他") + private Integer type; + + @Schema(description = "名称") + private String name; + + @Schema(description = "默认值") + private String defaultValue; + + @Schema(description = "可修改的值 [on|off]") + private String modifyRange; + + @Schema(description = "备注") + private String comments; + + @Schema(description = "css样式") + private String style; + + @Schema(description = "名称") + private String value; + + @Schema(description = "国际化语言") + private String lang; + + @Schema(description = "是否加密") + private Boolean encrypted; + + @Schema(description = "商户ID") + private Long merchantId; + + @Schema(description = "排序(数字越小越靠前)") + private Integer sortNumber; + + @Schema(description = "是否删除, 0否, 1是") + @TableLogic + private Integer deleted; + + @Schema(description = "租户id") + private Integer tenantId; + + @Schema(description = "创建时间") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private LocalDateTime createTime; + +} diff --git a/src/main/java/com/gxwebsoft/cms/entity/CmsAppSetting.java b/src/main/java/com/gxwebsoft/cms/entity/CmsAppSetting.java new file mode 100644 index 0000000..94c6c5c --- /dev/null +++ b/src/main/java/com/gxwebsoft/cms/entity/CmsAppSetting.java @@ -0,0 +1,89 @@ +package com.gxwebsoft.cms.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; + +import com.baomidou.mybatisplus.annotation.TableLogic; +import java.io.Serializable; +import java.util.Date; + +import com.fasterxml.jackson.annotation.JsonFormat; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * 网站设置 + * + * @author 科技小王子 + * @since 2025-02-19 01:35:44 + */ +@Data +@EqualsAndHashCode(callSuper = false) +@Schema(name = "CmsAppSetting对象", description = "网站设置") +public class CmsAppSetting implements Serializable { + private static final long serialVersionUID = 1L; + + @Schema(description = "自增ID") + @TableId(value = "id", type = IdType.AUTO) + private Integer id; + + @Schema(description = "关联网站ID") + private Integer appId; + + @Schema(description = "是否官方插件") + private Boolean official; + + @Schema(description = "是否展示在插件市场") + private Boolean market; + + @Schema(description = "是否允许被搜索") + private Boolean search; + + @Schema(description = "是否共享") + private Boolean share; + + @Schema(description = "文章是否需要审核") + private Boolean articleReview; + + @Schema(description = "是否插件 0应用1 插件 ") + private Boolean plugin; + + @Schema(description = "编辑器类型 1 富文本编辑器 2 Markdown编辑器") + private Integer editor; + + @Schema(description = "显示站内搜索") + private Boolean searchBtn; + + @Schema(description = "显示登录注册功能") + private Boolean loginBtn; + + @Schema(description = "显示悬浮客服工具") + private Boolean floatTool; + + @Schema(description = "显示版权链接") + private Boolean copyrightLink; + + @Schema(description = "导航栏最多显示数量") + private Boolean maxMenuNum; + + @Schema(description = "排序号") + private Integer sortNumber; + + @Schema(description = "是否删除, 0否, 1是") + @TableLogic + private Integer deleted; + + @Schema(description = "租户id") + private Integer tenantId; + + @Schema(description = "创建时间") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date createTime; + + @Schema(description = "修改时间") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date updateTime; + +} diff --git a/src/main/java/com/gxwebsoft/cms/mapper/CmsAppFieldMapper.java b/src/main/java/com/gxwebsoft/cms/mapper/CmsAppFieldMapper.java new file mode 100644 index 0000000..3d10733 --- /dev/null +++ b/src/main/java/com/gxwebsoft/cms/mapper/CmsAppFieldMapper.java @@ -0,0 +1,43 @@ +package com.gxwebsoft.cms.mapper; + +import com.baomidou.mybatisplus.annotation.InterceptorIgnore; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.gxwebsoft.cms.entity.CmsAppField; +import com.gxwebsoft.cms.param.CmsAppFieldParam; +import org.apache.ibatis.annotations.Param; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; + +/** + * 应用参数Mapper + * + * @author 科技小王子 + * @since 2024-09-10 20:36:14 + */ +public interface CmsAppFieldMapper extends BaseMapper { + + /** + * 分页查询 + * + * @param page 分页对象 + * @param param 查询参数 + * @return List + */ + List selectPageRel(@Param("page") IPage page, + @Param("param") CmsAppFieldParam param); + + /** + * 查询全部 + * + * @param param 查询参数 + * @return List + */ + List selectListRel(@Param("param") CmsAppFieldParam param); + + + @InterceptorIgnore(tenantLine = "true") + List selectListAllRel(@Param("param") CmsAppFieldParam param); + +} diff --git a/src/main/java/com/gxwebsoft/cms/mapper/CmsAppMapper.java b/src/main/java/com/gxwebsoft/cms/mapper/CmsAppMapper.java new file mode 100644 index 0000000..eed133c --- /dev/null +++ b/src/main/java/com/gxwebsoft/cms/mapper/CmsAppMapper.java @@ -0,0 +1,53 @@ +package com.gxwebsoft.cms.mapper; + +import com.baomidou.mybatisplus.annotation.InterceptorIgnore; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.gxwebsoft.cms.entity.CmsApp; +import com.gxwebsoft.cms.param.CmsAppParam; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** + * 网站信息记录表Mapper + * + * @author 科技小王子 + * @since 2024-09-10 20:36:14 + */ +public interface CmsAppMapper extends BaseMapper { + + /** + * 分页查询 + * + * @param page 分页对象 + * @param param 查询参数 + * @return List + */ + List selectPageRel(@Param("page") IPage page, + @Param("param") CmsAppParam param); + + /** + * 查询全部 + * + * @param param 查询参数 + * @return List + */ + List selectListRel(@Param("param") CmsAppParam param); + + @InterceptorIgnore(tenantLine = "true") + List selectPageRelAll(@Param("page") IPage page, + @Param("param") CmsAppParam param); + + @InterceptorIgnore(tenantLine = "true") + CmsApp getByIdRelAll(@Param("appId") Integer id); + + @InterceptorIgnore(tenantLine = "true") + boolean updateByIdAll(@Param("param") CmsApp cmsApp); + + @InterceptorIgnore(tenantLine = "true") + boolean removeByIdAll(@Param("appId") Integer id); + + @InterceptorIgnore(tenantLine = "true") + CmsApp getByTenantId(@Param("tenantId") Integer tenantId); +} diff --git a/src/main/java/com/gxwebsoft/cms/mapper/CmsAppSettingMapper.java b/src/main/java/com/gxwebsoft/cms/mapper/CmsAppSettingMapper.java new file mode 100644 index 0000000..66b9f91 --- /dev/null +++ b/src/main/java/com/gxwebsoft/cms/mapper/CmsAppSettingMapper.java @@ -0,0 +1,37 @@ +package com.gxwebsoft.cms.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.gxwebsoft.cms.entity.CmsAppSetting; +import com.gxwebsoft.cms.param.CmsAppSettingParam; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** + * 网站设置Mapper + * + * @author 科技小王子 + * @since 2025-02-19 01:35:44 + */ +public interface CmsAppSettingMapper extends BaseMapper { + + /** + * 分页查询 + * + * @param page 分页对象 + * @param param 查询参数 + * @return List + */ + List selectPageRel(@Param("page") IPage page, + @Param("param") CmsAppSettingParam param); + + /** + * 查询全部 + * + * @param param 查询参数 + * @return List + */ + List selectListRel(@Param("param") CmsAppSettingParam param); + +} diff --git a/src/main/java/com/gxwebsoft/cms/mapper/xml/CmsAppFieldMapper.xml b/src/main/java/com/gxwebsoft/cms/mapper/xml/CmsAppFieldMapper.xml new file mode 100644 index 0000000..54ae605 --- /dev/null +++ b/src/main/java/com/gxwebsoft/cms/mapper/xml/CmsAppFieldMapper.xml @@ -0,0 +1,82 @@ + + + + + + + SELECT a.*, b.user_id + FROM cms_app_field a + LEFT JOIN cms_app b ON a.tenant_id = b.tenant_id + + + AND a.id = #{param.id} + + + AND a.type = #{param.type} + + + AND a.lang = #{param.lang} + + + AND a.name LIKE CONCAT('%', #{param.name}, '%') + + + AND a.default_value LIKE CONCAT('%', #{param.defaultValue}, '%') + + + AND a.modify_range LIKE CONCAT('%', #{param.modifyRange}, '%') + + + AND a.comments LIKE CONCAT('%', #{param.comments}, '%') + + + AND a.style LIKE CONCAT('%', #{param.style}, '%') + + + AND a.value LIKE CONCAT('%', #{param.value}, '%') + + + AND a.sort_number = #{param.sortNumber} + + + AND b.user_id = #{param.userId} + + + AND a.deleted = #{param.deleted} + + + AND a.deleted = 0 + + + AND a.create_time >= #{param.createTimeStart} + + + AND a.create_time <= #{param.createTimeEnd} + + + AND (a.comments LIKE CONCAT('%', #{param.keywords}, '%') + OR a.value LIKE CONCAT('%', #{param.keywords}, '%') + OR a.name LIKE CONCAT('%', #{param.keywords}, '%') + ) + + + + + + + + + + + + + + + + diff --git a/src/main/java/com/gxwebsoft/cms/mapper/xml/CmsAppMapper.xml b/src/main/java/com/gxwebsoft/cms/mapper/xml/CmsAppMapper.xml new file mode 100644 index 0000000..fd1cc74 --- /dev/null +++ b/src/main/java/com/gxwebsoft/cms/mapper/xml/CmsAppMapper.xml @@ -0,0 +1,463 @@ + + + + + + + SELECT a.*, b.tenant_name as tenantName + FROM cms_app a + LEFT JOIN gxwebsoft_core.sys_tenant b ON a.tenant_id = b.tenant_id + + + AND a.app_id = #{param.appId} + + + AND a.type LIKE CONCAT('%', #{param.type}, '%') + + + AND a.app_name LIKE CONCAT('%', #{param.appName}, '%') + + + AND a.app_code LIKE CONCAT('%', #{param.appCode}, '%') + + + AND a.app_icon LIKE CONCAT('%', #{param.appIcon}, '%') + + + AND a.website_logo LIKE CONCAT('%', #{param.websiteLogo}, '%') + + + AND a.website_dark_logo LIKE CONCAT('%', #{param.websiteDarkLogo}, '%') + + + AND a.app_type LIKE CONCAT('%', #{param.appType}, '%') + + + AND a.prefix LIKE CONCAT('%', #{param.prefix}, '%') + + + AND a.domain LIKE CONCAT('%', #{param.domain}, '%') + + + AND a.style LIKE CONCAT('%', #{param.style}, '%') + + + AND a.admin_url LIKE CONCAT('%', #{param.adminUrl}, '%') + + + AND a.version = #{param.version} + + + AND a.expiration_time LIKE CONCAT('%', #{param.expirationTime}, '%') + + + AND a.template_id = #{param.templateId} + + + AND a.industry_parent LIKE CONCAT('%', #{param.industryParent}, '%') + + + AND a.industry_child LIKE CONCAT('%', #{param.industryChild}, '%') + + + AND a.category_id = #{param.categoryId} + + + AND a.company_id = #{param.companyId} + + + AND a.country LIKE CONCAT('%', #{param.country}, '%') + + + AND a.province LIKE CONCAT('%', #{param.province}, '%') + + + AND a.city LIKE CONCAT('%', #{param.city}, '%') + + + AND a.region LIKE CONCAT('%', #{param.region}, '%') + + + AND a.longitude LIKE CONCAT('%', #{param.longitude}, '%') + + + AND a.latitude LIKE CONCAT('%', #{param.latitude}, '%') + + + AND a.address LIKE CONCAT('%', #{param.address}, '%') + + + AND a.email LIKE CONCAT('%', #{param.email}, '%') + + + AND a.icp_no LIKE CONCAT('%', #{param.icpNo}, '%') + + + AND a.police_no LIKE CONCAT('%', #{param.policeNo}, '%') + + + AND a.comments LIKE CONCAT('%', #{param.comments}, '%') + + + AND a.recommend = #{param.recommend} + + + AND a.official = #{param.official} + + + AND a.market = #{param.market} + + + AND a.plugin = #{param.plugin} + + + AND a.search = #{param.search} + + + AND a.color = #{param.color} + + + AND a.status = #{param.status} + + + AND a.status_text LIKE CONCAT('%', #{param.statusText}, '%') + + + AND a.status_close LIKE CONCAT('%', #{param.statusClose}, '%') + + + AND a.styles LIKE CONCAT('%', #{param.styles}, '%') + + + AND a.sort_number = #{param.sortNumber} + + + AND a.user_id = #{param.userId} + + + AND (a.app_name LIKE CONCAT('%', #{param.keywords}, '%') + OR a.app_code LIKE CONCAT('%', #{param.keywords}, '%') + OR a.domain LIKE CONCAT('%', #{param.keywords}, '%') + OR a.tenant_id = #{param.keywords} + ) + + + AND a.deleted = #{param.deleted} + + + AND a.deleted = 0 + + + AND a.create_time >= #{param.createTimeStart} + + + AND a.create_time <= #{param.createTimeEnd} + + + + + + + + + + + + + + + + + UPDATE cms_app + + + type = #{param.type}, + + + app_name = #{param.appName}, + + + website_logo = #{param.websiteLogo}, + + + app_code = #{param.appCode}, + + + app_type = #{param.appType}, + + + template_id = #{param.templateId}, + + + company_id = #{param.companyId}, + + + admin_url = #{param.adminUrl}, + + + running = #{param.running}, + + + domain = #{param.domain}, + + + market = #{param.market}, + + + plugin = #{param.plugin}, + + + `search` = #{param.search}, + + + developer = #{param.developer}, + + + color = #{param.color}, + + + recommend = #{param.recommend}, + + + official = #{param.official}, + + + prefix = #{param.prefix}, + + + version = #{param.version}, + + + files = #{param.files}, + + + price = #{param.price}, + + + delivery_method = #{param.deliveryMethod}, + + + charging_method = #{param.chargingMethod}, + + + keywords = #{param.keywords}, + + + comments = #{param.comments}, + + + content = #{param.content}, + + + deleted = #{param.deleted}, + + + deleted = 0, + + + + app_id = #{param.appId} + + + + + + diff --git a/src/main/java/com/gxwebsoft/cms/mapper/xml/CmsAppSettingMapper.xml b/src/main/java/com/gxwebsoft/cms/mapper/xml/CmsAppSettingMapper.xml new file mode 100644 index 0000000..0d8f3ea --- /dev/null +++ b/src/main/java/com/gxwebsoft/cms/mapper/xml/CmsAppSettingMapper.xml @@ -0,0 +1,81 @@ + + + + + + + SELECT a.* + FROM cms_app_setting a + + + AND a.id = #{param.id} + + + AND a.app_id = #{param.appId} + + + AND a.official = #{param.official} + + + AND a.market = #{param.market} + + + AND a.search = #{param.search} + + + AND a.share = #{param.share} + + + AND a.plugin = #{param.plugin} + + + AND a.editor = #{param.editor} + + + AND a.search_btn = #{param.searchBtn} + + + AND a.login_btn = #{param.loginBtn} + + + AND a.float_tool = #{param.floatTool} + + + AND a.copyright_link = #{param.copyrightLink} + + + AND a.max_menu_num = #{param.maxMenuNum} + + + AND a.sort_number = #{param.sortNumber} + + + AND a.deleted = #{param.deleted} + + + AND a.deleted = 0 + + + AND a.create_time >= #{param.createTimeStart} + + + AND a.create_time <= #{param.createTimeEnd} + + + AND (a.comments LIKE CONCAT('%', #{param.keywords}, '%') + ) + + + + + + + + + + + diff --git a/src/main/java/com/gxwebsoft/cms/param/CmsAppFieldImportParam.java b/src/main/java/com/gxwebsoft/cms/param/CmsAppFieldImportParam.java new file mode 100644 index 0000000..8c56d11 --- /dev/null +++ b/src/main/java/com/gxwebsoft/cms/param/CmsAppFieldImportParam.java @@ -0,0 +1,56 @@ +package com.gxwebsoft.cms.param; + +import cn.afterturn.easypoi.excel.annotation.Excel; +import lombok.Data; + +import java.io.Serializable; + +/** + * 应用参数导入参数 + * + * @author 科技小王子 + * @since 2024-09-10 20:36:14 + */ +@Data +public class CmsAppFieldImportParam implements Serializable { + private static final long serialVersionUID = 1L; + + @Excel(name = "自增ID") + private Integer id; + + @Excel(name = "类型") + private Integer type; + + @Excel(name = "名称") + private String name; + + @Excel(name = "默认值") + private String defaultValue; + + @Excel(name = "可修改的值") + private String modifyRange; + + @Excel(name = "备注") + private String comments; + + @Excel(name = "css样式") + private String style; + + @Excel(name = "值") + private String value; + + @Excel(name = "国际化语言") + private String lang; + + @Excel(name = "加密") + private Boolean encrypted; + + @Excel(name = "商户ID") + private Long merchantId; + + @Excel(name = "排序") + private Integer sortNumber; + + @Excel(name = "租户ID") + private Integer tenantId; +} diff --git a/src/main/java/com/gxwebsoft/cms/param/CmsAppFieldParam.java b/src/main/java/com/gxwebsoft/cms/param/CmsAppFieldParam.java new file mode 100644 index 0000000..1d2b43e --- /dev/null +++ b/src/main/java/com/gxwebsoft/cms/param/CmsAppFieldParam.java @@ -0,0 +1,66 @@ +package com.gxwebsoft.cms.param; + +import com.gxwebsoft.common.core.annotation.QueryField; +import com.gxwebsoft.common.core.annotation.QueryType; +import com.gxwebsoft.common.core.web.BaseParam; +import com.fasterxml.jackson.annotation.JsonInclude; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * 应用参数查询参数 + * + * @author 科技小王子 + * @since 2024-09-10 20:36:14 + */ +@Data +@EqualsAndHashCode(callSuper = false) +@JsonInclude(JsonInclude.Include.NON_NULL) +@Schema(name = "CmsAppFieldParam对象", description = "应用参数查询参数") +public class CmsAppFieldParam extends BaseParam { + private static final long serialVersionUID = 1L; + + @Schema(description = "自增ID") + @QueryField(type = QueryType.EQ) + private Integer id; + + @Schema(description = "类型,0文本 1图片 2其他") + @QueryField(type = QueryType.EQ) + private Integer type; + + @Schema(description = "名称") + private String name; + + @Schema(description = "默认值") + private String defaultValue; + + @Schema(description = "可修改的值 [on|off]") + private String modifyRange; + + @Schema(description = "备注") + private String comments; + + @Schema(description = "css样式") + private String style; + + @Schema(description = "名称") + private String value; + + @Schema(description = "是否加密") + private Boolean encrypted; + + @Schema(description = "排序(数字越小越靠前)") + @QueryField(type = QueryType.EQ) + private Integer sortNumber; + + @Schema(description = "是否删除, 0否, 1是") + @QueryField(type = QueryType.EQ) + private Integer deleted; + + @Schema(description = "创建者") + @QueryField(type = QueryType.EQ) + private Integer userId; + +} diff --git a/src/main/java/com/gxwebsoft/cms/param/CmsAppParam.java b/src/main/java/com/gxwebsoft/cms/param/CmsAppParam.java new file mode 100644 index 0000000..483004a --- /dev/null +++ b/src/main/java/com/gxwebsoft/cms/param/CmsAppParam.java @@ -0,0 +1,231 @@ +package com.gxwebsoft.cms.param; + +import com.baomidou.mybatisplus.annotation.TableField; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.gxwebsoft.common.core.annotation.QueryField; +import com.gxwebsoft.common.core.annotation.QueryType; +import com.gxwebsoft.common.core.web.BaseParam; +import com.fasterxml.jackson.annotation.JsonInclude; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.time.LocalDateTime; +import java.util.Set; + +/** + * 网站信息记录表查询参数 + * + * @author 科技小王子 + * @since 2024-09-10 20:36:14 + */ +@Data +@EqualsAndHashCode(callSuper = false) +@JsonInclude(JsonInclude.Include.NON_NULL) +@Schema(name = "CmsAppParam对象", description = "网站信息记录表查询参数") +public class CmsAppParam extends BaseParam { + private static final long serialVersionUID = 1L; + + @Schema(description = "站点ID") + @QueryField(type = QueryType.EQ) + private Integer appId; + + @Schema(description = "站点类型") + @QueryField(type = QueryType.EQ) + private Integer type; + + @Schema(description = "网站名称") + private String appName; + + @Schema(description = "网站标识") + private String appCode; + + @Schema(description = "网站密钥") + private String websiteSecret; + + @Schema(description = "网站LOGO") + private String appIcon; + + @Schema(description = "网站LOGO") + private String websiteLogo; + + @Schema(description = "网站LOGO(深色模式)") + private String websiteDarkLogo; + + @Schema(description = "网站类型") + private String appType; + + @Schema(description = "栏目ID") + @QueryField(type = QueryType.EQ) + private Integer categoryId; + + @Schema(description = "网站截图") + private String files; + + @Schema(description = "网站关键词") + private String keywords; + + @Schema(description = "域名前缀") + private String prefix; + + @Schema(description = "绑定域名") + private String domain; + + @Schema(description = "全局样式") + private String style; + + @Schema(description = "后台管理地址") + private String adminUrl; + + @Schema(description = "应用版本 10免费版 20授权版 30永久授权") + @QueryField(type = QueryType.EQ) + private Integer version; + + @Schema(description = "服务到期时间") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private String expirationTime; + + @Schema(description = "模版ID") + @QueryField(type = QueryType.EQ) + private Integer templateId; + + @Schema(description = "行业类型(父级)") + private String industryParent; + + @Schema(description = "行业类型(子级)") + private String industryChild; + + @Schema(description = "企业ID") + @QueryField(type = QueryType.EQ) + private Integer companyId; + + @Schema(description = "开发者名称") + private String developer; + + @Schema(description = "所在国家") + private String country; + + @Schema(description = "所在省份") + private String province; + + @Schema(description = "所在城市") + private String city; + + @Schema(description = "所在辖区") + private String region; + + @Schema(description = "经度") + private String longitude; + + @Schema(description = "纬度") + private String latitude; + + @Schema(description = "街道地址") + private String address; + + @Schema(description = "联系电话") + private String phone; + + @Schema(description = "电子邮箱") + private String email; + + @Schema(description = "ICP备案号") + private String icpNo; + + @Schema(description = "公安备案") + private String policeNo; + + @Schema(description = "备注") + private String comments; + + @Schema(description = "是否推荐") + @QueryField(type = QueryType.EQ) + private Integer recommend; + + @Schema(description = "是否官方产品") + @QueryField(type = QueryType.EQ) + private Boolean official; + + @Schema(description = "是否查询超管账号") + @QueryField(type = QueryType.EQ) + private Boolean showAdminPhone; + + @Schema(description = "允许展示到插件市场") + @QueryField(type = QueryType.EQ) + private Boolean market; + + @Schema(description = "是否插件类型 0应用 1插件") + @QueryField(type = QueryType.EQ) + private Boolean plugin; + + @Schema(description = "允许被搜索") + @QueryField(type = QueryType.EQ) + private Boolean search; + + @Schema(description = "主题色") + private String color; + + @Schema(description = "点赞数量") + private Integer likes; + + @Schema(description = "点击数量") + private Integer clicks; + + @Schema(description = "购买数量") + private Integer buys; + + @Schema(description = "下载数量") + private Integer downloads; + + @Schema(description = "状态 0未开通 1运行中 2维护中 3已关闭 4已欠费停机 5违规关停") + @QueryField(type = QueryType.EQ) + private Integer status; + + @Schema(description = "维护说明") + private String statusText; + + @Schema(description = "关闭说明") + private String statusClose; + + @Schema(description = "全局样式") + private String styles; + + @Schema(description = "运行状态") + @QueryField(type = QueryType.EQ) + private Integer running; + + @Schema(description = "排序号") + @QueryField(type = QueryType.EQ) + private Integer sortNumber; + + @Schema(description = "用户ID") + @QueryField(type = QueryType.EQ) + private Integer userId; + + @Schema(description = "按userId集搜索") + @QueryField(type = QueryType.EQ) + private Set userIds; + + @Schema(description = "是否删除, 0否, 1是") + @QueryField(type = QueryType.EQ) + private Integer deleted; + + @Schema(description = "按WebsiteIds集搜索") + private Set appIds; + + @Schema(description = "协作成员userId(查该用户作为成员参与的应用,联表 app_user)") + private Integer memberUserId; + + @Schema(description = "当前登录用户ID") + @QueryField(type = QueryType.EQ) + private Integer loginUserId; + + @Schema(description = "管理员手机号") + @QueryField(type = QueryType.EQ) + private String adminPhone; + + @Schema(description = "发布状态筛选: developing/pending_review/published/rejected/deprecated") + private String publishStatus; + +} diff --git a/src/main/java/com/gxwebsoft/cms/param/CmsAppSettingParam.java b/src/main/java/com/gxwebsoft/cms/param/CmsAppSettingParam.java new file mode 100644 index 0000000..30d64a0 --- /dev/null +++ b/src/main/java/com/gxwebsoft/cms/param/CmsAppSettingParam.java @@ -0,0 +1,86 @@ +package com.gxwebsoft.cms.param; + +import java.math.BigDecimal; +import com.gxwebsoft.common.core.annotation.QueryField; +import com.gxwebsoft.common.core.annotation.QueryType; +import com.gxwebsoft.common.core.web.BaseParam; +import com.fasterxml.jackson.annotation.JsonInclude; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * 网站设置查询参数 + * + * @author 科技小王子 + * @since 2025-02-19 01:35:44 + */ +@Data +@EqualsAndHashCode(callSuper = false) +@JsonInclude(JsonInclude.Include.NON_NULL) +@Schema(name = "CmsAppSettingParam对象", description = "网站设置查询参数") +public class CmsAppSettingParam extends BaseParam { + private static final long serialVersionUID = 1L; + + @Schema(description = "自增ID") + @QueryField(type = QueryType.EQ) + private Integer id; + + @Schema(description = "关联网站ID") + @QueryField(type = QueryType.EQ) + private Integer appId; + + @Schema(description = "是否官方插件") + @QueryField(type = QueryType.EQ) + private Boolean official; + + @Schema(description = "是否展示在插件市场") + @QueryField(type = QueryType.EQ) + private Boolean market; + + @Schema(description = "是否允许被搜索") + @QueryField(type = QueryType.EQ) + private Boolean search; + + @Schema(description = "是否共享") + @QueryField(type = QueryType.EQ) + private Boolean share; + + @Schema(description = "是否插件 0应用1 插件 ") + @QueryField(type = QueryType.EQ) + private Boolean plugin; + + @Schema(description = "编辑器类型 1 md-editor-v3, 2 tinymce-editor") + @QueryField(type = QueryType.EQ) + private Boolean editor; + + @Schema(description = "显示站内搜索") + @QueryField(type = QueryType.EQ) + private Boolean searchBtn; + + @Schema(description = "显示登录注册功能") + @QueryField(type = QueryType.EQ) + private Boolean loginBtn; + + @Schema(description = "显示悬浮客服工具") + @QueryField(type = QueryType.EQ) + private Boolean floatTool; + + @Schema(description = "显示版权链接") + @QueryField(type = QueryType.EQ) + private Boolean copyrightLink; + + @Schema(description = "导航栏最多显示数量") + @QueryField(type = QueryType.EQ) + private Boolean maxMenuNum; + + @Schema(description = "排序号") + @QueryField(type = QueryType.EQ) + private Integer sortNumber; + + @Schema(description = "是否删除, 0否, 1是") + @QueryField(type = QueryType.EQ) + private Integer deleted; + +} diff --git a/src/main/java/com/gxwebsoft/cms/service/CmsAppFieldService.java b/src/main/java/com/gxwebsoft/cms/service/CmsAppFieldService.java new file mode 100644 index 0000000..85f95e1 --- /dev/null +++ b/src/main/java/com/gxwebsoft/cms/service/CmsAppFieldService.java @@ -0,0 +1,43 @@ +package com.gxwebsoft.cms.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.gxwebsoft.common.core.web.PageResult; +import com.gxwebsoft.cms.entity.CmsAppField; +import com.gxwebsoft.cms.param.CmsAppFieldParam; + +import java.util.List; + +/** + * 应用参数Service + * + * @author 科技小王子 + * @since 2024-09-10 20:36:14 + */ +public interface CmsAppFieldService extends IService { + + /** + * 分页关联查询 + * + * @param param 查询参数 + * @return PageResult + */ + PageResult pageRel(CmsAppFieldParam param); + + /** + * 关联查询全部 + * + * @param param 查询参数 + * @return List + */ + List listRel(CmsAppFieldParam param); + + /** + * 根据id查询 + * + * @param id 自增ID + * @return CmsAppField + */ + CmsAppField getByIdRel(Integer id); + + CmsAppField getByCodeRel(String code); +} diff --git a/src/main/java/com/gxwebsoft/cms/service/CmsAppService.java b/src/main/java/com/gxwebsoft/cms/service/CmsAppService.java new file mode 100644 index 0000000..e70fbb8 --- /dev/null +++ b/src/main/java/com/gxwebsoft/cms/service/CmsAppService.java @@ -0,0 +1,92 @@ +package com.gxwebsoft.cms.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.gxwebsoft.common.core.web.PageResult; +import com.gxwebsoft.cms.entity.CmsApp; +import com.gxwebsoft.cms.param.CmsAppParam; +import com.gxwebsoft.shop.vo.ShopVo; + +import java.util.List; + +/** + * 网站信息记录表Service + * + * @author 科技小王子 + * @since 2024-09-10 20:36:14 + */ +public interface CmsAppService extends IService { + + /** + * 分页关联查询 + * + * @param param 查询参数 + * @return PageResult + */ + PageResult pageRel(CmsAppParam param); + + /** + * 关联查询全部 + * + * @param param 查询参数 + * @return List + */ + List listRel(CmsAppParam param); + + /** + * 根据id查询 + * + * @param appId 站点ID + * @return CmsApp + */ + CmsApp getByIdRel(Integer appId); + + PageResult pageRelAll(CmsAppParam param); + + // 创建站点 + CmsApp create(CmsApp cmsApp); + + CmsApp getByIdRelAll(Integer id); + + boolean updateByIdAll(CmsApp cmsApp); + + boolean removeByIdAll(Integer id); + + CmsApp getByTenantId(Integer tenantId); + + /** + * 获取网站基本信息(VO格式) + * + * @param tenantId 租户ID + * @return 网站信息VO + */ + ShopVo getSiteInfo(Integer tenantId); + + /** + * 清除网站信息缓存 + * + * @param tenantId 租户ID + */ + void clearSiteInfoCache(Integer tenantId); + + // ─── 发布管理 ────────────────────────────────────────────────── + + /** 提交上架审核 */ + void submitReview(Integer appId, String priceType, Integer price, + String subscriptionPeriod, String appDescription, + String detailDescription, String screenshots, Integer userId); + + /** 撤回审核申请(回到开发中) */ + void withdrawReview(Integer appId, Integer userId); + + /** 下架应用 */ + void unpublish(Integer appId, Integer userId); + + /** 管理员审核通过 */ + void approveReview(Integer appId, Integer reviewerId); + + /** 管理员审核拒绝 */ + void rejectReview(Integer appId, String rejectReason, Integer reviewerId); + + /** 分页查询审核列表(管理员) */ + PageResult pageReviews(CmsAppParam param); +} diff --git a/src/main/java/com/gxwebsoft/cms/service/CmsAppSettingService.java b/src/main/java/com/gxwebsoft/cms/service/CmsAppSettingService.java new file mode 100644 index 0000000..8c2b915 --- /dev/null +++ b/src/main/java/com/gxwebsoft/cms/service/CmsAppSettingService.java @@ -0,0 +1,42 @@ +package com.gxwebsoft.cms.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.gxwebsoft.common.core.web.PageResult; +import com.gxwebsoft.cms.entity.CmsAppSetting; +import com.gxwebsoft.cms.param.CmsAppSettingParam; + +import java.util.List; + +/** + * 网站设置Service + * + * @author 科技小王子 + * @since 2025-02-19 01:35:44 + */ +public interface CmsAppSettingService extends IService { + + /** + * 分页关联查询 + * + * @param param 查询参数 + * @return PageResult + */ + PageResult pageRel(CmsAppSettingParam param); + + /** + * 关联查询全部 + * + * @param param 查询参数 + * @return List + */ + List listRel(CmsAppSettingParam param); + + /** + * 根据id查询 + * + * @param id 自增ID + * @return CmsAppSetting + */ + CmsAppSetting getByIdRel(Integer id); + +} diff --git a/src/main/java/com/gxwebsoft/cms/service/impl/CmsAppFieldServiceImpl.java b/src/main/java/com/gxwebsoft/cms/service/impl/CmsAppFieldServiceImpl.java new file mode 100644 index 0000000..2a1e0eb --- /dev/null +++ b/src/main/java/com/gxwebsoft/cms/service/impl/CmsAppFieldServiceImpl.java @@ -0,0 +1,54 @@ +package com.gxwebsoft.cms.service.impl; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.gxwebsoft.cms.mapper.CmsAppFieldMapper; +import com.gxwebsoft.cms.service.CmsAppFieldService; +import com.gxwebsoft.cms.entity.CmsAppField; +import com.gxwebsoft.cms.param.CmsAppFieldParam; +import com.gxwebsoft.common.core.web.PageParam; +import com.gxwebsoft.common.core.web.PageResult; +import org.springframework.stereotype.Service; + +import java.util.List; + +/** + * 应用参数Service实现 + * + * @author 科技小王子 + * @since 2024-09-10 20:36:14 + */ +@Service +public class CmsAppFieldServiceImpl extends ServiceImpl implements CmsAppFieldService { + + @Override + public PageResult pageRel(CmsAppFieldParam param) { + PageParam page = new PageParam<>(param); + page.setDefaultOrder("sort_number asc, create_time asc"); + List list = baseMapper.selectPageRel(page, param); + return new PageResult<>(list, page.getTotal()); + } + + @Override + public List listRel(CmsAppFieldParam param) { + List list = baseMapper.selectListRel(param); + // 排序 + PageParam page = new PageParam<>(); + page.setDefaultOrder("sort_number asc, create_time asc"); + return page.sortRecords(list); + } + + @Override + public CmsAppField getByIdRel(Integer id) { + CmsAppFieldParam param = new CmsAppFieldParam(); + param.setId(id); + return param.getOne(baseMapper.selectListRel(param)); + } + + @Override + public CmsAppField getByCodeRel(String code) { + CmsAppFieldParam param = new CmsAppFieldParam(); + param.setName(code); + return param.getOne(baseMapper.selectListRel(param)); + } + +} diff --git a/src/main/java/com/gxwebsoft/cms/service/impl/CmsAppServiceImpl.java b/src/main/java/com/gxwebsoft/cms/service/impl/CmsAppServiceImpl.java new file mode 100644 index 0000000..9b10606 --- /dev/null +++ b/src/main/java/com/gxwebsoft/cms/service/impl/CmsAppServiceImpl.java @@ -0,0 +1,587 @@ +package com.gxwebsoft.cms.service.impl; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.gxwebsoft.cms.entity.*; +import com.gxwebsoft.cms.mapper.*; +import com.gxwebsoft.cms.param.*; +import com.gxwebsoft.cms.service.*; +import com.gxwebsoft.common.core.utils.JSONUtil; +import com.gxwebsoft.common.core.utils.RedisUtil; +import com.gxwebsoft.common.core.web.PageParam; +import com.gxwebsoft.common.core.web.PageResult; +import com.gxwebsoft.common.system.entity.User; +import com.gxwebsoft.common.system.service.CompanyService; +import com.gxwebsoft.common.system.service.UserService; +import com.gxwebsoft.project.entity.Project; +import com.gxwebsoft.project.service.ProjectService; +import com.gxwebsoft.shop.vo.ShopVo; +import org.springframework.stereotype.Service; + +import javax.annotation.Resource; +import java.time.LocalDateTime; +import java.util.List; +import java.util.concurrent.TimeUnit; + +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.StrUtil; +import lombok.extern.slf4j.Slf4j; + +/** + * 网站信息记录表Service实现 + * + * @author 科技小王子 + * @since 2024-09-10 20:36:14 + */ +@Slf4j +@Service +public class CmsAppServiceImpl extends ServiceImpl implements CmsAppService { + + private static final String SITE_INFO_KEY_PREFIX = "SiteInfo:"; + @Resource + private CmsAppFieldMapper cmsAppFieldMapper; + @Resource + private CmsModelMapper cmsModelMapper; + @Resource + private CmsNavigationMapper cmsNavigationMapper; + @Resource + private CmsLangLogMapper cmsLangLogMapper; + @Resource + private CmsAdMapper cmsAdMapper; + @Resource + private CmsLinkMapper cmsLinkMapper; + @Resource + private CmsArticleMapper cmsArticleMapper; + @Resource + private CmsAppFieldService cmsAppFieldService; + @Resource + private CmsModelService cmsModelService; + @Resource + private CmsNavigationService cmsNavigationService; + @Resource + private CmsLangLogService cmsLangLogService; + @Resource + private CmsAdService cmsAdService; + @Resource + private CmsLinkService cmsLinkService; + @Resource + private CmsArticleService cmsArticleService; + @Resource + private CmsArticleContentService cmsArticleContentService; + @Resource + private CmsAppMapper cmsAppMapper; + @Resource + private ProjectService projectService; + @Resource + private RedisUtil redisUtil; + @Resource + private UserService userService; + @Resource + private CompanyService companyService; + + @Override + public PageResult pageRel(CmsAppParam param) { + PageParam page = new PageParam<>(param); + page.setDefaultOrder("create_time desc"); + List list = baseMapper.selectPageRel(page, param); + list.forEach(d -> { + LocalDateTime now = LocalDateTime.now(); + if (d.getExpirationTime() != null) { + // 将Date转换为LocalDateTime进行计算 + LocalDateTime expirationTime = d.getExpirationTime().toInstant() + .atZone(java.time.ZoneId.systemDefault()) + .toLocalDateTime(); + // 即将过期(30天内过期的) + d.setSoon(expirationTime.minusDays(30).compareTo(now)); + // 是否过期 -1已过期 大于0 未过期 + d.setStatus(expirationTime.compareTo(now)); + } else { + d.setSoon(0); + d.setStatus(1); + } + }); + return new PageResult<>(list, page.getTotal()); + } + + @Override + public List listRel(CmsAppParam param) { + List list = baseMapper.selectListRel(param); + // 排序 + PageParam page = new PageParam<>(); + page.setDefaultOrder("create_time desc"); + return page.sortRecords(list); + } + + @Override + public CmsApp getByIdRel(Integer appId) { + CmsAppParam param = new CmsAppParam(); + param.setAppId(appId); + return param.getOne(baseMapper.selectListRel(param)); + } + + @Override + public PageResult pageRelAll(CmsAppParam param) { + PageParam page = new PageParam<>(param); + page.setDefaultOrder("create_time desc"); + List list = baseMapper.selectPageRelAll(page, param); + list.forEach(d -> { + LocalDateTime now = LocalDateTime.now(); + if (d.getExpirationTime() != null) { + // 将Date转换为LocalDateTime进行计算 + LocalDateTime expirationTime = d.getExpirationTime().toInstant() + .atZone(java.time.ZoneId.systemDefault()) + .toLocalDateTime(); + // 即将过期(30天内过期的) + d.setSoon(expirationTime.minusDays(30).compareTo(now)); + // 是否过期 -1已过期 大于0 未过期 + d.setStatus(expirationTime.compareTo(now)); + } else { + d.setSoon(0); + d.setStatus(1); + } + }); + return new PageResult<>(list, page.getTotal()); + } + + @Override + public CmsApp create(CmsApp website) { + final User loginUser = website.getLoginUser(); + // 生成全局唯一 appCode:若调用方已指定则保留,否则自动生成 + if (StrUtil.isBlank(website.getAppCode())) { + website.setAppCode(generateUniqueCode("template-" + loginUser.getTenantId())); + } + website.setAppIcon("/favicon.ico"); + // 根据应用类型设置管理后台地址和 appType 字符串 + // 10=web(Web应用) | 20=miniprogram(小程序) | 30=mobile(移动App) | 40=api(API服务) | 50=internal(内部工具) + Integer siteType = website.getType(); + if (siteType == null) siteType = 10; + String adminUrl; + String appTypeName; + switch (siteType) { + case 20: + adminUrl = "https://mp.websoft.top"; + appTypeName = "miniprogram"; + break; + case 30: + adminUrl = "https://app.websoft.top"; + appTypeName = "mobile"; + break; + case 40: + adminUrl = "https://api.websoft.top"; + appTypeName = "api"; + break; + case 50: + adminUrl = "https://oa.websoft.top"; + appTypeName = "internal"; + break; + case 10: + default: + adminUrl = "https://site.websoft.top"; + appTypeName = "web"; + break; + } + website.setAdminUrl(adminUrl); + // 若前端已传 appType 则优先使用,否则用 type 推导值 + if (StrUtil.isBlank(website.getAppType())) { + website.setAppType(appTypeName); + } + website.setVersion(10); + website.setExpirationTime(java.util.Date.from(LocalDateTime.now().plusMonths(1) + .atZone(java.time.ZoneId.systemDefault()).toInstant())); + website.setUserId(loginUser.getUserId()); + website.setDeveloper(loginUser.getNickname()); + website.setTenantId(loginUser.getTenantId()); + website.setTemplateId(loginUser.getTemplateId()); + website.setCompanyId(loginUser.getCompanyId()); + + // 初始化数据 + if(save(website)){ + // 插入网站设置记录 +// final CmsAppSetting setting = new CmsAppSetting(); +// setting.setAppId(website.getAppId()); +// setting.setCreateTime(DateUtil.date()); +// setting.setUpdateTime(DateUtil.date()); +// cmsAppSettingService.save(setting); + + // 将网站创建者的userId做为查询条件 10257(4716),10324(6978),10398(26564) + Integer websiteUserId = website.getTemplateId(); + + // 只有当templateId存在时才执行复制操作 + if (websiteUserId != null && websiteUserId > 0) { + // TODO 国际化 + final CmsLangLogParam cmsLangLogParam = new CmsLangLogParam(); + cmsLangLogParam.setWebsiteUserId(websiteUserId); + final List logs = cmsLangLogMapper.selectListAllRel(cmsLangLogParam); + logs.forEach(d->{ + d.setTenantId(loginUser.getTenantId()); + }); + cmsLangLogService.saveBatch(logs); + + // TODO 复制参数 + final CmsAppFieldParam param = new CmsAppFieldParam(); + param.setUserId(websiteUserId); + final List fields = cmsAppFieldMapper.selectListAllRel(param); + fields.forEach(d->{ + d.setTenantId(loginUser.getTenantId()); + }); + cmsAppFieldService.saveBatch(fields); + + // TODO 复制模型 + final CmsModelParam modelParam = new CmsModelParam(); + modelParam.setWebsiteUserId(websiteUserId); + final List models = cmsModelMapper.selectListAllRel(modelParam); + models.forEach(d->{ + d.setUserId(loginUser.getUserId()); + d.setTenantId(loginUser.getTenantId()); + }); + cmsModelService.saveBatch(models); + + // TODO 复制广告 + final CmsAdParam cmsAdParam = new CmsAdParam(); + cmsAdParam.setWebsiteUserId(websiteUserId); + final List ads = cmsAdMapper.selectListAllRel(cmsAdParam); + ads.forEach(d -> { + d.setUserId(loginUser.getUserId()); + d.setTenantId(loginUser.getTenantId()); + }); + cmsAdService.saveBatch(ads); + + // TODO 复制链接 + CmsLinkParam cmsLinkParam = new CmsLinkParam(); + cmsLinkParam.setWebsiteUserId(websiteUserId); + final List links = cmsLinkMapper.selectListAllRel(cmsLinkParam); + links.forEach(d -> { + d.setUserId(loginUser.getUserId()); + d.setTenantId(loginUser.getTenantId()); + }); + cmsLinkService.saveBatch(links); + + // TODO 复制栏目和文章、文章内容 + CmsNavigationParam cmsNavigationParam = new CmsNavigationParam(); + cmsNavigationParam.setWebsiteUserId(websiteUserId); + cmsNavigationParam.setParentId(0); + final List parents = cmsNavigationMapper.selectListAllRel(cmsNavigationParam); + parents.forEach(d -> { + Integer navigationId = d.getNavigationId(); + // 复制顶级栏目 + d.setTenantId(loginUser.getTenantId()); + d.setUserId(loginUser.getUserId()); + if (cmsNavigationService.save(d)) { + cmsNavigationService.saveAsync(d); + // 复制栏目文章 + CmsArticleParam cmsArticleParam = new CmsArticleParam(); + cmsArticleParam.setWebsiteUserId(websiteUserId); + cmsArticleParam.setCategoryId(navigationId); + final List articles = cmsArticleMapper.selectListAllRel(cmsArticleParam); + articles.forEach(a -> { + a.setCategoryId(d.getNavigationId()); + a.setUserId(loginUser.getUserId()); + a.setTenantId(loginUser.getTenantId()); + if (cmsArticleService.save(a)) { + final CmsArticleContent content = new CmsArticleContent(); + content.setArticleId(a.getArticleId()); + content.setContent(a.getContent()); + cmsArticleContentService.save(content); + } + }); + // 复制子栏目 + cmsNavigationParam.setParentId(navigationId); + final List navigations = cmsNavigationMapper.selectListAllRel(cmsNavigationParam); + navigations.forEach(c -> { + cmsArticleParam.setCategoryId(c.getNavigationId()); + c.setParentId(d.getNavigationId()); + c.setTenantId(loginUser.getTenantId()); + c.setUserId(loginUser.getUserId()); + cmsNavigationService.save(c); + cmsNavigationService.saveAsync(c); + // 复制子栏目文章 + final List articles2 = cmsArticleMapper.selectListAllRel(cmsArticleParam); + articles2.forEach(a2 -> { + a2.setCategoryId(c.getNavigationId()); + a2.setParentId(c.getParentId()); + a2.setUserId(loginUser.getUserId()); + a2.setTenantId(loginUser.getTenantId()); + if (cmsArticleService.save(a2)) { + final CmsArticleContent content = new CmsArticleContent(); + content.setArticleId(a2.getArticleId()); + content.setContent(a2.getContent()); + cmsArticleContentService.save(content); + } + }); + }); + } + }); + } else { + log.warn("没有有效的模板ID,跳过复制操作"); + } + + // 新增项目 + final Project project = new Project(); + project.setUserId(website.getUserId()); + project.setAppName(website.getAppName()); + project.setAppIcon(website.getAppIcon()); + project.setAppCode(website.getAppCode()); + project.setAdminUrl(website.getAdminUrl()); + project.setRenewMoney(website.getPrice()); + project.setAppId(website.getAppId()); + project.setAdminUrl(website.getAdminUrl()); + project.setAppType(website.getAppType()); + project.setAppIcon(website.getWebsiteLogo()); + project.setAppUrl(website.getDomain()); + project.setCompanyId(website.getUserId()); + project.setTenantId(5); + projectService.save(project); + } + return website; + } + + @Override + public CmsApp getByIdRelAll(Integer id) { + return cmsAppMapper.getByIdRelAll(id); + } + + @Override + public boolean updateByIdAll(CmsApp cmsApp) { + return baseMapper.updateByIdAll(cmsApp); + } + + @Override + public boolean removeByIdAll(Integer id) { + return baseMapper.removeByIdAll(id); + } + + @Override + public CmsApp getByTenantId(Integer tenantId) { + return baseMapper.getByTenantId(tenantId); + } + + @Override + public ShopVo getSiteInfo(Integer tenantId) { + // 参数验证 + if (ObjectUtil.isEmpty(tenantId)) { + throw new IllegalArgumentException("租户ID不能为空"); + } + + // 尝试从缓存获取 + String cacheKey = SITE_INFO_KEY_PREFIX + tenantId; + String siteInfo = redisUtil.get(cacheKey); + if (StrUtil.isNotBlank(siteInfo)) { + log.info("从缓存获取网站信息,租户ID: {}", tenantId); + try { + // 兼容历史缓存:JSON "null" 会被解析为 null;此时应视为未命中并回源数据库。 + ShopVo cacheVo = JSONUtil.parseObject(siteInfo, ShopVo.class); + if (cacheVo != null) { + return cacheVo; + } + log.warn("网站信息缓存命中但内容为空(null),清理缓存后回源数据库,租户ID: {}", tenantId); + redisUtil.delete(cacheKey); + } catch (Exception e) { + log.warn("缓存解析失败,清理缓存后从数据库重新获取: {}", e.getMessage()); + redisUtil.delete(cacheKey); + } + } + + // 从数据库获取站点信息 + CmsApp website = getWebsiteFromDatabase(tenantId); + + + if (website == null) { + throw new RuntimeException("请先创建站点"); + } + + // 构建完整的网站信息 + buildCompleteWebsiteInfo(website); + + // 处理过期时间 + CmsAppServiceImplHelper.processExpirationTime(website); + + // 转换为VO对象 + ShopVo websiteVO = CmsAppServiceImplHelper.convertToVO(website); + + // 缓存结果 + try { + redisUtil.set(cacheKey, websiteVO, 1L, TimeUnit.DAYS); + } catch (Exception e) { + log.warn("缓存网站信息失败: {}", e.getMessage()); + } + + log.info("获取网站信息成功,网站ID: {}, 租户ID: {}", website.getAppId(), tenantId); + return websiteVO; + } + + @Override + public void clearSiteInfoCache(Integer tenantId) { + if (tenantId != null) { + String cacheKey = SITE_INFO_KEY_PREFIX + tenantId; + redisUtil.delete(cacheKey); + log.info("清除网站信息缓存成功,租户ID: {}", tenantId); + } + } + + // ─── 发布管理 ────────────────────────────────────────────────────────── + + @Override + public void submitReview(Integer appId, String priceType, Integer price, + String subscriptionPeriod, String appDescription, + String detailDescription, String screenshots, Integer userId) { + CmsApp website = getById(appId); + if (website == null) throw new RuntimeException("应用不存在"); + // 只有开发中 / 审核未通过 才能提交 + String cur = website.getPublishStatus(); + if (!"developing".equals(cur) && !"rejected".equals(cur) && cur != null) { + throw new RuntimeException("当前状态不允许提交审核"); + } + CmsApp update = new CmsApp(); + update.setAppId(appId); + update.setPublishStatus("pending_review"); + update.setPriceType(priceType != null ? priceType : "free"); + update.setPrice(price != null ? new java.math.BigDecimal(price).movePointLeft(2) : java.math.BigDecimal.ZERO); + update.setSubscriptionPeriod(subscriptionPeriod); + update.setAppDescription(appDescription); + update.setDetailDescription(detailDescription); + update.setScreenshots(screenshots); + update.setPublishApplyTime(new java.util.Date()); + update.setRejectReason(null); + updateById(update); + } + + @Override + public void withdrawReview(Integer appId, Integer userId) { + CmsApp website = getById(appId); + if (website == null) throw new RuntimeException("应用不存在"); + if (!"pending_review".equals(website.getPublishStatus())) { + throw new RuntimeException("当前状态不是待审核,无法撤回"); + } + CmsApp update = new CmsApp(); + update.setAppId(appId); + update.setPublishStatus("developing"); + updateById(update); + } + + @Override + public void unpublish(Integer appId, Integer userId) { + CmsApp website = getById(appId); + if (website == null) throw new RuntimeException("应用不存在"); + if (!"published".equals(website.getPublishStatus())) { + throw new RuntimeException("应用未上架,无法下架"); + } + CmsApp update = new CmsApp(); + update.setAppId(appId); + update.setPublishStatus("deprecated"); + updateById(update); + } + + @Override + public void approveReview(Integer appId, Integer reviewerId) { + CmsApp website = getById(appId); + if (website == null) throw new RuntimeException("应用不存在"); + if (!"pending_review".equals(website.getPublishStatus())) { + throw new RuntimeException("当前状态不是待审核"); + } + CmsApp update = new CmsApp(); + update.setAppId(appId); + update.setPublishStatus("published"); + update.setMarket(true); + update.setReviewerId(reviewerId); + update.setReviewTime(new java.util.Date()); + update.setPublishTime(new java.util.Date()); + update.setRejectReason(null); + updateById(update); + } + + @Override + public void rejectReview(Integer appId, String rejectReason, Integer reviewerId) { + CmsApp website = getById(appId); + if (website == null) throw new RuntimeException("应用不存在"); + if (!"pending_review".equals(website.getPublishStatus())) { + throw new RuntimeException("当前状态不是待审核"); + } + CmsApp update = new CmsApp(); + update.setAppId(appId); + update.setPublishStatus("rejected"); + update.setRejectReason(rejectReason); + update.setReviewerId(reviewerId); + update.setReviewTime(new java.util.Date()); + updateById(update); + } + + @Override + public PageResult pageReviews(CmsAppParam param) { + com.baomidou.mybatisplus.extension.plugins.pagination.Page page = + new com.baomidou.mybatisplus.extension.plugins.pagination.Page<>(param.getPage() != null ? param.getPage() : 1, param.getLimit() != null && param.getLimit() > 0 ? param.getLimit() : 20); + com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper wrapper = + new com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper() + .eq(CmsApp::getDeleted, 0) + .isNotNull(CmsApp::getPublishStatus) + .ne(CmsApp::getPublishStatus, "developing") + .eq(cn.hutool.core.util.ObjectUtil.isNotEmpty(param.getPublishStatus()), + CmsApp::getPublishStatus, param.getPublishStatus()) + .and(cn.hutool.core.util.ObjectUtil.isNotEmpty(param.getKeywords()), q -> + q.like(CmsApp::getAppName, param.getKeywords()) + .or().like(CmsApp::getAppCode, param.getKeywords())) + .last("ORDER BY FIELD(publish_status,'pending_review','rejected','published','deprecated'), publish_apply_time DESC"); + baseMapper.selectPage(page, wrapper); + return new com.gxwebsoft.common.core.web.PageResult<>(page.getRecords(), page.getTotal()); + } + + + private CmsApp getWebsiteFromDatabase(Integer tenantId) { + return getByTenantId(tenantId); + } + + /** + * 构建完整的网站信息 + */ + private void buildCompleteWebsiteInfo(CmsApp website) { + // 设置网站状态 + CmsAppServiceImplHelper.setWebsiteStatus(website); + + // 设置网站配置 + CmsAppServiceImplHelper.setWebsiteConfig(website); + + // 设置网站导航 + setWebsiteNavigation(website); + + // 设置网站设置信息 + CmsAppServiceImplHelper.setWebsiteSetting(website); + + // 设置服务器时间信息 + CmsAppServiceImplHelper.setServerTimeInfo(website); + } + + /** + * 设置网站导航 + */ + private void setWebsiteNavigation(CmsApp website) { + // 获取顶部导航 + CmsNavigationParam navigationParam = new CmsNavigationParam(); + navigationParam.setHide(0); + navigationParam.setTop(0); + navigationParam.setBottom(null); + List topNavs = cmsNavigationService.listRel(navigationParam); + website.setTopNavs(topNavs); + + // 获取底部导航 + navigationParam.setTop(null); + navigationParam.setBottom(0); + List bottomNavs = cmsNavigationService.listRel(navigationParam); + website.setBottomNavs(bottomNavs); + } + + /** + * 生成全局唯一的 appCode。 + *

以 baseCode 为基础,若已存在则追加 -2、-3 … 直到找到空闲值。

+ * + * @param baseCode 期望的 code 前缀,如 "site-10398" + * @return 全局唯一的 appCode + */ + private String generateUniqueCode(String baseCode) { + String candidate = baseCode; + int suffix = 2; + while (count(new com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper() + .eq(CmsApp::getAppCode, candidate)) > 0) { + candidate = baseCode + "-" + suffix; + suffix++; + } + return candidate; + } +} diff --git a/src/main/java/com/gxwebsoft/cms/service/impl/CmsAppServiceImplHelper.java b/src/main/java/com/gxwebsoft/cms/service/impl/CmsAppServiceImplHelper.java new file mode 100644 index 0000000..64f2bc0 --- /dev/null +++ b/src/main/java/com/gxwebsoft/cms/service/impl/CmsAppServiceImplHelper.java @@ -0,0 +1,239 @@ +package com.gxwebsoft.cms.service.impl; + +import com.gxwebsoft.cms.entity.CmsNavigation; +import com.gxwebsoft.cms.entity.CmsApp; +import com.gxwebsoft.shop.vo.MenuVo; +import com.gxwebsoft.shop.vo.ShopVo; + +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.time.temporal.ChronoUnit; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.stream.Collectors; + +/** + * CmsAppServiceImpl 辅助方法 + * 包含转换和处理逻辑 + */ +public class CmsAppServiceImplHelper { + + /** + * 处理过期时间,只处理真正需要的字段 + */ + public static void processExpirationTime(CmsApp website) { + if (website.getExpirationTime() != null) { + LocalDateTime now = LocalDateTime.now(); + Date expirationTimeDate = website.getExpirationTime(); + + // 将Date转换为LocalDateTime进行计算 + LocalDateTime expirationTime = expirationTimeDate.toInstant() + .atZone(java.time.ZoneId.systemDefault()) + .toLocalDateTime(); + + // 计算是否即将过期(30天内过期) + LocalDateTime thirtyDaysLater = now.plusDays(30); + website.setSoon(expirationTime.isBefore(thirtyDaysLater) ? 1 : 0); + + // 计算是否已过期 + website.setExpired(expirationTime.isBefore(now) ? -1 : 1); + + // 计算剩余天数 + long daysBetween = ChronoUnit.DAYS.between(now, expirationTime); + website.setExpiredDays(daysBetween); + } else { + // 没有过期时间的默认值 + website.setSoon(0); + website.setExpired(1); + website.setExpiredDays(0L); + } + } + + /** + * 将实体对象转换为VO对象 + */ + public static ShopVo convertToVO(CmsApp website) { + ShopVo vo = new ShopVo(); + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); + + // 基本信息 + vo.setAppId(website.getTenantId()); + vo.setAppName(website.getTenantName()); + vo.setTitle(website.getAppName()); + vo.setKeywords(website.getKeywords()); + vo.setDescription(website.getComments()); + vo.setLogo(website.getWebsiteLogo()); + vo.setMpQrCode(website.getWebsiteDarkLogo()); + vo.setDomain(website.getDomain()); + vo.setAdminUrl(website.getAdminUrl()); + vo.setApiUrl(website.getApiUrl()); + vo.setRunning(website.getRunning()); + vo.setVersion(website.getVersion()); + if (website.getCreateTime() != null) { + // 将Date转换为LocalDateTime后格式化 + LocalDateTime createTime = website.getCreateTime().toInstant() + .atZone(java.time.ZoneId.systemDefault()) + .toLocalDateTime(); + vo.setCreateTime(createTime.format(formatter)); + } + + // 时间字段 - 格式化为字符串 + if (website.getExpirationTime() != null) { + // 将Date转换为LocalDateTime后格式化 + LocalDateTime expirationTime = website.getExpirationTime().toInstant() + .atZone(java.time.ZoneId.systemDefault()) + .toLocalDateTime(); + vo.setExpirationTime(expirationTime.format(formatter)); + } + + // 过期相关信息 + vo.setExpired(website.getExpired()); + vo.setExpiredDays(website.getExpiredDays()); + vo.setSoon(website.getSoon()); + + // 状态信息 + vo.setStatusIcon(website.getStatusIcon()); + vo.setStatusText(website.getStatusText()); + + // 复杂对象 + vo.setConfig(website.getConfig()); + vo.setServerTime(website.getServerTime()); + vo.setSetting(website.getSetting()); // CmsAppSetting对象可以直接设置给Object类型 + + // 导航信息 + vo.setTopNavs(convertNavigationToVO(website.getTopNavs())); + vo.setBottomNavs(convertNavigationToVO(website.getBottomNavs())); + + return vo; + } + + /** + * 安全转换 target 字段为整数 + * + * @param target 字符串类型的 target 值 + * @return 对应的整数值 + */ + private static Integer convertTargetToInteger(String target) { + if (target == null) { + return 0; // 默认值:当前窗口 + } + + switch (target.toLowerCase()) { + case "_self": + return 0; // 当前窗口 + case "_blank": + return 1; // 新窗口 + default: + // 如果是数字字符串,尝试直接转换 + try { + return Integer.valueOf(target); + } catch (NumberFormatException e) { + // 转换失败时返回默认值 + return 0; + } + } + } + + /** + * 转换导航列表为VO + * 整理导航栏目录结构(ShopInfo) + */ + public static List convertNavigationToVO(List navigations) { + if (navigations == null) { + return null; + } + + return navigations.stream().map(nav -> { + MenuVo navVO = new MenuVo(); + navVO.setNavigationId(nav.getNavigationId()); + navVO.setTitle(nav.getTitle()); + navVO.setPath(nav.getPath()); + navVO.setIcon(nav.getIcon()); + navVO.setColor(nav.getColor()); + navVO.setParentId(nav.getParentId()); + navVO.setSort(nav.getSortNumber()); + navVO.setHide(nav.getHide()); + navVO.setTop(nav.getTop()); + navVO.setPath(nav.getPath()); + navVO.setTarget(convertTargetToInteger(nav.getTarget())); + navVO.setModel(nav.getModel()); + + // 递归处理子导航 + if (nav.getChildren() != null) { + navVO.setChildren(convertNavigationToVO(nav.getChildren())); + } + + return navVO; + }).collect(Collectors.toList()); + } + + /** + * 设置网站状态 + */ + public static void setWebsiteStatus(CmsApp website) { + if (website.getRunning() != null) { + switch (website.getRunning()) { + case 0: + website.setStatusIcon("🔴"); + website.setStatusText("未开通"); + break; + case 1: + website.setStatusIcon("🟢"); + website.setStatusText("正常运行"); + break; + case 2: + website.setStatusIcon("🟡"); + website.setStatusText("维护中"); + break; + case 3: + website.setStatusIcon("🔴"); + website.setStatusText("违规关停"); + break; + default: + website.setStatusIcon("❓"); + website.setStatusText("未知状态"); + } + } + } + + /** + * 设置网站配置 + */ + public static void setWebsiteConfig(CmsApp website) { + HashMap config = new HashMap<>(); + config.put("appName", website.getAppName()); + config.put("websiteComments", website.getComments()); + config.put("websiteTitle", website.getAppName()); + config.put("websiteKeywords", website.getKeywords()); + config.put("websiteDescription", website.getContent()); // 使用 content 字段作为描述 + config.put("websiteLogo", website.getWebsiteLogo()); + config.put("appIcon", website.getAppIcon()); + config.put("domain", website.getDomain()); + website.setConfig(config); + } + + /** + * 设置服务器时间信息 + */ + public static void setServerTimeInfo(CmsApp website) { + HashMap serverTime = new HashMap<>(); + LocalDateTime now = LocalDateTime.now(); + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); + + serverTime.put("currentTime", now.format(formatter)); + serverTime.put("timestamp", System.currentTimeMillis()); + serverTime.put("timezone", "Asia/Shanghai"); + + website.setServerTime(serverTime); + } + + /** + * 设置网站设置信息 + */ + public static void setWebsiteSetting(CmsApp website) { + // 这里可以根据需要设置网站的其他设置信息 + // 暂时设置为null,因为setting字段类型是CmsAppSetting而不是HashMap + website.setSetting(null); + } +} diff --git a/src/main/java/com/gxwebsoft/cms/service/impl/CmsAppSettingServiceImpl.java b/src/main/java/com/gxwebsoft/cms/service/impl/CmsAppSettingServiceImpl.java new file mode 100644 index 0000000..d59f649 --- /dev/null +++ b/src/main/java/com/gxwebsoft/cms/service/impl/CmsAppSettingServiceImpl.java @@ -0,0 +1,47 @@ +package com.gxwebsoft.cms.service.impl; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.gxwebsoft.cms.mapper.CmsAppSettingMapper; +import com.gxwebsoft.cms.service.CmsAppSettingService; +import com.gxwebsoft.cms.entity.CmsAppSetting; +import com.gxwebsoft.cms.param.CmsAppSettingParam; +import com.gxwebsoft.common.core.web.PageParam; +import com.gxwebsoft.common.core.web.PageResult; +import org.springframework.stereotype.Service; + +import java.util.List; + +/** + * 网站设置Service实现 + * + * @author 科技小王子 + * @since 2025-02-19 01:35:44 + */ +@Service +public class CmsAppSettingServiceImpl extends ServiceImpl implements CmsAppSettingService { + + @Override + public PageResult pageRel(CmsAppSettingParam param) { + PageParam page = new PageParam<>(param); + page.setDefaultOrder("sort_number asc, create_time desc"); + List list = baseMapper.selectPageRel(page, param); + return new PageResult<>(list, page.getTotal()); + } + + @Override + public List listRel(CmsAppSettingParam param) { + List list = baseMapper.selectListRel(param); + // 排序 + PageParam page = new PageParam<>(); + page.setDefaultOrder("sort_number asc, create_time desc"); + return page.sortRecords(list); + } + + @Override + public CmsAppSetting getByIdRel(Integer id) { + CmsAppSettingParam param = new CmsAppSettingParam(); + param.setId(id); + return param.getOne(baseMapper.selectListRel(param)); + } + +} diff --git a/src/main/java/com/gxwebsoft/cms/sql/cms_app_publish.sql b/src/main/java/com/gxwebsoft/cms/sql/cms_app_publish.sql new file mode 100644 index 0000000..df3fa87 --- /dev/null +++ b/src/main/java/com/gxwebsoft/cms/sql/cms_app_publish.sql @@ -0,0 +1,17 @@ +-- 为 cms_app 表新增发布管理相关字段 +-- 执行前请确认已连接正确的数据库 + +ALTER TABLE `cms_app` + ADD COLUMN `publish_status` VARCHAR(20) NULL DEFAULT 'developing' COMMENT '发布状态: developing开发中 pending_review待审核 published已上架 rejected审核未通过 deprecated已下架' AFTER `market`, + ADD COLUMN `price_type` VARCHAR(20) NULL COMMENT '定价模式: free免费 one_time一次性 subscription订阅' AFTER `publish_status`, + ADD COLUMN `subscription_period` VARCHAR(10) NULL COMMENT '订阅周期: month按月 year按年' AFTER `price_type`, + ADD COLUMN `app_description` VARCHAR(500) NULL COMMENT '应用简介(市场展示用)' AFTER `subscription_period`, + ADD COLUMN `detail_description` TEXT NULL COMMENT '详细说明(富文本)' AFTER `app_description`, + ADD COLUMN `screenshots` TEXT NULL COMMENT '应用截图(JSON数组字符串)' AFTER `detail_description`, + ADD COLUMN `install_count` INT NULL DEFAULT 0 COMMENT '安装/使用次数' AFTER `screenshots`, + ADD COLUMN `rating` DECIMAL(3,1) NULL DEFAULT 0.0 COMMENT '评分(1-5)' AFTER `install_count`, + ADD COLUMN `reject_reason` VARCHAR(500) NULL COMMENT '审核拒绝原因' AFTER `rating`, + ADD COLUMN `publish_apply_time` DATETIME NULL COMMENT '提交审核时间' AFTER `reject_reason`, + ADD COLUMN `publish_time` DATETIME NULL COMMENT '正式发布上架时间' AFTER `publish_apply_time`, + ADD COLUMN `reviewer_id` INT NULL COMMENT '审核人用户ID' AFTER `publish_time`, + ADD COLUMN `review_time` DATETIME NULL COMMENT '审核操作时间' AFTER `reviewer_id`; diff --git a/src/main/java/com/gxwebsoft/dormitory/controller/DormitoryApplyController.java b/src/main/java/com/gxwebsoft/dormitory/controller/DormitoryApplyController.java deleted file mode 100644 index b34af63..0000000 --- a/src/main/java/com/gxwebsoft/dormitory/controller/DormitoryApplyController.java +++ /dev/null @@ -1,128 +0,0 @@ -package com.gxwebsoft.dormitory.controller; - -import com.gxwebsoft.common.core.annotation.OperationLog; -import com.gxwebsoft.common.core.web.ApiResult; -import com.gxwebsoft.common.core.web.BaseController; -import com.gxwebsoft.common.core.web.BatchParam; -import com.gxwebsoft.common.core.web.PageResult; -import com.gxwebsoft.common.system.entity.User; -import com.gxwebsoft.dormitory.entity.DormitoryApply; -import com.gxwebsoft.dormitory.param.DormitoryApplyParam; -import com.gxwebsoft.dormitory.service.DormitoryApplyService; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.tags.Tag; -import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.web.bind.annotation.*; - -import javax.annotation.Resource; -import java.util.List; - -/** - * 审批管理控制器 - * - * @author 科技小王子 - * @since 2025-10-03 15:54:30 - */ -@Tag(name = "审批管理管理") -@RestController -@RequestMapping("/api/dormitory/dormitory-apply") -public class DormitoryApplyController extends BaseController { - @Resource - private DormitoryApplyService dormitoryApplyService; - - @PreAuthorize("hasAuthority('dormitory:dormitoryApply:list')") - @Operation(summary = "分页查询审批管理") - @GetMapping("/page") - public ApiResult> page(DormitoryApplyParam param) { - // 使用关联查询 - return success(dormitoryApplyService.pageRel(param)); - } - - @PreAuthorize("hasAuthority('dormitory:dormitoryApply:list')") - @Operation(summary = "查询全部审批管理") - @GetMapping() - public ApiResult> list(DormitoryApplyParam param) { - // 使用关联查询 - return success(dormitoryApplyService.listRel(param)); - } - - @PreAuthorize("hasAuthority('dormitory:dormitoryApply:list')") - @Operation(summary = "根据id查询审批管理") - @GetMapping("/{id}") - public ApiResult get(@PathVariable("id") Integer id) { - // 使用关联查询 - return success(dormitoryApplyService.getByIdRel(id)); - } - - @PreAuthorize("hasAuthority('dormitory:dormitoryApply:save')") - @OperationLog - @Operation(summary = "添加审批管理") - @PostMapping() - public ApiResult save(@RequestBody DormitoryApply dormitoryApply) { - // 记录当前登录用户id - User loginUser = getLoginUser(); - if (loginUser != null) { - dormitoryApply.setUserId(loginUser.getUserId()); - } - if (dormitoryApplyService.save(dormitoryApply)) { - return success("添加成功"); - } - return fail("添加失败"); - } - - @PreAuthorize("hasAuthority('dormitory:dormitoryApply:update')") - @OperationLog - @Operation(summary = "修改审批管理") - @PutMapping() - public ApiResult update(@RequestBody DormitoryApply dormitoryApply) { - if (dormitoryApplyService.updateById(dormitoryApply)) { - return success("修改成功"); - } - return fail("修改失败"); - } - - @PreAuthorize("hasAuthority('dormitory:dormitoryApply:remove')") - @OperationLog - @Operation(summary = "删除审批管理") - @DeleteMapping("/{id}") - public ApiResult remove(@PathVariable("id") Integer id) { - if (dormitoryApplyService.removeById(id)) { - return success("删除成功"); - } - return fail("删除失败"); - } - - @PreAuthorize("hasAuthority('dormitory:dormitoryApply:save')") - @OperationLog - @Operation(summary = "批量添加审批管理") - @PostMapping("/batch") - public ApiResult saveBatch(@RequestBody List list) { - if (dormitoryApplyService.saveBatch(list)) { - return success("添加成功"); - } - return fail("添加失败"); - } - - @PreAuthorize("hasAuthority('dormitory:dormitoryApply:update')") - @OperationLog - @Operation(summary = "批量修改审批管理") - @PutMapping("/batch") - public ApiResult removeBatch(@RequestBody BatchParam batchParam) { - if (batchParam.update(dormitoryApplyService, "id")) { - return success("修改成功"); - } - return fail("修改失败"); - } - - @PreAuthorize("hasAuthority('dormitory:dormitoryApply:remove')") - @OperationLog - @Operation(summary = "批量删除审批管理") - @DeleteMapping("/batch") - public ApiResult removeBatch(@RequestBody List ids) { - if (dormitoryApplyService.removeByIds(ids)) { - return success("删除成功"); - } - return fail("删除失败"); - } - -} \ No newline at end of file diff --git a/src/main/java/com/gxwebsoft/dormitory/controller/DormitoryBedController.java b/src/main/java/com/gxwebsoft/dormitory/controller/DormitoryBedController.java deleted file mode 100644 index c90f412..0000000 --- a/src/main/java/com/gxwebsoft/dormitory/controller/DormitoryBedController.java +++ /dev/null @@ -1,126 +0,0 @@ -package com.gxwebsoft.dormitory.controller; - -import com.gxwebsoft.common.core.web.BaseController; -import com.gxwebsoft.dormitory.service.DormitoryBedService; -import com.gxwebsoft.dormitory.entity.DormitoryBed; -import com.gxwebsoft.dormitory.param.DormitoryBedParam; -import com.gxwebsoft.common.core.web.ApiResult; -import com.gxwebsoft.common.core.web.PageResult; -import com.gxwebsoft.common.core.web.PageParam; -import com.gxwebsoft.common.core.web.BatchParam; -import com.gxwebsoft.common.core.annotation.OperationLog; -import com.gxwebsoft.common.system.entity.User; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.tags.Tag; -import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.web.bind.annotation.*; - -import javax.annotation.Resource; -import java.util.List; - -/** - * 宿舍床位控制器 - * - * @author 科技小王子 - * @since 2025-10-03 10:49:27 - */ -@Tag(name = "宿舍床位管理") -@RestController -@RequestMapping("/api/dormitory/dormitory-bed") -public class DormitoryBedController extends BaseController { - @Resource - private DormitoryBedService dormitoryBedService; - - @Operation(summary = "分页查询宿舍床位") - @GetMapping("/page") - public ApiResult> page(DormitoryBedParam param) { - // 使用关联查询 - return success(dormitoryBedService.pageRel(param)); - } - - @Operation(summary = "查询全部宿舍床位") - @GetMapping() - public ApiResult> list(DormitoryBedParam param) { - // 使用关联查询 - return success(dormitoryBedService.listRel(param)); - } - - @Operation(summary = "根据id查询宿舍床位") - @GetMapping("/{id}") - public ApiResult get(@PathVariable("id") Integer id) { - // 使用关联查询 - return success(dormitoryBedService.getByIdRel(id)); - } - - @PreAuthorize("hasAuthority('dormitory:dormitoryBed:save')") - @OperationLog - @Operation(summary = "添加宿舍床位") - @PostMapping() - public ApiResult save(@RequestBody DormitoryBed dormitoryBed) { - // 记录当前登录用户id - User loginUser = getLoginUser(); - if (loginUser != null) { - // dormitoryBed实体类没有userId字段,所以不设置 - } - if (dormitoryBedService.save(dormitoryBed)) { - return success("添加成功"); - } - return fail("添加失败"); - } - - @PreAuthorize("hasAuthority('dormitory:dormitoryBed:update')") - @OperationLog - @Operation(summary = "修改宿舍床位") - @PutMapping() - public ApiResult update(@RequestBody DormitoryBed dormitoryBed) { - if (dormitoryBedService.updateById(dormitoryBed)) { - return success("修改成功"); - } - return fail("修改失败"); - } - - @PreAuthorize("hasAuthority('dormitory:dormitoryBed:remove')") - @OperationLog - @Operation(summary = "删除宿舍床位") - @DeleteMapping("/{id}") - public ApiResult remove(@PathVariable("id") Integer id) { - if (dormitoryBedService.removeById(id)) { - return success("删除成功"); - } - return fail("删除失败"); - } - - @PreAuthorize("hasAuthority('dormitory:dormitoryBed:save')") - @OperationLog - @Operation(summary = "批量添加宿舍床位") - @PostMapping("/batch") - public ApiResult saveBatch(@RequestBody List list) { - if (dormitoryBedService.saveBatch(list)) { - return success("添加成功"); - } - return fail("添加失败"); - } - - @PreAuthorize("hasAuthority('dormitory:dormitoryBed:update')") - @OperationLog - @Operation(summary = "批量修改宿舍床位") - @PutMapping("/batch") - public ApiResult removeBatch(@RequestBody BatchParam batchParam) { - if (batchParam.update(dormitoryBedService, "id")) { - return success("修改成功"); - } - return fail("修改失败"); - } - - @PreAuthorize("hasAuthority('dormitory:dormitoryBed:remove')") - @OperationLog - @Operation(summary = "批量删除宿舍床位") - @DeleteMapping("/batch") - public ApiResult removeBatch(@RequestBody List ids) { - if (dormitoryBedService.removeByIds(ids)) { - return success("删除成功"); - } - return fail("删除失败"); - } - -} diff --git a/src/main/java/com/gxwebsoft/dormitory/controller/DormitoryBuildingController.java b/src/main/java/com/gxwebsoft/dormitory/controller/DormitoryBuildingController.java deleted file mode 100644 index ab84fbf..0000000 --- a/src/main/java/com/gxwebsoft/dormitory/controller/DormitoryBuildingController.java +++ /dev/null @@ -1,126 +0,0 @@ -package com.gxwebsoft.dormitory.controller; - -import com.gxwebsoft.common.core.web.BaseController; -import com.gxwebsoft.dormitory.service.DormitoryBuildingService; -import com.gxwebsoft.dormitory.entity.DormitoryBuilding; -import com.gxwebsoft.dormitory.param.DormitoryBuildingParam; -import com.gxwebsoft.common.core.web.ApiResult; -import com.gxwebsoft.common.core.web.PageResult; -import com.gxwebsoft.common.core.web.PageParam; -import com.gxwebsoft.common.core.web.BatchParam; -import com.gxwebsoft.common.core.annotation.OperationLog; -import com.gxwebsoft.common.system.entity.User; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.tags.Tag; -import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.web.bind.annotation.*; - -import javax.annotation.Resource; -import java.util.List; - -/** - * 宿舍楼栋控制器 - * - * @author 科技小王子 - * @since 2025-10-03 10:49:27 - */ -@Tag(name = "宿舍楼栋管理") -@RestController -@RequestMapping("/api/dormitory/dormitory-building") -public class DormitoryBuildingController extends BaseController { - @Resource - private DormitoryBuildingService dormitoryBuildingService; - - @Operation(summary = "分页查询宿舍楼栋") - @GetMapping("/page") - public ApiResult> page(DormitoryBuildingParam param) { - // 使用关联查询 - return success(dormitoryBuildingService.pageRel(param)); - } - - @Operation(summary = "查询全部宿舍楼栋") - @GetMapping() - public ApiResult> list(DormitoryBuildingParam param) { - // 使用关联查询 - return success(dormitoryBuildingService.listRel(param)); - } - - @Operation(summary = "根据id查询宿舍楼栋") - @GetMapping("/{id}") - public ApiResult get(@PathVariable("id") Integer id) { - // 使用关联查询 - return success(dormitoryBuildingService.getByIdRel(id)); - } - - @PreAuthorize("hasAuthority('dormitory:dormitoryBuilding:save')") - @OperationLog - @Operation(summary = "添加宿舍楼栋") - @PostMapping() - public ApiResult save(@RequestBody DormitoryBuilding dormitoryBuilding) { - // 记录当前登录用户id - User loginUser = getLoginUser(); - if (loginUser != null) { - // dormitoryBuilding实体类没有userId字段,所以不设置 - } - if (dormitoryBuildingService.save(dormitoryBuilding)) { - return success("添加成功"); - } - return fail("添加失败"); - } - - @PreAuthorize("hasAuthority('dormitory:dormitoryBuilding:update')") - @OperationLog - @Operation(summary = "修改宿舍楼栋") - @PutMapping() - public ApiResult update(@RequestBody DormitoryBuilding dormitoryBuilding) { - if (dormitoryBuildingService.updateById(dormitoryBuilding)) { - return success("修改成功"); - } - return fail("修改失败"); - } - - @PreAuthorize("hasAuthority('dormitory:dormitoryBuilding:remove')") - @OperationLog - @Operation(summary = "删除宿舍楼栋") - @DeleteMapping("/{id}") - public ApiResult remove(@PathVariable("id") Integer id) { - if (dormitoryBuildingService.removeById(id)) { - return success("删除成功"); - } - return fail("删除失败"); - } - - @PreAuthorize("hasAuthority('dormitory:dormitoryBuilding:save')") - @OperationLog - @Operation(summary = "批量添加宿舍楼栋") - @PostMapping("/batch") - public ApiResult saveBatch(@RequestBody List list) { - if (dormitoryBuildingService.saveBatch(list)) { - return success("添加成功"); - } - return fail("添加失败"); - } - - @PreAuthorize("hasAuthority('dormitory:dormitoryBuilding:update')") - @OperationLog - @Operation(summary = "批量修改宿舍楼栋") - @PutMapping("/batch") - public ApiResult removeBatch(@RequestBody BatchParam batchParam) { - if (batchParam.update(dormitoryBuildingService, "id")) { - return success("修改成功"); - } - return fail("修改失败"); - } - - @PreAuthorize("hasAuthority('dormitory:dormitoryBuilding:remove')") - @OperationLog - @Operation(summary = "批量删除宿舍楼栋") - @DeleteMapping("/batch") - public ApiResult removeBatch(@RequestBody List ids) { - if (dormitoryBuildingService.removeByIds(ids)) { - return success("删除成功"); - } - return fail("删除失败"); - } - -} diff --git a/src/main/java/com/gxwebsoft/dormitory/controller/DormitoryFloorController.java b/src/main/java/com/gxwebsoft/dormitory/controller/DormitoryFloorController.java deleted file mode 100644 index 2d16bda..0000000 --- a/src/main/java/com/gxwebsoft/dormitory/controller/DormitoryFloorController.java +++ /dev/null @@ -1,126 +0,0 @@ -package com.gxwebsoft.dormitory.controller; - -import com.gxwebsoft.common.core.web.BaseController; -import com.gxwebsoft.dormitory.service.DormitoryFloorService; -import com.gxwebsoft.dormitory.entity.DormitoryFloor; -import com.gxwebsoft.dormitory.param.DormitoryFloorParam; -import com.gxwebsoft.common.core.web.ApiResult; -import com.gxwebsoft.common.core.web.PageResult; -import com.gxwebsoft.common.core.web.PageParam; -import com.gxwebsoft.common.core.web.BatchParam; -import com.gxwebsoft.common.core.annotation.OperationLog; -import com.gxwebsoft.common.system.entity.User; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.tags.Tag; -import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.web.bind.annotation.*; - -import javax.annotation.Resource; -import java.util.List; - -/** - * 宿舍楼层控制器 - * - * @author 科技小王子 - * @since 2025-10-03 10:49:27 - */ -@Tag(name = "宿舍楼层管理") -@RestController -@RequestMapping("/api/dormitory/dormitory-floor") -public class DormitoryFloorController extends BaseController { - @Resource - private DormitoryFloorService dormitoryFloorService; - - @Operation(summary = "分页查询宿舍楼层") - @GetMapping("/page") - public ApiResult> page(DormitoryFloorParam param) { - // 使用关联查询 - return success(dormitoryFloorService.pageRel(param)); - } - - @Operation(summary = "查询全部宿舍楼层") - @GetMapping() - public ApiResult> list(DormitoryFloorParam param) { - // 使用关联查询 - return success(dormitoryFloorService.listRel(param)); - } - - @Operation(summary = "根据id查询宿舍楼层") - @GetMapping("/{id}") - public ApiResult get(@PathVariable("id") Integer id) { - // 使用关联查询 - return success(dormitoryFloorService.getByIdRel(id)); - } - - @PreAuthorize("hasAuthority('dormitory:dormitoryFloor:save')") - @OperationLog - @Operation(summary = "添加宿舍楼层") - @PostMapping() - public ApiResult save(@RequestBody DormitoryFloor dormitoryFloor) { - // 记录当前登录用户id - User loginUser = getLoginUser(); - if (loginUser != null) { - // dormitoryFloor实体类没有userId字段,所以不设置 - } - if (dormitoryFloorService.save(dormitoryFloor)) { - return success("添加成功"); - } - return fail("添加失败"); - } - - @PreAuthorize("hasAuthority('dormitory:dormitoryFloor:update')") - @OperationLog - @Operation(summary = "修改宿舍楼层") - @PutMapping() - public ApiResult update(@RequestBody DormitoryFloor dormitoryFloor) { - if (dormitoryFloorService.updateById(dormitoryFloor)) { - return success("修改成功"); - } - return fail("修改失败"); - } - - @PreAuthorize("hasAuthority('dormitory:dormitoryFloor:remove')") - @OperationLog - @Operation(summary = "删除宿舍楼层") - @DeleteMapping("/{id}") - public ApiResult remove(@PathVariable("id") Integer id) { - if (dormitoryFloorService.removeById(id)) { - return success("删除成功"); - } - return fail("删除失败"); - } - - @PreAuthorize("hasAuthority('dormitory:dormitoryFloor:save')") - @OperationLog - @Operation(summary = "批量添加宿舍楼层") - @PostMapping("/batch") - public ApiResult saveBatch(@RequestBody List list) { - if (dormitoryFloorService.saveBatch(list)) { - return success("添加成功"); - } - return fail("添加失败"); - } - - @PreAuthorize("hasAuthority('dormitory:dormitoryFloor:update')") - @OperationLog - @Operation(summary = "批量修改宿舍楼层") - @PutMapping("/batch") - public ApiResult removeBatch(@RequestBody BatchParam batchParam) { - if (batchParam.update(dormitoryFloorService, "id")) { - return success("修改成功"); - } - return fail("修改失败"); - } - - @PreAuthorize("hasAuthority('dormitory:dormitoryFloor:remove')") - @OperationLog - @Operation(summary = "批量删除宿舍楼层") - @DeleteMapping("/batch") - public ApiResult removeBatch(@RequestBody List ids) { - if (dormitoryFloorService.removeByIds(ids)) { - return success("删除成功"); - } - return fail("删除失败"); - } - -} diff --git a/src/main/java/com/gxwebsoft/dormitory/controller/DormitoryRecordController.java b/src/main/java/com/gxwebsoft/dormitory/controller/DormitoryRecordController.java deleted file mode 100644 index 7a6cc15..0000000 --- a/src/main/java/com/gxwebsoft/dormitory/controller/DormitoryRecordController.java +++ /dev/null @@ -1,126 +0,0 @@ -package com.gxwebsoft.dormitory.controller; - -import com.gxwebsoft.common.core.web.BaseController; -import com.gxwebsoft.dormitory.service.DormitoryRecordService; -import com.gxwebsoft.dormitory.entity.DormitoryRecord; -import com.gxwebsoft.dormitory.param.DormitoryRecordParam; -import com.gxwebsoft.common.core.web.ApiResult; -import com.gxwebsoft.common.core.web.PageResult; -import com.gxwebsoft.common.core.web.PageParam; -import com.gxwebsoft.common.core.web.BatchParam; -import com.gxwebsoft.common.core.annotation.OperationLog; -import com.gxwebsoft.common.system.entity.User; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.tags.Tag; -import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.web.bind.annotation.*; - -import javax.annotation.Resource; -import java.util.List; - -/** - * 宿舍记录控制器 - * - * @author 科技小王子 - * @since 2025-10-03 10:49:27 - */ -@Tag(name = "宿舍记录管理") -@RestController -@RequestMapping("/api/dormitory/dormitory-record") -public class DormitoryRecordController extends BaseController { - @Resource - private DormitoryRecordService dormitoryRecordService; - - @Operation(summary = "分页查询宿舍记录") - @GetMapping("/page") - public ApiResult> page(DormitoryRecordParam param) { - // 使用关联查询 - return success(dormitoryRecordService.pageRel(param)); - } - - @Operation(summary = "查询全部宿舍记录") - @GetMapping() - public ApiResult> list(DormitoryRecordParam param) { - // 使用关联查询 - return success(dormitoryRecordService.listRel(param)); - } - - @Operation(summary = "根据id查询宿舍记录") - @GetMapping("/{id}") - public ApiResult get(@PathVariable("id") Integer id) { - // 使用关联查询 - return success(dormitoryRecordService.getByIdRel(id)); - } - - @PreAuthorize("hasAuthority('dormitory:dormitoryRecord:save')") - @OperationLog - @Operation(summary = "添加宿舍记录") - @PostMapping() - public ApiResult save(@RequestBody DormitoryRecord dormitoryRecord) { - // 记录当前登录用户id - User loginUser = getLoginUser(); - if (loginUser != null) { - // dormitoryRecord实体类没有userId字段,所以不设置 - } - if (dormitoryRecordService.save(dormitoryRecord)) { - return success("添加成功"); - } - return fail("添加失败"); - } - - @PreAuthorize("hasAuthority('dormitory:dormitoryRecord:update')") - @OperationLog - @Operation(summary = "修改宿舍记录") - @PutMapping() - public ApiResult update(@RequestBody DormitoryRecord dormitoryRecord) { - if (dormitoryRecordService.updateById(dormitoryRecord)) { - return success("修改成功"); - } - return fail("修改失败"); - } - - @PreAuthorize("hasAuthority('dormitory:dormitoryRecord:remove')") - @OperationLog - @Operation(summary = "删除宿舍记录") - @DeleteMapping("/{id}") - public ApiResult remove(@PathVariable("id") Integer id) { - if (dormitoryRecordService.removeById(id)) { - return success("删除成功"); - } - return fail("删除失败"); - } - - @PreAuthorize("hasAuthority('dormitory:dormitoryRecord:save')") - @OperationLog - @Operation(summary = "批量添加宿舍记录") - @PostMapping("/batch") - public ApiResult saveBatch(@RequestBody List list) { - if (dormitoryRecordService.saveBatch(list)) { - return success("添加成功"); - } - return fail("添加失败"); - } - - @PreAuthorize("hasAuthority('dormitory:dormitoryRecord:update')") - @OperationLog - @Operation(summary = "批量修改宿舍记录") - @PutMapping("/batch") - public ApiResult removeBatch(@RequestBody BatchParam batchParam) { - if (batchParam.update(dormitoryRecordService, "id")) { - return success("修改成功"); - } - return fail("修改失败"); - } - - @PreAuthorize("hasAuthority('dormitory:dormitoryRecord:remove')") - @OperationLog - @Operation(summary = "批量删除宿舍记录") - @DeleteMapping("/batch") - public ApiResult removeBatch(@RequestBody List ids) { - if (dormitoryRecordService.removeByIds(ids)) { - return success("删除成功"); - } - return fail("删除失败"); - } - -} diff --git a/src/main/java/com/gxwebsoft/dormitory/entity/DormitoryApply.java b/src/main/java/com/gxwebsoft/dormitory/entity/DormitoryApply.java deleted file mode 100644 index 0b82350..0000000 --- a/src/main/java/com/gxwebsoft/dormitory/entity/DormitoryApply.java +++ /dev/null @@ -1,89 +0,0 @@ -package com.gxwebsoft.dormitory.entity; - -import com.baomidou.mybatisplus.annotation.IdType; -import com.baomidou.mybatisplus.annotation.TableId; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; -import lombok.EqualsAndHashCode; - -import java.io.Serializable; -import java.math.BigDecimal; -import java.time.LocalDateTime; - -/** - * 审批管理 - * - * @author 科技小王子 - * @since 2025-10-03 15:54:30 - */ -@Data -@EqualsAndHashCode(callSuper = false) -@Schema(description = "审批管理") -public class DormitoryApply implements Serializable { - private static final long serialVersionUID = 1L; - - @Schema(description = "主键ID") - @TableId(value = "id", type = IdType.AUTO) - private Integer id; - - @Schema(description = "类型") - private Integer type; - - @Schema(description = "用户ID") - private Integer userId; - - @Schema(description = "姓名") - private String realName; - - @Schema(description = "手机号") - private String mobile; - - @Schema(description = "客户名称") - private String dealerName; - - @Schema(description = "客户编号") - private String dealerCode; - - @Schema(description = "详细地址") - private String address; - - @Schema(description = "签约价格") - private BigDecimal money; - - @Schema(description = "推荐人用户ID") - private Integer refereeId; - - @Schema(description = "申请方式(10需后台审核 20无需审核)") - private Integer applyType; - - @Schema(description = "审核状态 (10待审核 20审核通过 30驳回)") - private Integer applyStatus; - - @Schema(description = "申请时间") - private LocalDateTime applyTime; - - @Schema(description = "审核时间") - private LocalDateTime auditTime; - - @Schema(description = "合同时间") - private LocalDateTime contractTime; - - @Schema(description = "过期时间") - private LocalDateTime expirationTime; - - @Schema(description = "驳回原因") - private String rejectReason; - - @Schema(description = "备注") - private String comments; - - @Schema(description = "商城ID") - private Integer tenantId; - - @Schema(description = "创建时间") - private LocalDateTime createTime; - - @Schema(description = "修改时间") - private LocalDateTime updateTime; - -} \ No newline at end of file diff --git a/src/main/java/com/gxwebsoft/dormitory/entity/DormitoryBed.java b/src/main/java/com/gxwebsoft/dormitory/entity/DormitoryBed.java deleted file mode 100644 index 7302543..0000000 --- a/src/main/java/com/gxwebsoft/dormitory/entity/DormitoryBed.java +++ /dev/null @@ -1,91 +0,0 @@ -package com.gxwebsoft.dormitory.entity; - -import com.baomidou.mybatisplus.annotation.IdType; -import com.baomidou.mybatisplus.annotation.TableField; -import com.baomidou.mybatisplus.annotation.TableId; -import java.time.LocalDateTime; -import java.io.Serializable; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; -import lombok.EqualsAndHashCode; - -/** - * 宿舍床位 - * - * @author 科技小王子 - * @since 2025-10-03 10:49:27 - */ -@Data -@EqualsAndHashCode(callSuper = false) -@Schema(description = "宿舍床位") -public class DormitoryBed implements Serializable { - private static final long serialVersionUID = 1L; - - @Schema(description = "ID") - @TableId(value = "id", type = IdType.AUTO) - private Integer id; - - @Schema(description = "宿舍名称") - private String name; - - @Schema(description = "编号") - private String code; - - @Schema(description = "楼栋ID") - private Integer buildingId; - - @Schema(description = "楼栋名称") - @TableField(exist = false) - private String buildingName; - - @Schema(description = "楼层ID") - private Integer floorId; - - @Schema(description = "楼层名称") - @TableField(exist = false) - private String floorName; - - @Schema(description = "宿舍ID") - private Integer recordId; - - @Schema(description = "宿舍名称") - @TableField(exist = false) - private String recordName; - - @Schema(description = "学生ID") - private Integer userId; - - @Schema(description = "学生昵称") - @TableField(exist = false) - private String realName; - - @Schema(description = "手机号码") - @TableField(exist = false) - private String phone; - - @Schema(description = "头像") - @TableField(exist = false) - private String avatar; - - @Schema(description = "上下铺 1下铺 2上铺 0无") - private Boolean bunk; - - @Schema(description = "充电口") - private Boolean chargingPort; - - @Schema(description = "排序(数字越小越靠前)") - private Integer sortNumber; - - @Schema(description = "备注") - private String comments; - - @Schema(description = "状态, 0正常, 1报修") - private Integer status; - - @Schema(description = "租户id") - private Integer tenantId; - - @Schema(description = "创建时间") - private LocalDateTime createTime; - -} diff --git a/src/main/java/com/gxwebsoft/dormitory/entity/DormitoryBuilding.java b/src/main/java/com/gxwebsoft/dormitory/entity/DormitoryBuilding.java deleted file mode 100644 index f79d4fb..0000000 --- a/src/main/java/com/gxwebsoft/dormitory/entity/DormitoryBuilding.java +++ /dev/null @@ -1,48 +0,0 @@ -package com.gxwebsoft.dormitory.entity; - -import com.baomidou.mybatisplus.annotation.IdType; -import com.baomidou.mybatisplus.annotation.TableId; -import java.time.LocalDateTime; -import java.io.Serializable; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; -import lombok.EqualsAndHashCode; - -/** - * 宿舍楼栋 - * - * @author 科技小王子 - * @since 2025-10-03 10:49:27 - */ -@Data -@EqualsAndHashCode(callSuper = false) -@Schema(description = "宿舍楼栋") -public class DormitoryBuilding implements Serializable { - private static final long serialVersionUID = 1L; - - @Schema(description = "ID") - @TableId(value = "id", type = IdType.AUTO) - private Integer id; - - @Schema(description = "楼栋名称") - private String name; - - @Schema(description = "楼栋编号") - private String code; - - @Schema(description = "排序(数字越小越靠前)") - private Integer sortNumber; - - @Schema(description = "备注") - private String comments; - - @Schema(description = "状态, 0正常, 1冻结") - private Integer status; - - @Schema(description = "租户id") - private Integer tenantId; - - @Schema(description = "创建时间") - private LocalDateTime createTime; - -} \ No newline at end of file diff --git a/src/main/java/com/gxwebsoft/dormitory/entity/DormitoryFloor.java b/src/main/java/com/gxwebsoft/dormitory/entity/DormitoryFloor.java deleted file mode 100644 index e72b2e0..0000000 --- a/src/main/java/com/gxwebsoft/dormitory/entity/DormitoryFloor.java +++ /dev/null @@ -1,56 +0,0 @@ -package com.gxwebsoft.dormitory.entity; - -import com.baomidou.mybatisplus.annotation.IdType; -import com.baomidou.mybatisplus.annotation.TableField; -import com.baomidou.mybatisplus.annotation.TableId; -import java.time.LocalDateTime; -import java.io.Serializable; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; -import lombok.EqualsAndHashCode; - -/** - * 宿舍楼层 - * - * @author 科技小王子 - * @since 2025-10-03 10:49:27 - */ -@Data -@EqualsAndHashCode(callSuper = false) -@Schema(description = "宿舍楼层") -public class DormitoryFloor implements Serializable { - private static final long serialVersionUID = 1L; - - @Schema(description = "ID") - @TableId(value = "id", type = IdType.AUTO) - private Integer id; - - @Schema(description = "楼层") - private String name; - - @Schema(description = "编号") - private String code; - - @Schema(description = "楼栋") - @TableField(exist = false) - private String buildingName; - - @Schema(description = "楼栋ID") - private Integer buildingId; - - @Schema(description = "排序(数字越小越靠前)") - private Integer sortNumber; - - @Schema(description = "备注") - private String comments; - - @Schema(description = "状态, 0正常, 1冻结") - private Integer status; - - @Schema(description = "租户id") - private Integer tenantId; - - @Schema(description = "创建时间") - private LocalDateTime createTime; - -} diff --git a/src/main/java/com/gxwebsoft/dormitory/entity/DormitoryRecord.java b/src/main/java/com/gxwebsoft/dormitory/entity/DormitoryRecord.java deleted file mode 100644 index d2aac03..0000000 --- a/src/main/java/com/gxwebsoft/dormitory/entity/DormitoryRecord.java +++ /dev/null @@ -1,69 +0,0 @@ -package com.gxwebsoft.dormitory.entity; - -import com.baomidou.mybatisplus.annotation.IdType; -import com.baomidou.mybatisplus.annotation.TableField; -import com.baomidou.mybatisplus.annotation.TableId; -import java.time.LocalDateTime; -import java.io.Serializable; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; -import lombok.EqualsAndHashCode; - -/** - * 宿舍记录 - * - * @author 科技小王子 - * @since 2025-10-03 10:49:27 - */ -@Data -@EqualsAndHashCode(callSuper = false) -@Schema(description = "宿舍记录") -public class DormitoryRecord implements Serializable { - private static final long serialVersionUID = 1L; - - @Schema(description = "ID") - @TableId(value = "id", type = IdType.AUTO) - private Integer id; - - @Schema(description = "宿舍名称") - private String name; - - @Schema(description = "编号") - private String code; - - @Schema(description = "楼栋ID") - private Integer buildingId; - - @Schema(description = "楼栋名称") - @TableField(exist = false) - private String buildingName; - - @Schema(description = "楼层ID") - private Integer floorId; - - @Schema(description = "楼层名称") - @TableField(exist = false) - private String floorName; - - @Schema(description = "床位数") - private Integer beds; - - @Schema(description = "独立卫生间") - private Boolean toilet; - - @Schema(description = "排序(数字越小越靠前)") - private Integer sortNumber; - - @Schema(description = "备注") - private String comments; - - @Schema(description = "状态, 0正常, 1冻结") - private Integer status; - - @Schema(description = "租户id") - private Integer tenantId; - - @Schema(description = "创建时间") - private LocalDateTime createTime; - -} diff --git a/src/main/java/com/gxwebsoft/dormitory/mapper/DormitoryApplyMapper.java b/src/main/java/com/gxwebsoft/dormitory/mapper/DormitoryApplyMapper.java deleted file mode 100644 index b7e5ea5..0000000 --- a/src/main/java/com/gxwebsoft/dormitory/mapper/DormitoryApplyMapper.java +++ /dev/null @@ -1,37 +0,0 @@ -package com.gxwebsoft.dormitory.mapper; - -import com.baomidou.mybatisplus.core.mapper.BaseMapper; -import com.baomidou.mybatisplus.core.metadata.IPage; -import com.gxwebsoft.dormitory.entity.DormitoryApply; -import com.gxwebsoft.dormitory.param.DormitoryApplyParam; -import org.apache.ibatis.annotations.Param; - -import java.util.List; - -/** - * 审批管理Mapper - * - * @author 科技小王子 - * @since 2025-10-03 15:54:30 - */ -public interface DormitoryApplyMapper extends BaseMapper { - - /** - * 分页查询 - * - * @param page 分页对象 - * @param param 查询参数 - * @return List - */ - List selectPageRel(@Param("page") IPage page, - @Param("param") DormitoryApplyParam param); - - /** - * 查询全部 - * - * @param param 查询参数 - * @return List - */ - List selectListRel(@Param("param") DormitoryApplyParam param); - -} diff --git a/src/main/java/com/gxwebsoft/dormitory/mapper/DormitoryBedMapper.java b/src/main/java/com/gxwebsoft/dormitory/mapper/DormitoryBedMapper.java deleted file mode 100644 index 484bf2b..0000000 --- a/src/main/java/com/gxwebsoft/dormitory/mapper/DormitoryBedMapper.java +++ /dev/null @@ -1,37 +0,0 @@ -package com.gxwebsoft.dormitory.mapper; - -import com.baomidou.mybatisplus.core.mapper.BaseMapper; -import com.baomidou.mybatisplus.core.metadata.IPage; -import com.gxwebsoft.dormitory.entity.DormitoryBed; -import com.gxwebsoft.dormitory.param.DormitoryBedParam; -import org.apache.ibatis.annotations.Param; - -import java.util.List; - -/** - * 宿舍床位Mapper - * - * @author 科技小王子 - * @since 2025-10-03 10:49:27 - */ -public interface DormitoryBedMapper extends BaseMapper { - - /** - * 分页查询 - * - * @param page 分页对象 - * @param param 查询参数 - * @return List - */ - List selectPageRel(@Param("page") IPage page, - @Param("param") DormitoryBedParam param); - - /** - * 查询全部 - * - * @param param 查询参数 - * @return List - */ - List selectListRel(@Param("param") DormitoryBedParam param); - -} diff --git a/src/main/java/com/gxwebsoft/dormitory/mapper/DormitoryBuildingMapper.java b/src/main/java/com/gxwebsoft/dormitory/mapper/DormitoryBuildingMapper.java deleted file mode 100644 index 3bfc4ca..0000000 --- a/src/main/java/com/gxwebsoft/dormitory/mapper/DormitoryBuildingMapper.java +++ /dev/null @@ -1,37 +0,0 @@ -package com.gxwebsoft.dormitory.mapper; - -import com.baomidou.mybatisplus.core.mapper.BaseMapper; -import com.baomidou.mybatisplus.core.metadata.IPage; -import com.gxwebsoft.dormitory.entity.DormitoryBuilding; -import com.gxwebsoft.dormitory.param.DormitoryBuildingParam; -import org.apache.ibatis.annotations.Param; - -import java.util.List; - -/** - * 宿舍楼栋Mapper - * - * @author 科技小王子 - * @since 2025-10-03 10:49:27 - */ -public interface DormitoryBuildingMapper extends BaseMapper { - - /** - * 分页查询 - * - * @param page 分页对象 - * @param param 查询参数 - * @return List - */ - List selectPageRel(@Param("page") IPage page, - @Param("param") DormitoryBuildingParam param); - - /** - * 查询全部 - * - * @param param 查询参数 - * @return List - */ - List selectListRel(@Param("param") DormitoryBuildingParam param); - -} diff --git a/src/main/java/com/gxwebsoft/dormitory/mapper/DormitoryFloorMapper.java b/src/main/java/com/gxwebsoft/dormitory/mapper/DormitoryFloorMapper.java deleted file mode 100644 index 84f7e9b..0000000 --- a/src/main/java/com/gxwebsoft/dormitory/mapper/DormitoryFloorMapper.java +++ /dev/null @@ -1,37 +0,0 @@ -package com.gxwebsoft.dormitory.mapper; - -import com.baomidou.mybatisplus.core.mapper.BaseMapper; -import com.baomidou.mybatisplus.core.metadata.IPage; -import com.gxwebsoft.dormitory.entity.DormitoryFloor; -import com.gxwebsoft.dormitory.param.DormitoryFloorParam; -import org.apache.ibatis.annotations.Param; - -import java.util.List; - -/** - * 宿舍楼层Mapper - * - * @author 科技小王子 - * @since 2025-10-03 10:49:27 - */ -public interface DormitoryFloorMapper extends BaseMapper { - - /** - * 分页查询 - * - * @param page 分页对象 - * @param param 查询参数 - * @return List - */ - List selectPageRel(@Param("page") IPage page, - @Param("param") DormitoryFloorParam param); - - /** - * 查询全部 - * - * @param param 查询参数 - * @return List - */ - List selectListRel(@Param("param") DormitoryFloorParam param); - -} diff --git a/src/main/java/com/gxwebsoft/dormitory/mapper/DormitoryRecordMapper.java b/src/main/java/com/gxwebsoft/dormitory/mapper/DormitoryRecordMapper.java deleted file mode 100644 index 9d64316..0000000 --- a/src/main/java/com/gxwebsoft/dormitory/mapper/DormitoryRecordMapper.java +++ /dev/null @@ -1,37 +0,0 @@ -package com.gxwebsoft.dormitory.mapper; - -import com.baomidou.mybatisplus.core.mapper.BaseMapper; -import com.baomidou.mybatisplus.core.metadata.IPage; -import com.gxwebsoft.dormitory.entity.DormitoryRecord; -import com.gxwebsoft.dormitory.param.DormitoryRecordParam; -import org.apache.ibatis.annotations.Param; - -import java.util.List; - -/** - * 宿舍记录Mapper - * - * @author 科技小王子 - * @since 2025-10-03 10:49:27 - */ -public interface DormitoryRecordMapper extends BaseMapper { - - /** - * 分页查询 - * - * @param page 分页对象 - * @param param 查询参数 - * @return List - */ - List selectPageRel(@Param("page") IPage page, - @Param("param") DormitoryRecordParam param); - - /** - * 查询全部 - * - * @param param 查询参数 - * @return List - */ - List selectListRel(@Param("param") DormitoryRecordParam param); - -} diff --git a/src/main/java/com/gxwebsoft/dormitory/mapper/xml/DormitoryApplyMapper.xml b/src/main/java/com/gxwebsoft/dormitory/mapper/xml/DormitoryApplyMapper.xml deleted file mode 100644 index a42aa41..0000000 --- a/src/main/java/com/gxwebsoft/dormitory/mapper/xml/DormitoryApplyMapper.xml +++ /dev/null @@ -1,87 +0,0 @@ - - - - - - - SELECT a.* - FROM dormitory_apply a - - - AND a.id = #{param.id} - - - AND a.type = #{param.type} - - - AND a.user_id = #{param.userId} - - - AND a.real_name LIKE CONCAT('%', #{param.realName}, '%') - - - AND a.mobile LIKE CONCAT('%', #{param.mobile}, '%') - - - AND a.dealer_name LIKE CONCAT('%', #{param.dealerName}, '%') - - - AND a.dealer_code LIKE CONCAT('%', #{param.dealerCode}, '%') - - - AND a.address LIKE CONCAT('%', #{param.address}, '%') - - - AND a.money = #{param.money} - - - AND a.referee_id = #{param.refereeId} - - - AND a.apply_type = #{param.applyType} - - - AND a.apply_status = #{param.applyStatus} - - - AND a.apply_time LIKE CONCAT('%', #{param.applyTime}, '%') - - - AND a.audit_time LIKE CONCAT('%', #{param.auditTime}, '%') - - - AND a.contract_time LIKE CONCAT('%', #{param.contractTime}, '%') - - - AND a.expiration_time LIKE CONCAT('%', #{param.expirationTime}, '%') - - - AND a.reject_reason LIKE CONCAT('%', #{param.rejectReason}, '%') - - - AND a.comments LIKE CONCAT('%', #{param.comments}, '%') - - - AND a.create_time >= #{param.createTimeStart} - - - AND a.create_time <= #{param.createTimeEnd} - - - AND (a.comments LIKE CONCAT('%', #{param.keywords}, '%') - ) - - - - - - - - - - - diff --git a/src/main/java/com/gxwebsoft/dormitory/mapper/xml/DormitoryBedMapper.xml b/src/main/java/com/gxwebsoft/dormitory/mapper/xml/DormitoryBedMapper.xml deleted file mode 100644 index 351e144..0000000 --- a/src/main/java/com/gxwebsoft/dormitory/mapper/xml/DormitoryBedMapper.xml +++ /dev/null @@ -1,73 +0,0 @@ - - - - - - - SELECT a.*, b.name AS buildingName, c.name AS floorName, d.name AS recordName, e.real_name AS realName, e.phone AS phone, e.avatar - FROM dormitory_bed a - LEFT JOIN dormitory_building b ON a.building_id = b.id - LEFT JOIN dormitory_floor c ON a.floor_id = c.id - LEFT JOIN dormitory_record d ON a.record_id = d.id - LEFT JOIN shop_user e ON a.user_id = e.user_id - - - AND a.id = #{param.id} - - - AND a.name LIKE CONCAT('%', #{param.name}, '%') - - - AND a.code LIKE CONCAT('%', #{param.code}, '%') - - - AND a.building_id = #{param.buildingId} - - - AND a.floor_id = #{param.floorId} - - - AND a.record_id = #{param.recordId} - - - AND a.user_id = #{param.userId} - - - AND a.bunk = #{param.bunk} - - - AND a.charging_port = #{param.chargingPort} - - - AND a.sort_number = #{param.sortNumber} - - - AND a.comments LIKE CONCAT('%', #{param.comments}, '%') - - - AND a.status = #{param.status} - - - AND a.create_time >= #{param.createTimeStart} - - - AND a.create_time <= #{param.createTimeEnd} - - - AND (a.comments LIKE CONCAT('%', #{param.keywords}, '%') - ) - - - - - - - - - - - diff --git a/src/main/java/com/gxwebsoft/dormitory/mapper/xml/DormitoryBuildingMapper.xml b/src/main/java/com/gxwebsoft/dormitory/mapper/xml/DormitoryBuildingMapper.xml deleted file mode 100644 index b7a6e30..0000000 --- a/src/main/java/com/gxwebsoft/dormitory/mapper/xml/DormitoryBuildingMapper.xml +++ /dev/null @@ -1,51 +0,0 @@ - - - - - - - SELECT a.* - FROM dormitory_building a - - - AND a.id = #{param.id} - - - AND a.name LIKE CONCAT('%', #{param.name}, '%') - - - AND a.code LIKE CONCAT('%', #{param.code}, '%') - - - AND a.sort_number = #{param.sortNumber} - - - AND a.comments LIKE CONCAT('%', #{param.comments}, '%') - - - AND a.status = #{param.status} - - - AND a.create_time >= #{param.createTimeStart} - - - AND a.create_time <= #{param.createTimeEnd} - - - AND (a.comments LIKE CONCAT('%', #{param.keywords}, '%') - ) - - - - - - - - - - - diff --git a/src/main/java/com/gxwebsoft/dormitory/mapper/xml/DormitoryFloorMapper.xml b/src/main/java/com/gxwebsoft/dormitory/mapper/xml/DormitoryFloorMapper.xml deleted file mode 100644 index 9cafffe..0000000 --- a/src/main/java/com/gxwebsoft/dormitory/mapper/xml/DormitoryFloorMapper.xml +++ /dev/null @@ -1,55 +0,0 @@ - - - - - - - SELECT a.*, b.name AS buildingName - FROM dormitory_floor a - LEFT JOIN dormitory_building b ON a.building_id = b.id - - - AND a.id = #{param.id} - - - AND a.name LIKE CONCAT('%', #{param.name}, '%') - - - AND a.code LIKE CONCAT('%', #{param.code}, '%') - - - AND a.building_id = #{param.buildingId} - - - AND a.sort_number = #{param.sortNumber} - - - AND a.comments LIKE CONCAT('%', #{param.comments}, '%') - - - AND a.status = #{param.status} - - - AND a.create_time >= #{param.createTimeStart} - - - AND a.create_time <= #{param.createTimeEnd} - - - AND (a.comments LIKE CONCAT('%', #{param.keywords}, '%') - ) - - - - - - - - - - - diff --git a/src/main/java/com/gxwebsoft/dormitory/mapper/xml/DormitoryRecordMapper.xml b/src/main/java/com/gxwebsoft/dormitory/mapper/xml/DormitoryRecordMapper.xml deleted file mode 100644 index 95755c9..0000000 --- a/src/main/java/com/gxwebsoft/dormitory/mapper/xml/DormitoryRecordMapper.xml +++ /dev/null @@ -1,65 +0,0 @@ - - - - - - - SELECT a.*, b.name AS buildingName, c.name AS floorName - FROM dormitory_record a - LEFT JOIN dormitory_building b ON a.building_id = b.id - LEFT JOIN dormitory_floor c ON a.floor_id = c.id - - - AND a.id = #{param.id} - - - AND a.name LIKE CONCAT('%', #{param.name}, '%') - - - AND a.code LIKE CONCAT('%', #{param.code}, '%') - - - AND a.building_id = #{param.buildingId} - - - AND a.floor_id = #{param.floorId} - - - AND a.beds = #{param.beds} - - - AND a.toilet = #{param.toilet} - - - AND a.sort_number = #{param.sortNumber} - - - AND a.comments LIKE CONCAT('%', #{param.comments}, '%') - - - AND a.status = #{param.status} - - - AND a.create_time >= #{param.createTimeStart} - - - AND a.create_time <= #{param.createTimeEnd} - - - AND (a.comments LIKE CONCAT('%', #{param.keywords}, '%') - ) - - - - - - - - - - - diff --git a/src/main/java/com/gxwebsoft/dormitory/param/DormitoryApplyParam.java b/src/main/java/com/gxwebsoft/dormitory/param/DormitoryApplyParam.java deleted file mode 100644 index 474cb02..0000000 --- a/src/main/java/com/gxwebsoft/dormitory/param/DormitoryApplyParam.java +++ /dev/null @@ -1,87 +0,0 @@ -package com.gxwebsoft.dormitory.param; - -import com.fasterxml.jackson.annotation.JsonInclude; -import com.gxwebsoft.common.core.annotation.QueryField; -import com.gxwebsoft.common.core.annotation.QueryType; -import com.gxwebsoft.common.core.web.BaseParam; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; -import lombok.EqualsAndHashCode; - -import java.math.BigDecimal; - -/** - * 审批管理查询参数 - * - * @author 科技小王子 - * @since 2025-10-03 15:54:30 - */ -@Data -@EqualsAndHashCode(callSuper = false) -@JsonInclude(JsonInclude.Include.NON_NULL) -@Schema(description = "审批管理查询参数") -public class DormitoryApplyParam extends BaseParam { - private static final long serialVersionUID = 1L; - - @Schema(description = "主键ID") - @QueryField(type = QueryType.EQ) - private Integer id; - - @Schema(description = "类型") - @QueryField(type = QueryType.EQ) - private Integer type; - - @Schema(description = "用户ID") - @QueryField(type = QueryType.EQ) - private Integer userId; - - @Schema(description = "姓名") - private String realName; - - @Schema(description = "手机号") - private String mobile; - - @Schema(description = "客户名称") - private String dealerName; - - @Schema(description = "客户编号") - private String dealerCode; - - @Schema(description = "详细地址") - private String address; - - @Schema(description = "签约价格") - @QueryField(type = QueryType.EQ) - private BigDecimal money; - - @Schema(description = "推荐人用户ID") - @QueryField(type = QueryType.EQ) - private Integer refereeId; - - @Schema(description = "申请方式(10需后台审核 20无需审核)") - @QueryField(type = QueryType.EQ) - private Integer applyType; - - @Schema(description = "审核状态 (10待审核 20审核通过 30驳回)") - @QueryField(type = QueryType.EQ) - private Integer applyStatus; - - @Schema(description = "申请时间") - private String applyTime; - - @Schema(description = "审核时间") - private String auditTime; - - @Schema(description = "合同时间") - private String contractTime; - - @Schema(description = "过期时间") - private String expirationTime; - - @Schema(description = "驳回原因") - private String rejectReason; - - @Schema(description = "备注") - private String comments; - -} \ No newline at end of file diff --git a/src/main/java/com/gxwebsoft/dormitory/param/DormitoryBedParam.java b/src/main/java/com/gxwebsoft/dormitory/param/DormitoryBedParam.java deleted file mode 100644 index 2b99a10..0000000 --- a/src/main/java/com/gxwebsoft/dormitory/param/DormitoryBedParam.java +++ /dev/null @@ -1,69 +0,0 @@ -package com.gxwebsoft.dormitory.param; - -import java.math.BigDecimal; -import com.gxwebsoft.common.core.annotation.QueryField; -import com.gxwebsoft.common.core.annotation.QueryType; -import com.gxwebsoft.common.core.web.BaseParam; -import com.fasterxml.jackson.annotation.JsonInclude; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; -import lombok.EqualsAndHashCode; - -/** - * 宿舍床位查询参数 - * - * @author 科技小王子 - * @since 2025-10-03 10:49:27 - */ -@Data -@EqualsAndHashCode(callSuper = false) -@JsonInclude(JsonInclude.Include.NON_NULL) -@Schema(description = "宿舍床位查询参数") -public class DormitoryBedParam extends BaseParam { - private static final long serialVersionUID = 1L; - - @Schema(description = "ID") - @QueryField(type = QueryType.EQ) - private Integer id; - - @Schema(description = "宿舍名称") - private String name; - - @Schema(description = "编号") - private String code; - - @Schema(description = "楼栋ID") - @QueryField(type = QueryType.EQ) - private Integer buildingId; - - @Schema(description = "楼层ID") - @QueryField(type = QueryType.EQ) - private Integer floorId; - - @Schema(description = "宿舍ID") - @QueryField(type = QueryType.EQ) - private Integer recordId; - - @Schema(description = "学生ID") - private Integer userId; - - @Schema(description = "上下铺 1下铺 2上铺 0无") - @QueryField(type = QueryType.EQ) - private Boolean bunk; - - @Schema(description = "充电口") - @QueryField(type = QueryType.EQ) - private Boolean chargingPort; - - @Schema(description = "排序(数字越小越靠前)") - @QueryField(type = QueryType.EQ) - private Integer sortNumber; - - @Schema(description = "备注") - private String comments; - - @Schema(description = "状态, 0正常, 1报修") - @QueryField(type = QueryType.EQ) - private Integer status; - -} diff --git a/src/main/java/com/gxwebsoft/dormitory/param/DormitoryBuildingParam.java b/src/main/java/com/gxwebsoft/dormitory/param/DormitoryBuildingParam.java deleted file mode 100644 index 1c02691..0000000 --- a/src/main/java/com/gxwebsoft/dormitory/param/DormitoryBuildingParam.java +++ /dev/null @@ -1,46 +0,0 @@ -package com.gxwebsoft.dormitory.param; - -import java.math.BigDecimal; -import com.gxwebsoft.common.core.annotation.QueryField; -import com.gxwebsoft.common.core.annotation.QueryType; -import com.gxwebsoft.common.core.web.BaseParam; -import com.fasterxml.jackson.annotation.JsonInclude; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; -import lombok.EqualsAndHashCode; - -/** - * 宿舍楼栋查询参数 - * - * @author 科技小王子 - * @since 2025-10-03 10:49:27 - */ -@Data -@EqualsAndHashCode(callSuper = false) -@JsonInclude(JsonInclude.Include.NON_NULL) -@Schema(description = "宿舍楼栋查询参数") -public class DormitoryBuildingParam extends BaseParam { - private static final long serialVersionUID = 1L; - - @Schema(description = "ID") - @QueryField(type = QueryType.EQ) - private Integer id; - - @Schema(description = "楼栋名称") - private String name; - - @Schema(description = "楼栋编号") - private String code; - - @Schema(description = "排序(数字越小越靠前)") - @QueryField(type = QueryType.EQ) - private Integer sortNumber; - - @Schema(description = "备注") - private String comments; - - @Schema(description = "状态, 0正常, 1冻结") - @QueryField(type = QueryType.EQ) - private Integer status; - -} \ No newline at end of file diff --git a/src/main/java/com/gxwebsoft/dormitory/param/DormitoryFloorParam.java b/src/main/java/com/gxwebsoft/dormitory/param/DormitoryFloorParam.java deleted file mode 100644 index d699dbc..0000000 --- a/src/main/java/com/gxwebsoft/dormitory/param/DormitoryFloorParam.java +++ /dev/null @@ -1,50 +0,0 @@ -package com.gxwebsoft.dormitory.param; - -import java.math.BigDecimal; -import com.gxwebsoft.common.core.annotation.QueryField; -import com.gxwebsoft.common.core.annotation.QueryType; -import com.gxwebsoft.common.core.web.BaseParam; -import com.fasterxml.jackson.annotation.JsonInclude; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; -import lombok.EqualsAndHashCode; - -/** - * 宿舍楼层查询参数 - * - * @author 科技小王子 - * @since 2025-10-03 10:49:27 - */ -@Data -@EqualsAndHashCode(callSuper = false) -@JsonInclude(JsonInclude.Include.NON_NULL) -@Schema(description = "宿舍楼层查询参数") -public class DormitoryFloorParam extends BaseParam { - private static final long serialVersionUID = 1L; - - @Schema(description = "ID") - @QueryField(type = QueryType.EQ) - private Integer id; - - @Schema(description = "楼层") - private String name; - - @Schema(description = "编号") - private String code; - - @Schema(description = "楼栋ID") - @QueryField(type = QueryType.EQ) - private Integer buildingId; - - @Schema(description = "排序(数字越小越靠前)") - @QueryField(type = QueryType.EQ) - private Integer sortNumber; - - @Schema(description = "备注") - private String comments; - - @Schema(description = "状态, 0正常, 1冻结") - @QueryField(type = QueryType.EQ) - private Integer status; - -} \ No newline at end of file diff --git a/src/main/java/com/gxwebsoft/dormitory/param/DormitoryRecordParam.java b/src/main/java/com/gxwebsoft/dormitory/param/DormitoryRecordParam.java deleted file mode 100644 index 2843d1d..0000000 --- a/src/main/java/com/gxwebsoft/dormitory/param/DormitoryRecordParam.java +++ /dev/null @@ -1,62 +0,0 @@ -package com.gxwebsoft.dormitory.param; - -import java.math.BigDecimal; -import com.gxwebsoft.common.core.annotation.QueryField; -import com.gxwebsoft.common.core.annotation.QueryType; -import com.gxwebsoft.common.core.web.BaseParam; -import com.fasterxml.jackson.annotation.JsonInclude; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; -import lombok.EqualsAndHashCode; - -/** - * 宿舍记录查询参数 - * - * @author 科技小王子 - * @since 2025-10-03 10:49:27 - */ -@Data -@EqualsAndHashCode(callSuper = false) -@JsonInclude(JsonInclude.Include.NON_NULL) -@Schema(description = "宿舍记录查询参数") -public class DormitoryRecordParam extends BaseParam { - private static final long serialVersionUID = 1L; - - @Schema(description = "ID") - @QueryField(type = QueryType.EQ) - private Integer id; - - @Schema(description = "宿舍名称") - private String name; - - @Schema(description = "编号") - private String code; - - @Schema(description = "楼栋ID") - @QueryField(type = QueryType.EQ) - private Integer buildingId; - - @Schema(description = "楼层ID") - @QueryField(type = QueryType.EQ) - private Integer floorId; - - @Schema(description = "床位数") - @QueryField(type = QueryType.EQ) - private Integer beds; - - @Schema(description = "独立卫生间") - @QueryField(type = QueryType.EQ) - private Boolean toilet; - - @Schema(description = "排序(数字越小越靠前)") - @QueryField(type = QueryType.EQ) - private Integer sortNumber; - - @Schema(description = "备注") - private String comments; - - @Schema(description = "状态, 0正常, 1冻结") - @QueryField(type = QueryType.EQ) - private Integer status; - -} \ No newline at end of file diff --git a/src/main/java/com/gxwebsoft/dormitory/service/DormitoryApplyService.java b/src/main/java/com/gxwebsoft/dormitory/service/DormitoryApplyService.java deleted file mode 100644 index 3456e75..0000000 --- a/src/main/java/com/gxwebsoft/dormitory/service/DormitoryApplyService.java +++ /dev/null @@ -1,42 +0,0 @@ -package com.gxwebsoft.dormitory.service; - -import com.baomidou.mybatisplus.extension.service.IService; -import com.gxwebsoft.common.core.web.PageResult; -import com.gxwebsoft.dormitory.entity.DormitoryApply; -import com.gxwebsoft.dormitory.param.DormitoryApplyParam; - -import java.util.List; - -/** - * 审批管理Service - * - * @author 科技小王子 - * @since 2025-10-03 15:54:30 - */ -public interface DormitoryApplyService extends IService { - - /** - * 分页关联查询 - * - * @param param 查询参数 - * @return PageResult - */ - PageResult pageRel(DormitoryApplyParam param); - - /** - * 关联查询全部 - * - * @param param 查询参数 - * @return List - */ - List listRel(DormitoryApplyParam param); - - /** - * 根据id查询 - * - * @param id 主键ID - * @return DormitoryApply - */ - DormitoryApply getByIdRel(Integer id); - -} diff --git a/src/main/java/com/gxwebsoft/dormitory/service/DormitoryBedService.java b/src/main/java/com/gxwebsoft/dormitory/service/DormitoryBedService.java deleted file mode 100644 index 3f80f7b..0000000 --- a/src/main/java/com/gxwebsoft/dormitory/service/DormitoryBedService.java +++ /dev/null @@ -1,42 +0,0 @@ -package com.gxwebsoft.dormitory.service; - -import com.baomidou.mybatisplus.extension.service.IService; -import com.gxwebsoft.common.core.web.PageResult; -import com.gxwebsoft.dormitory.entity.DormitoryBed; -import com.gxwebsoft.dormitory.param.DormitoryBedParam; - -import java.util.List; - -/** - * 宿舍床位Service - * - * @author 科技小王子 - * @since 2025-10-03 10:49:27 - */ -public interface DormitoryBedService extends IService { - - /** - * 分页关联查询 - * - * @param param 查询参数 - * @return PageResult - */ - PageResult pageRel(DormitoryBedParam param); - - /** - * 关联查询全部 - * - * @param param 查询参数 - * @return List - */ - List listRel(DormitoryBedParam param); - - /** - * 根据id查询 - * - * @param id ID - * @return DormitoryBed - */ - DormitoryBed getByIdRel(Integer id); - -} diff --git a/src/main/java/com/gxwebsoft/dormitory/service/DormitoryBuildingService.java b/src/main/java/com/gxwebsoft/dormitory/service/DormitoryBuildingService.java deleted file mode 100644 index c5b015f..0000000 --- a/src/main/java/com/gxwebsoft/dormitory/service/DormitoryBuildingService.java +++ /dev/null @@ -1,42 +0,0 @@ -package com.gxwebsoft.dormitory.service; - -import com.baomidou.mybatisplus.extension.service.IService; -import com.gxwebsoft.common.core.web.PageResult; -import com.gxwebsoft.dormitory.entity.DormitoryBuilding; -import com.gxwebsoft.dormitory.param.DormitoryBuildingParam; - -import java.util.List; - -/** - * 宿舍楼栋Service - * - * @author 科技小王子 - * @since 2025-10-03 10:49:27 - */ -public interface DormitoryBuildingService extends IService { - - /** - * 分页关联查询 - * - * @param param 查询参数 - * @return PageResult - */ - PageResult pageRel(DormitoryBuildingParam param); - - /** - * 关联查询全部 - * - * @param param 查询参数 - * @return List - */ - List listRel(DormitoryBuildingParam param); - - /** - * 根据id查询 - * - * @param id ID - * @return DormitoryBuilding - */ - DormitoryBuilding getByIdRel(Integer id); - -} diff --git a/src/main/java/com/gxwebsoft/dormitory/service/DormitoryFloorService.java b/src/main/java/com/gxwebsoft/dormitory/service/DormitoryFloorService.java deleted file mode 100644 index 0e64087..0000000 --- a/src/main/java/com/gxwebsoft/dormitory/service/DormitoryFloorService.java +++ /dev/null @@ -1,42 +0,0 @@ -package com.gxwebsoft.dormitory.service; - -import com.baomidou.mybatisplus.extension.service.IService; -import com.gxwebsoft.common.core.web.PageResult; -import com.gxwebsoft.dormitory.entity.DormitoryFloor; -import com.gxwebsoft.dormitory.param.DormitoryFloorParam; - -import java.util.List; - -/** - * 宿舍楼层Service - * - * @author 科技小王子 - * @since 2025-10-03 10:49:27 - */ -public interface DormitoryFloorService extends IService { - - /** - * 分页关联查询 - * - * @param param 查询参数 - * @return PageResult - */ - PageResult pageRel(DormitoryFloorParam param); - - /** - * 关联查询全部 - * - * @param param 查询参数 - * @return List - */ - List listRel(DormitoryFloorParam param); - - /** - * 根据id查询 - * - * @param id ID - * @return DormitoryFloor - */ - DormitoryFloor getByIdRel(Integer id); - -} diff --git a/src/main/java/com/gxwebsoft/dormitory/service/DormitoryRecordService.java b/src/main/java/com/gxwebsoft/dormitory/service/DormitoryRecordService.java deleted file mode 100644 index 13fe03d..0000000 --- a/src/main/java/com/gxwebsoft/dormitory/service/DormitoryRecordService.java +++ /dev/null @@ -1,42 +0,0 @@ -package com.gxwebsoft.dormitory.service; - -import com.baomidou.mybatisplus.extension.service.IService; -import com.gxwebsoft.common.core.web.PageResult; -import com.gxwebsoft.dormitory.entity.DormitoryRecord; -import com.gxwebsoft.dormitory.param.DormitoryRecordParam; - -import java.util.List; - -/** - * 宿舍记录Service - * - * @author 科技小王子 - * @since 2025-10-03 10:49:27 - */ -public interface DormitoryRecordService extends IService { - - /** - * 分页关联查询 - * - * @param param 查询参数 - * @return PageResult - */ - PageResult pageRel(DormitoryRecordParam param); - - /** - * 关联查询全部 - * - * @param param 查询参数 - * @return List - */ - List listRel(DormitoryRecordParam param); - - /** - * 根据id查询 - * - * @param id ID - * @return DormitoryRecord - */ - DormitoryRecord getByIdRel(Integer id); - -} diff --git a/src/main/java/com/gxwebsoft/dormitory/service/impl/DormitoryApplyServiceImpl.java b/src/main/java/com/gxwebsoft/dormitory/service/impl/DormitoryApplyServiceImpl.java deleted file mode 100644 index 003fa9a..0000000 --- a/src/main/java/com/gxwebsoft/dormitory/service/impl/DormitoryApplyServiceImpl.java +++ /dev/null @@ -1,47 +0,0 @@ -package com.gxwebsoft.dormitory.service.impl; - -import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; -import com.gxwebsoft.common.core.web.PageParam; -import com.gxwebsoft.common.core.web.PageResult; -import com.gxwebsoft.dormitory.entity.DormitoryApply; -import com.gxwebsoft.dormitory.mapper.DormitoryApplyMapper; -import com.gxwebsoft.dormitory.param.DormitoryApplyParam; -import com.gxwebsoft.dormitory.service.DormitoryApplyService; -import org.springframework.stereotype.Service; - -import java.util.List; - -/** - * 审批管理Service实现 - * - * @author 科技小王子 - * @since 2025-10-03 15:54:30 - */ -@Service -public class DormitoryApplyServiceImpl extends ServiceImpl implements DormitoryApplyService { - - @Override - public PageResult pageRel(DormitoryApplyParam param) { - PageParam page = new PageParam<>(param); - page.setDefaultOrder("sort_number asc, create_time desc"); - List list = baseMapper.selectPageRel(page, param); - return new PageResult<>(list, page.getTotal()); - } - - @Override - public List listRel(DormitoryApplyParam param) { - List list = baseMapper.selectListRel(param); - // 排序 - PageParam page = new PageParam<>(); - page.setDefaultOrder("sort_number asc, create_time desc"); - return page.sortRecords(list); - } - - @Override - public DormitoryApply getByIdRel(Integer id) { - DormitoryApplyParam param = new DormitoryApplyParam(); - param.setId(id); - return param.getOne(baseMapper.selectListRel(param)); - } - -} diff --git a/src/main/java/com/gxwebsoft/dormitory/service/impl/DormitoryBedServiceImpl.java b/src/main/java/com/gxwebsoft/dormitory/service/impl/DormitoryBedServiceImpl.java deleted file mode 100644 index 0014b17..0000000 --- a/src/main/java/com/gxwebsoft/dormitory/service/impl/DormitoryBedServiceImpl.java +++ /dev/null @@ -1,47 +0,0 @@ -package com.gxwebsoft.dormitory.service.impl; - -import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; -import com.gxwebsoft.dormitory.mapper.DormitoryBedMapper; -import com.gxwebsoft.dormitory.service.DormitoryBedService; -import com.gxwebsoft.dormitory.entity.DormitoryBed; -import com.gxwebsoft.dormitory.param.DormitoryBedParam; -import com.gxwebsoft.common.core.web.PageParam; -import com.gxwebsoft.common.core.web.PageResult; -import org.springframework.stereotype.Service; - -import java.util.List; - -/** - * 宿舍床位Service实现 - * - * @author 科技小王子 - * @since 2025-10-03 10:49:27 - */ -@Service -public class DormitoryBedServiceImpl extends ServiceImpl implements DormitoryBedService { - - @Override - public PageResult pageRel(DormitoryBedParam param) { - PageParam page = new PageParam<>(param); - page.setDefaultOrder("sort_number asc, create_time desc"); - List list = baseMapper.selectPageRel(page, param); - return new PageResult<>(list, page.getTotal()); - } - - @Override - public List listRel(DormitoryBedParam param) { - List list = baseMapper.selectListRel(param); - // 排序 - PageParam page = new PageParam<>(); - page.setDefaultOrder("sort_number asc, create_time desc"); - return page.sortRecords(list); - } - - @Override - public DormitoryBed getByIdRel(Integer id) { - DormitoryBedParam param = new DormitoryBedParam(); - param.setId(id); - return param.getOne(baseMapper.selectListRel(param)); - } - -} diff --git a/src/main/java/com/gxwebsoft/dormitory/service/impl/DormitoryBuildingServiceImpl.java b/src/main/java/com/gxwebsoft/dormitory/service/impl/DormitoryBuildingServiceImpl.java deleted file mode 100644 index e15e01e..0000000 --- a/src/main/java/com/gxwebsoft/dormitory/service/impl/DormitoryBuildingServiceImpl.java +++ /dev/null @@ -1,47 +0,0 @@ -package com.gxwebsoft.dormitory.service.impl; - -import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; -import com.gxwebsoft.dormitory.mapper.DormitoryBuildingMapper; -import com.gxwebsoft.dormitory.service.DormitoryBuildingService; -import com.gxwebsoft.dormitory.entity.DormitoryBuilding; -import com.gxwebsoft.dormitory.param.DormitoryBuildingParam; -import com.gxwebsoft.common.core.web.PageParam; -import com.gxwebsoft.common.core.web.PageResult; -import org.springframework.stereotype.Service; - -import java.util.List; - -/** - * 宿舍楼栋Service实现 - * - * @author 科技小王子 - * @since 2025-10-03 10:49:27 - */ -@Service -public class DormitoryBuildingServiceImpl extends ServiceImpl implements DormitoryBuildingService { - - @Override - public PageResult pageRel(DormitoryBuildingParam param) { - PageParam page = new PageParam<>(param); - page.setDefaultOrder("sort_number asc, create_time desc"); - List list = baseMapper.selectPageRel(page, param); - return new PageResult<>(list, page.getTotal()); - } - - @Override - public List listRel(DormitoryBuildingParam param) { - List list = baseMapper.selectListRel(param); - // 排序 - PageParam page = new PageParam<>(); - page.setDefaultOrder("sort_number asc, create_time desc"); - return page.sortRecords(list); - } - - @Override - public DormitoryBuilding getByIdRel(Integer id) { - DormitoryBuildingParam param = new DormitoryBuildingParam(); - param.setId(id); - return param.getOne(baseMapper.selectListRel(param)); - } - -} diff --git a/src/main/java/com/gxwebsoft/dormitory/service/impl/DormitoryFloorServiceImpl.java b/src/main/java/com/gxwebsoft/dormitory/service/impl/DormitoryFloorServiceImpl.java deleted file mode 100644 index 6ddd3c5..0000000 --- a/src/main/java/com/gxwebsoft/dormitory/service/impl/DormitoryFloorServiceImpl.java +++ /dev/null @@ -1,47 +0,0 @@ -package com.gxwebsoft.dormitory.service.impl; - -import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; -import com.gxwebsoft.dormitory.mapper.DormitoryFloorMapper; -import com.gxwebsoft.dormitory.service.DormitoryFloorService; -import com.gxwebsoft.dormitory.entity.DormitoryFloor; -import com.gxwebsoft.dormitory.param.DormitoryFloorParam; -import com.gxwebsoft.common.core.web.PageParam; -import com.gxwebsoft.common.core.web.PageResult; -import org.springframework.stereotype.Service; - -import java.util.List; - -/** - * 宿舍楼层Service实现 - * - * @author 科技小王子 - * @since 2025-10-03 10:49:27 - */ -@Service -public class DormitoryFloorServiceImpl extends ServiceImpl implements DormitoryFloorService { - - @Override - public PageResult pageRel(DormitoryFloorParam param) { - PageParam page = new PageParam<>(param); - page.setDefaultOrder("sort_number asc, create_time desc"); - List list = baseMapper.selectPageRel(page, param); - return new PageResult<>(list, page.getTotal()); - } - - @Override - public List listRel(DormitoryFloorParam param) { - List list = baseMapper.selectListRel(param); - // 排序 - PageParam page = new PageParam<>(); - page.setDefaultOrder("sort_number asc, create_time desc"); - return page.sortRecords(list); - } - - @Override - public DormitoryFloor getByIdRel(Integer id) { - DormitoryFloorParam param = new DormitoryFloorParam(); - param.setId(id); - return param.getOne(baseMapper.selectListRel(param)); - } - -} diff --git a/src/main/java/com/gxwebsoft/dormitory/service/impl/DormitoryRecordServiceImpl.java b/src/main/java/com/gxwebsoft/dormitory/service/impl/DormitoryRecordServiceImpl.java deleted file mode 100644 index a7f218b..0000000 --- a/src/main/java/com/gxwebsoft/dormitory/service/impl/DormitoryRecordServiceImpl.java +++ /dev/null @@ -1,47 +0,0 @@ -package com.gxwebsoft.dormitory.service.impl; - -import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; -import com.gxwebsoft.dormitory.mapper.DormitoryRecordMapper; -import com.gxwebsoft.dormitory.service.DormitoryRecordService; -import com.gxwebsoft.dormitory.entity.DormitoryRecord; -import com.gxwebsoft.dormitory.param.DormitoryRecordParam; -import com.gxwebsoft.common.core.web.PageParam; -import com.gxwebsoft.common.core.web.PageResult; -import org.springframework.stereotype.Service; - -import java.util.List; - -/** - * 宿舍记录Service实现 - * - * @author 科技小王子 - * @since 2025-10-03 10:49:27 - */ -@Service -public class DormitoryRecordServiceImpl extends ServiceImpl implements DormitoryRecordService { - - @Override - public PageResult pageRel(DormitoryRecordParam param) { - PageParam page = new PageParam<>(param); - page.setDefaultOrder("sort_number asc, create_time desc"); - List list = baseMapper.selectPageRel(page, param); - return new PageResult<>(list, page.getTotal()); - } - - @Override - public List listRel(DormitoryRecordParam param) { - List list = baseMapper.selectListRel(param); - // 排序 - PageParam page = new PageParam<>(); - page.setDefaultOrder("sort_number asc, create_time desc"); - return page.sortRecords(list); - } - - @Override - public DormitoryRecord getByIdRel(Integer id) { - DormitoryRecordParam param = new DormitoryRecordParam(); - param.setId(id); - return param.getOne(baseMapper.selectListRel(param)); - } - -} diff --git a/src/main/java/com/gxwebsoft/enterprise/controller/EnterpriseController.java b/src/main/java/com/gxwebsoft/enterprise/controller/EnterpriseController.java deleted file mode 100644 index 819568a..0000000 --- a/src/main/java/com/gxwebsoft/enterprise/controller/EnterpriseController.java +++ /dev/null @@ -1,147 +0,0 @@ -package com.gxwebsoft.enterprise.controller; - -import cn.hutool.core.util.ObjectUtil; -import cn.hutool.core.util.StrUtil; - -import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; -import com.baomidou.mybatisplus.core.metadata.IPage; -import com.baomidou.mybatisplus.extension.plugins.pagination.Page; -import com.gxwebsoft.common.core.web.BaseController; -import com.gxwebsoft.common.core.web.ApiResult; -import com.gxwebsoft.common.core.web.PageResult; -import com.gxwebsoft.common.core.annotation.OperationLog; -import com.gxwebsoft.common.system.entity.User; -import com.gxwebsoft.enterprise.entity.Enterprise; -import com.gxwebsoft.enterprise.service.EnterpriseService; -import io.swagger.v3.oas.annotations.tags.Tag; -import io.swagger.v3.oas.annotations.Operation; -import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.web.bind.annotation.*; - -import javax.annotation.Resource; -import java.util.List; - -/** - * - * @author GIIT-YC - * - */ -@Tag(name = "企业信息管理") -@RestController -@RequestMapping("/api/enterprise/enterprise") -public class EnterpriseController extends BaseController { - - @Resource - private EnterpriseService enterpriseService; - -// @PreAuthorize("hasAuthority('enterprise:enterprise:list')") - @Operation(summary = "分页查询企业信息") - @GetMapping("/page") - public ApiResult> page(Enterprise enterprise) { - LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); - wrapper.like(StrUtil.isNotBlank(enterprise.getName()), Enterprise::getName, enterprise.getName()); - wrapper.like(StrUtil.isNotBlank(enterprise.getCreditCode()), Enterprise::getCreditCode, enterprise.getCreditCode()); - wrapper.orderByAsc(Enterprise::getName); - - final Page page = new Page<>(enterprise.getPage(), enterprise.getLimit()); - final IPage p = enterpriseService.page(page, wrapper); - - return success(new PageResult(p.getRecords(), p.getTotal())); - } - -// @PreAuthorize("hasAuthority('enterprise:enterprise:list')") - @Operation(summary = "查询全部企业信息") - @GetMapping("/list") - public ApiResult> list(Enterprise enterprise) { - LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); - wrapper.like(StrUtil.isNotBlank(enterprise.getName()), Enterprise::getName, enterprise.getName()); - wrapper.like(StrUtil.isNotBlank(enterprise.getCreditCode()), Enterprise::getCreditCode, enterprise.getCreditCode()); - return success(enterpriseService.list(wrapper)); - } - -// @PreAuthorize("hasAuthority('enterprise:enterprise:list')") - @Operation(summary = "根据id查询企业信息") - @GetMapping("/{id}") - public ApiResult get(@PathVariable("id") Integer id) { - return success(enterpriseService.getById(id)); - } - -// @PreAuthorize("hasAuthority('enterprise:enterprise:list')") - @Operation(summary = "根据CreditCode查询企业信息") - @GetMapping("/creditCode/{creditCode}") - public ApiResult getByCreditCode(@PathVariable("creditCode") String creditCode) { - Enterprise enterprise = enterpriseService.getOne(new LambdaQueryWrapper().eq(Enterprise::getCreditCode, creditCode)); - return success(enterprise); - } - -// @PreAuthorize("hasAuthority('enterprise:enterprise:save')") - @OperationLog - @Operation(summary = "添加企业信息") - @PostMapping() - public ApiResult save(@RequestBody Enterprise enterprise) { - // 记录当前登录用户id - User loginUser = getLoginUser(); - if (loginUser != null) { - enterprise.setUserId(loginUser.getUserId()); - final Enterprise one = enterpriseService.getOne(new LambdaQueryWrapper().eq(Enterprise::getCreditCode, enterprise.getCreditCode())); - if (!ObjectUtil.isEmpty(one)) { - return fail("企业统一信用代码已存在"); - } - if (enterpriseService.save(enterprise)) { - //TODO 查询知识库(kb_name=enterprise.getCreditCode) - - //TODO 新建知识库 - String kbId = "pggi9mpair"; - - //绑定知识库 - enterprise.setKbId(kbId); - enterpriseService.updateById(enterprise); - return success("添加成功"); - } - } - return fail("添加失败"); - } - -// @PreAuthorize("hasAuthority('enterprise:enterprise:update')") - @OperationLog - @Operation(summary = "修改企业信息") - @PutMapping() - public ApiResult update(@RequestBody Enterprise enterprise) { - if(StrUtil.isEmpty(enterprise.getKbId())) { - //TODO 查询知识库 - - //TODO 新建知识库 - String kbId = "pggi9mpair"; - - //绑定知识库 - enterprise.setKbId(kbId); - } - if (enterpriseService.updateById(enterprise)) { - return success("修改成功"); - } - return fail("修改失败"); - } - -// @PreAuthorize("hasAuthority('enterprise:enterprise:remove')") - @OperationLog - @Operation(summary = "删除企业信息") - @DeleteMapping("/{id}") - public ApiResult remove(@PathVariable("id") Integer id) { - if (enterpriseService.removeById(id)) { - return success("删除成功"); - } - return fail("删除失败"); - } - -// @PreAuthorize("hasAuthority('enterprise:enterprise:remove')") - @OperationLog - @Operation(summary = "批量删除企业信息") - @DeleteMapping("/batch") - public ApiResult removeBatch(@RequestBody List ids) { - if (enterpriseService.removeByIds(ids)) { - return success("删除成功"); - } - return fail("删除失败"); - } - -} diff --git a/src/main/java/com/gxwebsoft/enterprise/entity/Enterprise.java b/src/main/java/com/gxwebsoft/enterprise/entity/Enterprise.java deleted file mode 100644 index 9e99efd..0000000 --- a/src/main/java/com/gxwebsoft/enterprise/entity/Enterprise.java +++ /dev/null @@ -1,67 +0,0 @@ -package com.gxwebsoft.enterprise.entity; - -import com.baomidou.mybatisplus.annotation.IdType; -import com.baomidou.mybatisplus.annotation.TableId; -import com.baomidou.mybatisplus.annotation.TableLogic; -import com.baomidou.mybatisplus.annotation.TableName; -import com.fasterxml.jackson.annotation.JsonFormat; -import com.gxwebsoft.common.core.web.BaseParam; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; -import lombok.EqualsAndHashCode; - -import java.io.Serializable; -import java.time.LocalDateTime; - -/** - * 企业信息 - * @author GIIT-YC - */ -@Data -@TableName("enterprise") -@EqualsAndHashCode(callSuper = false) -@Schema(name = "Enterprise对象", description = "企业信息表") -public class Enterprise extends BaseParam implements Serializable { - private static final long serialVersionUID = 1L; - - @Schema(description = "ID") - @TableId(value = "id", type = IdType.AUTO) - private Integer id; - - @Schema(description = "企业名称") - private String name; - - @Schema(description = "统一代码") - private String creditCode; - - @Schema(description = "企业性质(国企、行政事业单位、民间非营利组织)") - private String enterpriseType; - - @Schema(description = "所属行业(使用插件)") - private String industry; - - @Schema(description = "知识库ID") - private String kbId; - - @Schema(description = "客户ID") - private Integer userId; - - @Schema(description = "租户ID") - private Integer tenantId; - - @Schema(description = "创建时间") - @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") - private LocalDateTime createTime; - - @Schema(description = "修改时间") - @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") - private LocalDateTime updateTime; - - @Schema(description = "状态, 0正常, 1冻结") - private Integer status; - - @Schema(description = "是否删除, 0否, 1是") - @TableLogic - private Integer deleted; -} \ No newline at end of file diff --git a/src/main/java/com/gxwebsoft/enterprise/mapper/EnterpriseMapper.java b/src/main/java/com/gxwebsoft/enterprise/mapper/EnterpriseMapper.java deleted file mode 100644 index 33ca945..0000000 --- a/src/main/java/com/gxwebsoft/enterprise/mapper/EnterpriseMapper.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.gxwebsoft.enterprise.mapper; - -import com.baomidou.mybatisplus.core.mapper.BaseMapper; -import com.gxwebsoft.enterprise.entity.Enterprise; - -/** - * - * @author GIIT-YC - * - */ -public interface EnterpriseMapper extends BaseMapper { - -} diff --git a/src/main/java/com/gxwebsoft/enterprise/mapper/xml/EnterpriseMapper.xml b/src/main/java/com/gxwebsoft/enterprise/mapper/xml/EnterpriseMapper.xml deleted file mode 100644 index b543162..0000000 --- a/src/main/java/com/gxwebsoft/enterprise/mapper/xml/EnterpriseMapper.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/src/main/java/com/gxwebsoft/enterprise/service/EnterpriseService.java b/src/main/java/com/gxwebsoft/enterprise/service/EnterpriseService.java deleted file mode 100644 index daf3ff8..0000000 --- a/src/main/java/com/gxwebsoft/enterprise/service/EnterpriseService.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.gxwebsoft.enterprise.service; - -import com.baomidou.mybatisplus.extension.service.IService; -import com.gxwebsoft.enterprise.entity.Enterprise; - -/** - * - * @author GIIT-YC - * - */ -public interface EnterpriseService extends IService { - -} diff --git a/src/main/java/com/gxwebsoft/enterprise/service/impl/EnterpriseServiceImpl.java b/src/main/java/com/gxwebsoft/enterprise/service/impl/EnterpriseServiceImpl.java deleted file mode 100644 index a6eedda..0000000 --- a/src/main/java/com/gxwebsoft/enterprise/service/impl/EnterpriseServiceImpl.java +++ /dev/null @@ -1,17 +0,0 @@ -package com.gxwebsoft.enterprise.service.impl; - -import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; -import com.gxwebsoft.enterprise.entity.Enterprise; -import com.gxwebsoft.enterprise.mapper.EnterpriseMapper; -import com.gxwebsoft.enterprise.service.EnterpriseService; -import org.springframework.stereotype.Service; - -/** - * - * @author GIIT-YC - * - */ -@Service -public class EnterpriseServiceImpl extends ServiceImpl implements EnterpriseService { - -} diff --git a/src/test/java/com/gxwebsoft/cms/service/impl/CmsAppServiceImplCacheTest.java b/src/test/java/com/gxwebsoft/cms/service/impl/CmsAppServiceImplCacheTest.java new file mode 100644 index 0000000..6112ed0 --- /dev/null +++ b/src/test/java/com/gxwebsoft/cms/service/impl/CmsAppServiceImplCacheTest.java @@ -0,0 +1,66 @@ +package com.gxwebsoft.cms.service.impl; + +import com.gxwebsoft.cms.entity.CmsApp; +import com.gxwebsoft.cms.mapper.CmsAppMapper; +import com.gxwebsoft.cms.service.CmsNavigationService; +import com.gxwebsoft.common.core.utils.RedisUtil; +import com.gxwebsoft.shop.vo.ShopVo; +import org.junit.jupiter.api.Test; + +import java.lang.reflect.Field; +import java.util.Collections; + +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +class CmsAppServiceImplCacheTest { + + @Test + void getSiteInfo_cacheValueIsJsonNull_shouldFallbackToDb() throws Exception { + Integer tenantId = 1; + + RedisUtil redisUtil = mock(RedisUtil.class); + CmsNavigationService cmsNavigationService = mock(CmsNavigationService.class); + CmsAppMapper cmsAppMapper = mock(CmsAppMapper.class); + + // 历史缓存可能存在字符串 "null";Jackson 解析后会得到 null,需回源 DB。 + when(redisUtil.get("SiteInfo:" + tenantId)).thenReturn("null"); + when(cmsNavigationService.listRel(any())).thenReturn(Collections.emptyList()); + + CmsApp website = new CmsApp(); + website.setTenantId(tenantId); + website.setTenantName("tenant"); + website.setAppName("site"); + when(cmsAppMapper.getByTenantId(tenantId)).thenReturn(website); + + CmsAppServiceImpl service = new CmsAppServiceImpl(); + setField(service, "redisUtil", redisUtil); + setField(service, "cmsNavigationService", cmsNavigationService); + setField(service, "baseMapper", cmsAppMapper); + + ShopVo vo = service.getSiteInfo(tenantId); + + assertNotNull(vo); + verify(redisUtil).delete("SiteInfo:" + tenantId); + verify(cmsAppMapper).getByTenantId(tenantId); + } + + private static void setField(Object target, String fieldName, Object value) throws Exception { + Class c = target.getClass(); + while (c != null) { + try { + Field f = c.getDeclaredField(fieldName); + f.setAccessible(true); + f.set(target, value); + return; + } catch (NoSuchFieldException ignored) { + c = c.getSuperclass(); + } + } + throw new NoSuchFieldException(fieldName); + } +} +