From 6bc968ca6f6173df1fddc3da7c5af224875f7c0d Mon Sep 17 00:00:00 2001 From: dmz Date: Fri, 5 Dec 2025 23:08:31 +0800 Subject: [PATCH] =?UTF-8?q?=E8=AF=95=E4=B8=80=E4=B8=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 31 +++ amms_front/.gitignore | 30 +++ amms_front/src/App.vue | 10 + amms_front/src/api/announcement.js | 51 +++++ amms_front/src/assets/base.css | 86 +++++++ amms_front/src/assets/css/apple-input.css | 89 ++++++++ sql/amms.sql | 215 ++++++++++++++++++ sql/app.sql | 18 ++ src/main/java/com/amms/Application.java | 13 ++ .../controller/AnnouncementController.java | 106 +++++++++ .../java/com/amms/domain/Announcement.java | 133 +++++++++++ .../dto/BatchAddReservationTimeSlotParam.java | 31 +++ .../com/amms/mapper/AnnouncementMapper.java | 62 +++++ .../service/impl/AnnouncementServiceImpl.java | 116 ++++++++++ src/main/resources/application.yml | 70 ++++++ .../resources/mapper/AnnouncementMapper.xml | 101 ++++++++ .../example/bsbase2025/ApplicationTests.java | 14 ++ 文件/22150110017陈素霞任务书.docx | Bin 0 -> 18398 bytes 18 files changed, 1176 insertions(+) create mode 100644 .gitignore create mode 100644 amms_front/.gitignore create mode 100644 amms_front/src/App.vue create mode 100644 amms_front/src/api/announcement.js create mode 100644 amms_front/src/assets/base.css create mode 100644 amms_front/src/assets/css/apple-input.css create mode 100644 sql/amms.sql create mode 100644 sql/app.sql create mode 100644 src/main/java/com/amms/Application.java create mode 100644 src/main/java/com/amms/controller/AnnouncementController.java create mode 100644 src/main/java/com/amms/domain/Announcement.java create mode 100644 src/main/java/com/amms/domain/dto/BatchAddReservationTimeSlotParam.java create mode 100644 src/main/java/com/amms/mapper/AnnouncementMapper.java create mode 100644 src/main/java/com/amms/service/impl/AnnouncementServiceImpl.java create mode 100644 src/main/resources/application.yml create mode 100644 src/main/resources/mapper/AnnouncementMapper.xml create mode 100644 src/test/java/org/example/bsbase2025/ApplicationTests.java create mode 100644 文件/22150110017陈素霞任务书.docx diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b559d64 --- /dev/null +++ b/.gitignore @@ -0,0 +1,31 @@ + + +.idea/ +.trae/ +target + +# ---> Java +# Compiled class file +*.class + +# Log file +*.log + +# BlueJ files +*.ctxt + +# Mobile Tools for Java (J2ME) +.mtj.tmp/ + +# Package Files # +*.jar +*.war +*.nar +*.ear +*.zip +*.tar.gz +*.rar + +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml +hs_err_pid* +replay_pid* diff --git a/amms_front/.gitignore b/amms_front/.gitignore new file mode 100644 index 0000000..8ee54e8 --- /dev/null +++ b/amms_front/.gitignore @@ -0,0 +1,30 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +.DS_Store +dist +dist-ssr +coverage +*.local + +/cypress/videos/ +/cypress/screenshots/ + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? + +*.tsbuildinfo diff --git a/amms_front/src/App.vue b/amms_front/src/App.vue new file mode 100644 index 0000000..7e961a2 --- /dev/null +++ b/amms_front/src/App.vue @@ -0,0 +1,10 @@ + + + + + diff --git a/amms_front/src/api/announcement.js b/amms_front/src/api/announcement.js new file mode 100644 index 0000000..16b1f64 --- /dev/null +++ b/amms_front/src/api/announcement.js @@ -0,0 +1,51 @@ +import axios from '@/utils/request' + +// 查询公告列表 +export function listAnnouncement(query) { + return axios.get('/announcement/list',{ + params: { + ...query + } + }) +} + +// 查询全部公告列表 +export function listAllAnnouncement(query) { + return axios.get('/announcement/listAll', { + params: { + ...query + } + }) +} + +// 查询公告详细 +export function getAnnouncement(id) { + return axios.get('/announcement/info/' + id) +} + +// 新增公告 +export function addAnnouncement(data) { + return axios.post('/announcement/add', { + ...data + }) +} + +// 修改公告 +export function updateAnnouncement(data) { + return axios.put('/announcement', data) +} + +// 设为置顶(保证仅一个置顶) +export function setTopAnnouncement(id) { + return axios.put('/announcement/setTop/' + id) +} + +// 删除公告 +export function delAnnouncement(id) { + return axios.delete('/announcement/' + id) +} + +// 游客端公告列表(置顶 + 普通分页) +export function listTouristAnnouncements(params) { + return axios.get('/announcement/touristList', { params }) +} diff --git a/amms_front/src/assets/base.css b/amms_front/src/assets/base.css new file mode 100644 index 0000000..8816868 --- /dev/null +++ b/amms_front/src/assets/base.css @@ -0,0 +1,86 @@ +/* color palette from */ +:root { + --vt-c-white: #ffffff; + --vt-c-white-soft: #f8f8f8; + --vt-c-white-mute: #f2f2f2; + + --vt-c-black: #181818; + --vt-c-black-soft: #222222; + --vt-c-black-mute: #282828; + + --vt-c-indigo: #2c3e50; + + --vt-c-divider-light-1: rgba(60, 60, 60, 0.29); + --vt-c-divider-light-2: rgba(60, 60, 60, 0.12); + --vt-c-divider-dark-1: rgba(84, 84, 84, 0.65); + --vt-c-divider-dark-2: rgba(84, 84, 84, 0.48); + + --vt-c-text-light-1: var(--vt-c-indigo); + --vt-c-text-light-2: rgba(60, 60, 60, 0.66); + --vt-c-text-dark-1: var(--vt-c-white); + --vt-c-text-dark-2: rgba(235, 235, 235, 0.64); +} + +/* semantic color variables for this project */ +:root { + --color-background: var(--vt-c-white); + --color-background-soft: var(--vt-c-white-soft); + --color-background-mute: var(--vt-c-white-mute); + + --color-border: var(--vt-c-divider-light-2); + --color-border-hover: var(--vt-c-divider-light-1); + + --color-heading: var(--vt-c-text-light-1); + --color-text: var(--vt-c-text-light-1); + + --section-gap: 160px; +} + +@media (prefers-color-scheme: dark) { + :root { + --color-background: var(--vt-c-black); + --color-background-soft: var(--vt-c-black-soft); + --color-background-mute: var(--vt-c-black-mute); + + --color-border: var(--vt-c-divider-dark-2); + --color-border-hover: var(--vt-c-divider-dark-1); + + --color-heading: var(--vt-c-text-dark-1); + --color-text: var(--vt-c-text-dark-2); + } +} + +*, +*::before, +*::after { + box-sizing: border-box; + margin: 0; + font-weight: normal; +} + +body { + min-height: 100vh; + color: var(--color-text); + background: var(--color-background); + transition: + color 0.5s, + background-color 0.5s; + line-height: 1.6; + font-family: + Inter, + -apple-system, + BlinkMacSystemFont, + 'Segoe UI', + Roboto, + Oxygen, + Ubuntu, + Cantarell, + 'Fira Sans', + 'Droid Sans', + 'Helvetica Neue', + sans-serif; + font-size: 15px; + text-rendering: optimizeLegibility; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} diff --git a/amms_front/src/assets/css/apple-input.css b/amms_front/src/assets/css/apple-input.css new file mode 100644 index 0000000..9f649ca --- /dev/null +++ b/amms_front/src/assets/css/apple-input.css @@ -0,0 +1,89 @@ +/* + Apple风格Element Plus输入框公共样式 + 使用方法:在需要应用样式的el-input上添加 class="apple-input" + 示例: +*/ + +/* 基础样式 - 通过自定义类名限定范围 */ +.el-input.apple-input .el-input__wrapper { + background-color: rgba(255, 255, 255, 0.28) !important; + border: 1px solid rgba(255, 255, 255, 0.2) !important; + border-radius: 0.5rem !important; + padding: 0 1rem !important; + height: 2.75rem !important; + transition: all 0.3s ease !important; +} + +/* 悬停状态 */ +.el-input.apple-input .el-input__wrapper:hover { + border-color: rgba(255, 255, 255, 0.3) !important; + box-shadow: none !important; +} + +/* 聚焦状态 */ +.el-input.apple-input .el-input__wrapper:focus-within { + border-color: #0071e3 !important; /* Apple标志性蓝色 */ + box-shadow: 0 0 0 1px #0071e3 !important; +} + +/* 输入文本样式 */ +.el-input.apple-input .el-input__input { + color: white !important; + padding: 0 !important; + height: 100% !important; +} + +/* 占位符样式 */ +.el-input.apple-input .el-input__input::placeholder { + color: white !important; +} + +/* 后缀图标(如密码可见性切换) */ +.el-input.apple-input .el-input__suffix-inner { + color: rgba(255, 255, 255, 0.6) !important; +} + +.el-input.apple-input .el-input__suffix-inner:hover { + color: white !important; +} + +/* 清除按钮 */ +.el-input.apple-input .el-input__clear { + color: rgba(255, 255, 255, 0.6) !important; +} + +.el-input.apple-input .el-input__clear:hover { + color: white !important; + background-color: rgba(255, 255, 255, 0.1) !important; +} + +/* 前缀图标 */ +.el-input.apple-input .el-input__prefix { + color: rgba(255, 255, 255, 0.6) !important; +} + +/* 可选变体:更大的输入框 */ +.el-input.apple-input.large .el-input__wrapper { + height: 3.25rem !important; + font-size: 1rem !important; +} + +/* 可选变体:更小的输入框 */ +.el-input.apple-input.small .el-input__wrapper { + height: 2.25rem !important; + font-size: 0.875rem !important; +} + +/* 可选变体:浅色模式适配 */ +.el-input.apple-input.light .el-input__wrapper { + background-color: rgba(0, 0, 0, 0.05) !important; + border: 1px solid rgba(0, 0, 0, 0.1) !important; +} + +.el-input.apple-input.light .el-input__input { + color: #1d1d1f !important; +} + +.el-input.apple-input.light .el-input__input::placeholder { + color: rgba(0, 0, 0, 0.4) !important; +} diff --git a/sql/amms.sql b/sql/amms.sql new file mode 100644 index 0000000..3d4e3f0 --- /dev/null +++ b/sql/amms.sql @@ -0,0 +1,215 @@ +-- 用户主表(共用属性) +DROP TABLE IF EXISTS `sys_user`; +CREATE TABLE `sys_user` ( + `id` bigint NOT NULL AUTO_INCREMENT COMMENT '用户id', + `username` varchar(30) NOT NULL COMMENT '账号', + `password` varchar(100) DEFAULT '' COMMENT '密码', + `nickname` varchar(30) NOT NULL COMMENT '昵称', + `avatar` varchar(100) DEFAULT '' COMMENT '头像地址', + `status` int DEFAULT 0 COMMENT '状态(0正常 1停用 2待审核)', + `role` varchar(10) NOT NULL COMMENT '角色(1超级管理员 2普通用户)', + `creator` bigint DEFAULT NULL COMMENT '创建者id', + `create_time` datetime DEFAULT NULL COMMENT '创建时间', + `updater` bigint DEFAULT NULL COMMENT '更新者id', + `update_time` datetime DEFAULT NULL COMMENT '更新时间', + `remark` varchar(500) DEFAULT NULL COMMENT '备注', + `deleted` tinyint DEFAULT 0 COMMENT '删除标识(0未删 1已删)', + PRIMARY KEY (`id`), + UNIQUE KEY `idx_sys_user_username` (`username`) +) COMMENT='用户主表'; +-- 超级管理员 admin 密码:123456 +INSERT INTO `sys_user` (`username`, `password`, `nickname`, `avatar`, `status`, `role`, `creator`, `create_time`, `updater`, `update_time`, `remark`, `deleted`) +VALUES ('admin', '$2a$10$sSTvaDj1QtrEIgEglkumguRT88m0WRs61gWw7Rgm/gpP6/eZ4ffwG', '超级管理员', '', 0, '1', NULL, NOW(), NULL, NOW(), '系统默认超级管理员', 0); + +-- 普通用户(游客) +DROP TABLE IF EXISTS `tourist`; +CREATE TABLE `tourist` ( + `id` bigint NOT NULL COMMENT '用户id(与sys_user.id一致)', + `real_name` varchar(30) DEFAULT NULL COMMENT '真实姓名', + `id_card` varchar(18) DEFAULT NULL COMMENT '身份证号', + `phone` varchar(11) DEFAULT NULL COMMENT '手机号', + `email` varchar(50) DEFAULT NULL COMMENT '邮箱', + `update_time` datetime DEFAULT NULL COMMENT '更新时间', + PRIMARY KEY (`id`), + UNIQUE KEY `idx_sys_user_normal_phone` (`phone`), + UNIQUE KEY `idx_sys_user_normal_email` (`email`) +) COMMENT='普通用户子表' + +-- 轮播图表 +DROP TABLE IF EXISTS `carousel`; +CREATE TABLE `carousel` ( + `id` bigint NOT NULL AUTO_INCREMENT COMMENT '轮播图id', + `title` varchar(50) NOT NULL COMMENT '标题', + `image_url` varchar(255) NOT NULL COMMENT '图片地址', + `link` varchar(255) DEFAULT NULL COMMENT '跳转链接', + `sort` int DEFAULT 0 COMMENT '排序序号', + `status` tinyint DEFAULT 1 COMMENT '状态(0隐藏 1显示)', + `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `update_time` datetime DEFAULT NULL COMMENT '更新时间', + PRIMARY KEY (`id`), + KEY `idx_carousel_status` (`status`) +) COMMENT='轮播图'; + +-- 博物馆简介表 +DROP TABLE IF EXISTS `museum_intro`; +CREATE TABLE `museum_intro` ( + `museum_name` varchar(100) NOT NULL COMMENT '博物馆名称(主键)', + `content` text NOT NULL COMMENT '内容', + `logo_url` varchar(255) DEFAULT NULL COMMENT 'Logo地址', + `address` varchar(100) DEFAULT NULL COMMENT '地址', + `phone` varchar(20) DEFAULT NULL COMMENT '联系电话', + `open_time` time DEFAULT NULL COMMENT '营业开始时间', + `close_time` time DEFAULT NULL COMMENT '营业结束时间', + `update_time` datetime DEFAULT NULL COMMENT '更新时间', + `updater` bigint DEFAULT NULL COMMENT '更新者id', + PRIMARY KEY (`museum_name`) +) COMMENT='博物馆简介'; +INSERT INTO `museum_intro` ( + `museum_name`, + `content`, + `logo_url`, + `address`, + `phone`, + `open_time`, + `close_time`, + `update_time`, + `updater` +) VALUES ( + '邺城博物馆', + '邺城博物馆位于河北省邺城遗址保护区,致力于系统展示东魏、北齐时期的城市文明与考古成果。馆内设基本陈列、专题展厅与数字互动空间,重点呈现佛教造像、石刻艺术、陶瓷器与城址考古发掘成果,并提供文物保护科普、研学课程与公众教育服务。', + '/api/files/get?fileName=museum/logo/yecity.png', + '河北省临漳县邺城遗址博物馆', + '0310-8888888', + '09:00:00', + '17:30:00', + NOW(), + 1 +); + +-- 藏品分类表 +DROP TABLE IF EXISTS `item_category`; +CREATE TABLE `item_category` ( + `id` bigint NOT NULL AUTO_INCREMENT PRIMARY KEY COMMENT '分类ID', + `name` varchar(50) NOT NULL COMMENT '分类名称', + `creator` bigint NULL COMMENT '创建者', + `create_time` datetime NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `updater` bigint NULL COMMENT '更新者', + `update_time` datetime NULL DEFAULT NULL COMMENT '更新时间', + `remark` varchar(500) NULL COMMENT '备注' +) COMMENT = '藏品分类'; + + +-- 藏品主表 +DROP TABLE IF EXISTS `relic`; +CREATE TABLE `relic` ( + `id` bigint NOT NULL AUTO_INCREMENT COMMENT '藏品id', + `name` varchar(100) NOT NULL COMMENT '名称', + `category_id` bigint NOT NULL COMMENT '分类ID', + `cover_image_url` varchar(255) NOT NULL COMMENT '封面图地址', + `age` varchar(50) DEFAULT NULL COMMENT '年代', + `material` varchar(50) DEFAULT NULL COMMENT '材质', + `excavation_info` text DEFAULT NULL COMMENT '出土信息', + `story` text DEFAULT NULL COMMENT '文物故事', + `model_url` text DEFAULT NULL COMMENT '3D/360°图地址', + `is_hot` tinyint DEFAULT 0 COMMENT '热门标识(0否 1是)', + `status` tinyint DEFAULT 1 COMMENT '状态(0隐藏 1显示)', + `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `creator` bigint DEFAULT NULL COMMENT '创建者id', + `update_time` datetime DEFAULT NULL COMMENT '更新时间', + `updater` bigint DEFAULT NULL COMMENT '更新者id', + PRIMARY KEY (`id`) +) COMMENT = '藏品'; + +-- 测试数据:邺城博物馆热门/普通藏品(15条) +INSERT INTO `relic` (`name`, `category_id`, `cover_image_url`, `age`, `material`, `excavation_info`, `story`, `model_url`, `is_hot`, `status`, `create_time`, `creator`, `update_time`, `updater`) VALUES +('北齐石刻佛像', 1, '/api/files/get?fileName=relicCover/bei_qi_shike_foxiang.jpg', '北齐', '石质', '邺城遗址石窟区出土', '北齐时期石刻造像,线条劲挺,面相安详。', NULL, 1, 1, NOW(), 1, NOW(), 1), +('东魏青釉瓷碗', 2, '/api/files/get?fileName=relicCover/dong_wei_qingyou_ciwang.jpg', '东魏', '陶瓷', '邺城城址文化层出土', '胎质细腻,通体青釉,器形规整。', NULL, 1, 1, NOW(), 1, NOW(), 1), +('北齐石柱础', 1, '/api/files/get?fileName=relicCover/bei_qi_zhuchu.jpg', '北齐', '石质', '邺城宫殿区基址出土', '柱础覆莲纹,雕刻工整,体现工艺水准。', NULL, 0, 1, NOW(), 1, NOW(), 1), +('东魏鎏金饰片', 3, '/api/files/get?fileName=relicCover/dong_wei_liujin_shipian.jpg', '东魏', '金属', '邺城遗址手工业作坊出土', '鎏金工艺精湛,纹饰细密。', NULL, 1, 1, NOW(), 1, NOW(), 1), +('北齐陶俑', 2, '/api/files/get?fileName=relicCover/bei_qi_taoyong.jpg', '北齐', '陶', '邺城墓葬出土', '俑身比例匀称,服饰简洁,表情生动。', NULL, 0, 1, NOW(), 1, NOW(), 1), +('邺城瓦当', 4, '/api/files/get?fileName=relicCover/yecity_wadang.jpg', '北齐', '陶', '邺城宫殿区瓦件堆积出土', '瓦当纹饰清晰,常见卷云与莲纹。', NULL, 1, 1, NOW(), 1, NOW(), 1), +('邺城石刻残片', 1, '/api/files/get?fileName=relicCover/yecity_shike_canpian.jpg', '北齐', '石质', '邺城寺院区出土', '残片可见线刻与浅浮雕技法。', NULL, 0, 1, NOW(), 1, NOW(), 1), +('北齐碑刻', 1, '/api/files/get?fileName=relicCover/bei_qi_beike.jpg', '北齐', '石质', '邺城遗址碑廓出土', '字迹峻拔,为研究书法与历史提供资料。', NULL, 1, 1, NOW(), 1, NOW(), 1), +('邺城铜镜', 3, '/api/files/get?fileName=relicCover/yecity_tongjing.jpg', '北齐', '青铜', '邺城民居遗址出土', '背纹精美,折射古代生活审美。', NULL, 0, 1, NOW(), 1, NOW(), 1), +('邺城陶灯', 2, '/api/files/get?fileName=relicCover/yecity_taodeng.jpg', '东魏', '陶', '邺城手工业区出土', '造型朴素,实用性强。', NULL, 0, 1, NOW(), 1, NOW(), 1), +('邺城青铜器', 3, '/api/files/get?fileName=relicCover/yecity_qingtongqi.jpg', '北齐', '青铜', '邺城祭祀遗址出土', '铸造规整,纹饰典雅。', NULL, 1, 1, NOW(), 1, NOW(), 1), +('石雕护法像', 1, '/api/files/get?fileName=relicCover/shidiao_hufa.jpg', '北齐', '石质', '邺城寺院区出土', '表情威严,衣纹流畅,守护佛法之象。', NULL, 1, 1, NOW(), 1, NOW(), 1), +('鎏金佛像', 3, '/api/files/get?fileName=relicCover/liujin_foxiang.jpg', '北齐', '金属', '邺城佛教遗迹出土', '通体鎏金,庄严华美。', NULL, 1, 1, NOW(), 1, NOW(), 1), +('邺城瓷瓶', 2, '/api/files/get?fileName=relicCover/yecity_ciping.jpg', '东魏', '陶瓷', '邺城城址出土', '瓶身修长,青釉匀净。', NULL, 0, 1, NOW(), 1, NOW(), 1), +('石经残卷', 1, '/api/files/get?fileName=relicCover/shijing_canjuan.jpg', '北齐', '石质', '邺城经藏区出土', '石经残刻,见证佛教经典传播。', NULL, 0, 1, NOW(), 1, NOW(), 1); + +-- 公告表 +DROP TABLE IF EXISTS `announcement`; +CREATE TABLE `announcement` ( + `id` bigint NOT NULL AUTO_INCREMENT COMMENT '公告id', + `title` varchar(100) NOT NULL COMMENT '标题', + `content` text NOT NULL COMMENT '内容', + `is_top` tinyint DEFAULT 0 COMMENT '置顶标识(0否 1是)', + `status` tinyint DEFAULT 1 COMMENT '状态(0隐藏 1显示)', + `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '发布时间', + `creator` bigint DEFAULT NULL COMMENT '发布者id', + `update_time` datetime DEFAULT NULL COMMENT '更新时间', + `updater` bigint DEFAULT NULL COMMENT '更新者id', + PRIMARY KEY (`id`) +) COMMENT='公告'; + +-- 预约时段表 +DROP TABLE IF EXISTS `reservation_time_slot`; +CREATE TABLE `reservation_time_slot` ( + `id` bigint NOT NULL AUTO_INCREMENT COMMENT '时段id', + `date` date NOT NULL COMMENT '预约日期', + `start_time` time NOT NULL COMMENT '开始时间', + `end_time` time NOT NULL COMMENT '结束时间', + `max_people` int NOT NULL COMMENT '最大人数', + `current_people` int DEFAULT 0 COMMENT '已预约人数', + `status` tinyint DEFAULT 1 COMMENT '状态(0不可约 1可约)', + `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `creator` bigint DEFAULT NULL COMMENT '创建者id', + PRIMARY KEY (`id`) +) COMMENT='预约时段'; + +-- 预约记录表 +DROP TABLE IF EXISTS `reservation`; +CREATE TABLE `reservation` ( + `id` bigint NOT NULL AUTO_INCREMENT COMMENT '预约id(主表ID)', + `user_id` bigint NOT NULL COMMENT '预约发起者id(关联sys_user.id)', + `time_slot_id` bigint NOT NULL COMMENT '预约时段id(关联reservation_time_slot.id)', + `total_visitors` int NOT NULL COMMENT '预约总人数(与明细表记录数一致)', + `reserve_time` time NOT NULL COMMENT '预约时间', + `qr_code` varchar(255) DEFAULT NULL COMMENT '预约凭证二维码(整单唯一)', + `status` tinyint DEFAULT 0 COMMENT '整单状态(0待审核 1已通过 2已取消 3已驳回)', + `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '预约提交时间', + `update_time` datetime DEFAULT NULL COMMENT '审核/更新时间', + `updater` bigint DEFAULT NULL COMMENT '审核者id(关联sys_user.id)', + `remark` varchar(500) DEFAULT NULL COMMENT '审核备注(如驳回原因)', + PRIMARY KEY (`id`), + KEY `idx_user_id` (`user_id`), + KEY `idx_time_slot_id` (`time_slot_id`), + KEY `idx_status` (`status`) +) COMMENT='预约'; + +-- 预约明细表 +DROP TABLE IF EXISTS `reservation_visitor`; +CREATE TABLE `reservation_visitor` ( + `id` bigint NOT NULL AUTO_INCREMENT COMMENT '明细ID', + `reservation_id` bigint NOT NULL COMMENT '预约id', + `real_name` varchar(30) NOT NULL COMMENT '游客真实姓名', + `id_card` varchar(18) NOT NULL COMMENT '游客身份证号(唯一标识游客)', + `phone` varchar(11) NOT NULL COMMENT '游客手机号', + `visitor_qr_code` varchar(500) DEFAULT NULL COMMENT '游客个人入场二维码(可选,如分人验证)', + `verify_status` tinyint DEFAULT 0 COMMENT '游客入场验证状态(0未验证 1已验证)', + `verify_time` datetime DEFAULT NULL COMMENT '入场验证时间', + PRIMARY KEY (`id`), + KEY `idx_reservation_id` (`reservation_id`), + KEY `idx_id_card` (`id_card`) +) COMMENT='游客预约明细'; + +-- 收藏表 +DROP TABLE IF EXISTS `item_collection`; +CREATE TABLE `item_collection` ( + `id` bigint NOT NULL AUTO_INCREMENT COMMENT '收藏id', + `user_id` bigint NOT NULL COMMENT '用户id', + `item_id` bigint NOT NULL COMMENT '藏品id', + `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '收藏时间', + PRIMARY KEY (`id`) +) COMMENT='收藏'; diff --git a/sql/app.sql b/sql/app.sql new file mode 100644 index 0000000..584c85c --- /dev/null +++ b/sql/app.sql @@ -0,0 +1,18 @@ +DROP TABLE IF EXISTS `sys_user`; +CREATE TABLE `sys_user` ( + id bigint not null auto_increment primary key comment '用户ID', + username varchar(30) not null comment '用户账号', + nickname varchar(30) not null comment '用户昵称', + mobile varchar(11) null default '' comment '手机号码', + sex int null DEFAULT 0 comment '用户性别(0男 1女 2未知)', + avatar varchar(100) null default '' comment '头像地址', + password varchar(100) null default '' comment '密码', + status int null default 0 comment '帐号状态(0正常 1停用)', + role varchar(20) not null comment '角色 admin管理员', + creator bigint null default 1 comment '创建者', + create_time datetime null default null comment '创建时间', + updater bigint null default 1 comment '更新者', + update_time datetime null default null comment '更新时间', + remark varchar(500) null default null comment '备注', + deleted tinyint null default 0 comment '删除标识' +) comment = '用户'; diff --git a/src/main/java/com/amms/Application.java b/src/main/java/com/amms/Application.java new file mode 100644 index 0000000..d280227 --- /dev/null +++ b/src/main/java/com/amms/Application.java @@ -0,0 +1,13 @@ +package com.amms; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class Application { + + public static void main(String[] args) { + SpringApplication.run(Application.class, args); + } + +} diff --git a/src/main/java/com/amms/controller/AnnouncementController.java b/src/main/java/com/amms/controller/AnnouncementController.java new file mode 100644 index 0000000..66de09c --- /dev/null +++ b/src/main/java/com/amms/controller/AnnouncementController.java @@ -0,0 +1,106 @@ +package com.amms.controller; + +import com.github.pagehelper.PageHelper; +import com.github.pagehelper.PageInfo; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; +import com.amms.domain.vo.Result; +import com.amms.domain.Announcement; +import com.amms.service.IAnnouncementService; + +import java.util.List; +import java.util.HashMap; +import java.util.Map; + +/** + * 公告Controller + */ +@RestController +@RequestMapping("/announcement") +public class AnnouncementController { + + @Autowired + private IAnnouncementService announcementService; + + /** + * 查询公告列表 + */ + @GetMapping("/list") + public PageInfo list(Announcement announcement, @RequestParam("pageNum") Integer pageNum, @RequestParam("pageSize") Integer pageSize) { + PageHelper.startPage(pageNum, pageSize); + List announcements = announcementService.selectAnnouncementList(announcement); + return new PageInfo(announcements); + } + + /** + * 查询全部公告列表 + */ + @GetMapping("/listAll") + public List listAll(Announcement announcement) { + return announcementService.selectAnnouncementList(announcement); + } + + /** + * 获取公告详细信息 + */ + @GetMapping(value = "/info/{id}") + public Result getInfo(@PathVariable("id") Long id) { + return Result.success(announcementService.selectAnnouncementById(id)); + } + + /** + * 新增公告 + */ + @PostMapping("/add") + public Result add(@RequestBody Announcement announcement) { + return announcementService.insertAnnouncement(announcement) > 0 ? Result.success("新增成功") : Result.error("新增失败"); + } + + /** + * 修改公告 + */ + @PutMapping + public Result update(@RequestBody Announcement announcement) { + return announcementService.updateAnnouncement(announcement) > 0 ? Result.success("修改成功") : Result.error("修改失败"); + } + + /** + * 删除公告 + */ + @DeleteMapping("/{id}") + public Result delete(@PathVariable Long id) { + return announcementService.deleteAnnouncementById(id) > 0 ? Result.success("删除成功") : Result.error("删除失败"); + } + + /** + * 设为置顶(全局仅一个置顶) + */ + @PutMapping("/setTop/{id}") + public Result setTop(@PathVariable Long id) { + return announcementService.setTop(id) > 0 ? Result.success("置顶成功") : Result.error("置顶失败"); + } + + /** + * 游客端公告列表(置顶 + 普通分页) + * + * 返回: + * topList - 置顶公告列表 + * list - 普通公告当前页数据 + * total - 普通公告总条数 + */ + @GetMapping("/touristList") + public Result touristList(@RequestParam("pageNum") Integer pageNum, @RequestParam("pageSize") Integer pageSize, + @RequestParam(value = "title", required = false) String title) { + List topList = announcementService.selectTopAnnouncements(); + PageHelper.startPage(pageNum, pageSize); + Announcement filter = new Announcement(); + filter.setTitle(title); + List normals = announcementService.selectNormalAnnouncements(filter); + PageInfo pageInfo = new PageInfo(normals); + Map data = new HashMap<>(); + data.put("topList", topList); + data.put("list", pageInfo.getList()); + data.put("total", pageInfo.getTotal()); + return Result.success(data); + } +} diff --git a/src/main/java/com/amms/domain/Announcement.java b/src/main/java/com/amms/domain/Announcement.java new file mode 100644 index 0000000..8df6743 --- /dev/null +++ b/src/main/java/com/amms/domain/Announcement.java @@ -0,0 +1,133 @@ +package com.amms.domain; + + +import com.fasterxml.jackson.annotation.JsonFormat; +import org.springframework.format.annotation.DateTimeFormat; + +import java.util.Date; + +/** + * 公告对象 announcement + */ +public class Announcement { + + /** 公告id */ + private Long id; + + /** 标题 */ + private String title; + + /** 内容 */ + private String content; + + /** 置顶标识(0否 1是) */ + private Long isTop; + + /** 状态(0隐藏 1显示) */ + private Long status; + + /** 发布者id */ + private Long creator; + + /** 更新者id */ + private Long updater; + + /** 创建时间 */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date createTime; + + /** 更新时间 */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date updateTime; + + public void setId(Long id) { + this.id = id; + } + + public Long getId() { + return id; + } + + public void setTitle(String title) { + this.title = title; + } + + public String getTitle() { + return title; + } + + public void setContent(String content) { + this.content = content; + } + + public String getContent() { + return content; + } + + public void setIsTop(Long isTop) { + this.isTop = isTop; + } + + public Long getIsTop() { + return isTop; + } + + public void setStatus(Long status) { + this.status = status; + } + + public Long getStatus() { + return status; + } + + + public void setCreator(Long creator) { + this.creator = creator; + } + + public Long getCreator() { + return creator; + } + + + public void setUpdater(Long updater) { + this.updater = updater; + } + + public Long getUpdater() { + return updater; + } + + public Date getCreateTime() { + return createTime; + } + + public void setCreateTime(Date createTime) { + this.createTime = createTime; + } + + public Date getUpdateTime() { + return updateTime; + } + + public void setUpdateTime(Date updateTime) { + this.updateTime = updateTime; + } + + @Override + public String toString() { + return "Announcement{" + + "id=" + id + + ", title=" + title + + ", content=" + content + + ", isTop=" + isTop + + ", status=" + status + + ", createTime=" + createTime + + ", creator=" + creator + + ", updateTime=" + updateTime + + ", updater=" + updater + + '}'; + } +} diff --git a/src/main/java/com/amms/domain/dto/BatchAddReservationTimeSlotParam.java b/src/main/java/com/amms/domain/dto/BatchAddReservationTimeSlotParam.java new file mode 100644 index 0000000..c4de86f --- /dev/null +++ b/src/main/java/com/amms/domain/dto/BatchAddReservationTimeSlotParam.java @@ -0,0 +1,31 @@ +package com.amms.domain.dto; + +import com.fasterxml.jackson.annotation.JsonFormat; + +import java.util.Date; + +public class BatchAddReservationTimeSlotParam { + @JsonFormat(pattern = "yyyy-MM-dd") + private Date startDate; + @JsonFormat(pattern = "yyyy-MM-dd") + private Date endDate; + @JsonFormat(pattern = "HH:mm:ss") + private Date startTime; + @JsonFormat(pattern = "HH:mm:ss") + private Date endTime; + private Integer maxPeople; + private Long status; + + public Date getStartDate() { return startDate; } + public void setStartDate(Date startDate) { this.startDate = startDate; } + public Date getEndDate() { return endDate; } + public void setEndDate(Date endDate) { this.endDate = endDate; } + public Date getStartTime() { return startTime; } + public void setStartTime(Date startTime) { this.startTime = startTime; } + public Date getEndTime() { return endTime; } + public void setEndTime(Date endTime) { this.endTime = endTime; } + public Integer getMaxPeople() { return maxPeople; } + public void setMaxPeople(Integer maxPeople) { this.maxPeople = maxPeople; } + public Long getStatus() { return status; } + public void setStatus(Long status) { this.status = status; } +} diff --git a/src/main/java/com/amms/mapper/AnnouncementMapper.java b/src/main/java/com/amms/mapper/AnnouncementMapper.java new file mode 100644 index 0000000..a5fa8fe --- /dev/null +++ b/src/main/java/com/amms/mapper/AnnouncementMapper.java @@ -0,0 +1,62 @@ +package com.amms.mapper; + +import java.util.List; +import org.apache.ibatis.annotations.Mapper; +import com.amms.domain.Announcement; + +/** + * 公告Mapper接口 + */ +@Mapper +public interface AnnouncementMapper { + /** + * 查询公告 + * + * @param id 公告主键 + * @return 公告 + */ + public Announcement selectAnnouncementById(Long id); + + /** + * 查询公告列表 + * + * @param announcement 公告 + * @return 公告集合 + */ + public List selectAnnouncementList(Announcement announcement); + + /** + * 新增公告 + * + * @param announcement 公告 + * @return 结果 + */ + public int insertAnnouncement(Announcement announcement); + + /** + * 修改公告 + * + * @param announcement 公告 + * @return 结果 + */ + public int updateAnnouncement(Announcement announcement); + + /** + * 删除公告 + * + * @param id 公告主键 + * @return 结果 + */ + public int deleteAnnouncementById(Long id); + + /** + * 查询置顶公告(显示状态) + */ + public List selectTopAnnouncements(); + + /** + * 查询普通公告(非置顶且显示) + */ + public List selectNormalAnnouncements(Announcement filter); + +} diff --git a/src/main/java/com/amms/service/impl/AnnouncementServiceImpl.java b/src/main/java/com/amms/service/impl/AnnouncementServiceImpl.java new file mode 100644 index 0000000..1adbe9e --- /dev/null +++ b/src/main/java/com/amms/service/impl/AnnouncementServiceImpl.java @@ -0,0 +1,116 @@ +package com.amms.service.impl; + +import java.util.List; +import java.util.Date; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import com.amms.mapper.AnnouncementMapper; +import com.amms.domain.Announcement; +import com.amms.service.IAnnouncementService; +import com.amms.security.SecurityUtils; + +/** + * 公告Service业务层处理 + */ +@Service +public class AnnouncementServiceImpl implements IAnnouncementService { + + @Autowired + private AnnouncementMapper announcementMapper; + + /** + * 查询公告列表 + * + * @param announcement 公告 + * @return 公告 + */ + @Override + public List selectAnnouncementList(Announcement announcement) { + return announcementMapper.selectAnnouncementList(announcement); + } + + /** + * 查询公告 + * + * @param id 公告主键 + * @return 公告 + */ + @Override + public Announcement selectAnnouncementById(Long id) { + return announcementMapper.selectAnnouncementById(id); + } + + /** + * 新增公告 + * + * @param announcement 公告 + * @return 结果 + */ + @Override + public int insertAnnouncement(Announcement announcement) { + announcement.setCreateTime(new Date()); + announcement.setCreator(SecurityUtils.getCurrentUser().getId()); + return announcementMapper.insertAnnouncement(announcement); + } + + /** + * 修改公告 + * + * @param announcement 公告 + * @return 结果 + */ + @Override + public int updateAnnouncement(Announcement announcement) { + announcement.setUpdateTime(new Date()); + announcement.setUpdater(SecurityUtils.getCurrentUser().getId()); + return announcementMapper.updateAnnouncement(announcement); + } + + /** + * 删除公告信息 + * + * @param id 公告主键 + * @return 结果 + */ + @Override + public int deleteAnnouncementById(Long id) { + return announcementMapper.deleteAnnouncementById(id); + } + + @Override + public int setTop(Long id) { + Announcement query = new Announcement(); + query.setIsTop(1L); + List tops = announcementMapper.selectAnnouncementList(query); + Long currentTopId = (tops != null && !tops.isEmpty()) ? tops.get(0).getId() : null; + + Long userId = SecurityUtils.getCurrentUser().getId(); + Date now = new Date(); + + if (currentTopId != null && !currentTopId.equals(id)) { + Announcement clearTop = new Announcement(); + clearTop.setId(currentTopId); + clearTop.setIsTop(0L); + clearTop.setUpdater(userId); + clearTop.setUpdateTime(now); + announcementMapper.updateAnnouncement(clearTop); + } + + Announcement setTop = new Announcement(); + setTop.setId(id); + setTop.setIsTop(1L); + setTop.setUpdater(userId); + setTop.setUpdateTime(now); + return announcementMapper.updateAnnouncement(setTop); + } + + @Override + public List selectTopAnnouncements() { + return announcementMapper.selectTopAnnouncements(); + } + + @Override + public List selectNormalAnnouncements(Announcement filter) { + return announcementMapper.selectNormalAnnouncements(filter); + } +} diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml new file mode 100644 index 0000000..b54de7f --- /dev/null +++ b/src/main/resources/application.yml @@ -0,0 +1,70 @@ +spring: + application: + name: amms + jackson: + time-zone: GMT+8 + datasource: + driver-class-name: com.mysql.cj.jdbc.Driver + url: jdbc:mysql://localhost:3306/mms?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&serverTimezone=GMT%2B8&allowPublicKeyRetrieval=true + username: root + password: Ding1998@ + hikari: + max-lifetime: 3600000 # 最大生命周期 + maximum-pool-size: 30 # 最大连接数 + minimum-idle: 10 # 最小连接数 + idle-timeout: 60000 # 空闲连接超时时间 + connection-timeout: 60000 # 连接超时时间 + # 文件上传 + servlet: + multipart: + # 单个文件大小 + max-file-size: 500MB + # 设置总上传的文件大小 + max-request-size: 1000MB + data: + redis: + host: 127.0.0.1 + port: 6379 + password: + database: 1 + lettuce: + pool: + # 连接池最大连接数(使用负值表示没有限制): 8 + max-active: 8 + # 连接池最大阻塞等待时间(使用负值表示没有限制)默认为-1 + max-wait: -1 + # 连接池中的最大空闲连接 默认为8 + max-idle: 8 + # 连接池中的最小空闲连接 默认为0 + min-idle: 0 +server: + port: 8848 + servlet: + context-path: /amms +logging: + level: + root: info + sql: debug + web: info + file: + name: /Users/dmz/Work/Project/BS/2026/LogFiles/amms/log/app.log +# token配置 +token: + # 令牌自定义标识 + header: Authorization + # 令牌密钥 + secret: abcdefghijklmnopqrstuvwxyz + # 令牌有效期(默认30分钟) + expireTime: 120 +# MyBatis配置 +mybatis: + # 搜索指定包别名 + typeAliasesPackage: com.amms.domain + # 配置mapper的扫描,找到所有的mapper.xml映射文件 + mapperLocations: classpath*:mapper/**/*Mapper.xml + # 加载全局的配置文件 + configLocation: classpath:mybatis/mybatis-config.xml + +# 文件上传参数 +files: + path: /Users/dmz/Work/Project/BS/2026/Files/ammsFiles diff --git a/src/main/resources/mapper/AnnouncementMapper.xml b/src/main/resources/mapper/AnnouncementMapper.xml new file mode 100644 index 0000000..67038b8 --- /dev/null +++ b/src/main/resources/mapper/AnnouncementMapper.xml @@ -0,0 +1,101 @@ + + + + + + + + + + + + + + + + + + select id, title, content, is_top, status, create_time, creator, update_time, updater from announcement + + + + + + + + + + + + + + insert into announcement + + title, + content, + is_top, + status, + create_time, + creator, + update_time, + updater, + + + #{title}, + #{content}, + #{isTop}, + #{status}, + #{createTime}, + #{creator}, + #{updateTime}, + #{updater}, + + + + + update announcement + + title = #{title}, + content = #{content}, + is_top = #{isTop}, + status = #{status}, + create_time = #{createTime}, + creator = #{creator}, + update_time = #{updateTime}, + updater = #{updater}, + + where id = #{id} + + + + delete from announcement where id = #{id} + + diff --git a/src/test/java/org/example/bsbase2025/ApplicationTests.java b/src/test/java/org/example/bsbase2025/ApplicationTests.java new file mode 100644 index 0000000..695a304 --- /dev/null +++ b/src/test/java/org/example/bsbase2025/ApplicationTests.java @@ -0,0 +1,14 @@ +package org.example.bsbase2025; + +import com.fasterxml.jackson.core.JsonProcessingException; +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; + +@SpringBootTest +class ApplicationTests { + + @Test + void contextLoads() throws JsonProcessingException { + } + +} diff --git a/文件/22150110017陈素霞任务书.docx b/文件/22150110017陈素霞任务书.docx new file mode 100644 index 0000000000000000000000000000000000000000..b638c42f54431d48358ad52b0c36e754011a3add GIT binary patch literal 18398 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ⅇ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 literal 0 HcmV?d00001