yangyi 4 сар өмнө
parent
commit
f7fcecf480
95 өөрчлөгдсөн 3707 нэмэгдсэн , 2 устгасан
  1. 193 0
      sql/init.sql
  2. 17 0
      src/main/java/space/anyi/petServiceManagementSystem/server/common/BusinessException.java
  3. 11 0
      src/main/java/space/anyi/petServiceManagementSystem/server/common/ForbiddenException.java
  4. 19 0
      src/main/java/space/anyi/petServiceManagementSystem/server/common/PageVo.java
  5. 38 0
      src/main/java/space/anyi/petServiceManagementSystem/server/common/R.java
  6. 11 0
      src/main/java/space/anyi/petServiceManagementSystem/server/common/ResourceNotFoundException.java
  7. 11 0
      src/main/java/space/anyi/petServiceManagementSystem/server/common/ResultCode.java
  8. 11 0
      src/main/java/space/anyi/petServiceManagementSystem/server/common/UnauthorizedException.java
  9. 17 0
      src/main/java/space/anyi/petServiceManagementSystem/server/config/CorsConfig.java
  10. 78 0
      src/main/java/space/anyi/petServiceManagementSystem/server/config/GlobalExceptionHandler.java
  11. 31 0
      src/main/java/space/anyi/petServiceManagementSystem/server/config/PasswordConfig.java
  12. 20 0
      src/main/java/space/anyi/petServiceManagementSystem/server/config/WebMvcConfig.java
  13. 58 0
      src/main/java/space/anyi/petServiceManagementSystem/server/controller/AppointmentController.java
  14. 39 0
      src/main/java/space/anyi/petServiceManagementSystem/server/controller/AuthController.java
  15. 31 0
      src/main/java/space/anyi/petServiceManagementSystem/server/controller/BoardingRoomController.java
  16. 30 0
      src/main/java/space/anyi/petServiceManagementSystem/server/controller/PackageController.java
  17. 91 0
      src/main/java/space/anyi/petServiceManagementSystem/server/controller/PetController.java
  18. 24 0
      src/main/java/space/anyi/petServiceManagementSystem/server/controller/ScheduleController.java
  19. 80 0
      src/main/java/space/anyi/petServiceManagementSystem/server/controller/UserController.java
  20. 39 0
      src/main/java/space/anyi/petServiceManagementSystem/server/controller/admin/AdminAppointmentController.java
  21. 23 0
      src/main/java/space/anyi/petServiceManagementSystem/server/controller/admin/AdminBoardingRoomController.java
  22. 36 0
      src/main/java/space/anyi/petServiceManagementSystem/server/controller/admin/AdminPackageController.java
  23. 38 0
      src/main/java/space/anyi/petServiceManagementSystem/server/controller/admin/AdminScheduleController.java
  24. 32 0
      src/main/java/space/anyi/petServiceManagementSystem/server/controller/admin/AdminUserController.java
  25. 31 0
      src/main/java/space/anyi/petServiceManagementSystem/server/dto/appointment/AppointmentDto.java
  26. 11 0
      src/main/java/space/anyi/petServiceManagementSystem/server/dto/appointment/HandleCancelDto.java
  27. 15 0
      src/main/java/space/anyi/petServiceManagementSystem/server/dto/appointment/ReviewDto.java
  28. 16 0
      src/main/java/space/anyi/petServiceManagementSystem/server/dto/auth/LoginDto.java
  29. 25 0
      src/main/java/space/anyi/petServiceManagementSystem/server/dto/auth/RegisterDto.java
  30. 29 0
      src/main/java/space/anyi/petServiceManagementSystem/server/dto/pet/DewormingDto.java
  31. 38 0
      src/main/java/space/anyi/petServiceManagementSystem/server/dto/pet/PetDto.java
  32. 29 0
      src/main/java/space/anyi/petServiceManagementSystem/server/dto/pet/VaccinationDto.java
  33. 35 0
      src/main/java/space/anyi/petServiceManagementSystem/server/dto/room/QueryByPageParams.java
  34. 19 0
      src/main/java/space/anyi/petServiceManagementSystem/server/dto/room/RoomStatusDto.java
  35. 28 0
      src/main/java/space/anyi/petServiceManagementSystem/server/dto/schedule/ScheduleDto.java
  36. 34 0
      src/main/java/space/anyi/petServiceManagementSystem/server/dto/servicePackage/PackageDto.java
  37. 15 0
      src/main/java/space/anyi/petServiceManagementSystem/server/dto/user/UpdateUserAvatarDto.java
  38. 20 0
      src/main/java/space/anyi/petServiceManagementSystem/server/dto/user/UpdateUserPasswordDto.java
  39. 15 0
      src/main/java/space/anyi/petServiceManagementSystem/server/dto/user/UpdateUserStatusDto.java
  40. 32 0
      src/main/java/space/anyi/petServiceManagementSystem/server/dto/user/UserDto.java
  41. 60 0
      src/main/java/space/anyi/petServiceManagementSystem/server/entity/Appointment.java
  42. 35 0
      src/main/java/space/anyi/petServiceManagementSystem/server/entity/BoardingRoom.java
  43. 26 0
      src/main/java/space/anyi/petServiceManagementSystem/server/entity/BoardingRoomStatus.java
  44. 32 0
      src/main/java/space/anyi/petServiceManagementSystem/server/entity/DewormingRecord.java
  45. 44 0
      src/main/java/space/anyi/petServiceManagementSystem/server/entity/Pet.java
  46. 26 0
      src/main/java/space/anyi/petServiceManagementSystem/server/entity/Schedule.java
  47. 35 0
      src/main/java/space/anyi/petServiceManagementSystem/server/entity/ServicePackage.java
  48. 40 0
      src/main/java/space/anyi/petServiceManagementSystem/server/entity/User.java
  49. 32 0
      src/main/java/space/anyi/petServiceManagementSystem/server/entity/VaccinationRecord.java
  50. 17 0
      src/main/java/space/anyi/petServiceManagementSystem/server/interceptor/AdminInterceptor.java
  51. 25 0
      src/main/java/space/anyi/petServiceManagementSystem/server/interceptor/AuthInterceptor.java
  52. 21 0
      src/main/java/space/anyi/petServiceManagementSystem/server/mapper/AppointmentMapper.java
  53. 14 0
      src/main/java/space/anyi/petServiceManagementSystem/server/mapper/BoardingRoomMapper.java
  54. 18 0
      src/main/java/space/anyi/petServiceManagementSystem/server/mapper/BoardingRoomStatusMapper.java
  55. 14 0
      src/main/java/space/anyi/petServiceManagementSystem/server/mapper/DewormingRecordMapper.java
  56. 16 0
      src/main/java/space/anyi/petServiceManagementSystem/server/mapper/PackageMapper.java
  57. 16 0
      src/main/java/space/anyi/petServiceManagementSystem/server/mapper/PetMapper.java
  58. 19 0
      src/main/java/space/anyi/petServiceManagementSystem/server/mapper/ScheduleMapper.java
  59. 24 0
      src/main/java/space/anyi/petServiceManagementSystem/server/mapper/UserMapper.java
  60. 15 0
      src/main/java/space/anyi/petServiceManagementSystem/server/mapper/VaccinationRecordMapper.java
  61. 21 0
      src/main/java/space/anyi/petServiceManagementSystem/server/service/AppointmentService.java
  62. 11 0
      src/main/java/space/anyi/petServiceManagementSystem/server/service/AuthService.java
  63. 13 0
      src/main/java/space/anyi/petServiceManagementSystem/server/service/BoardingRoomService.java
  64. 14 0
      src/main/java/space/anyi/petServiceManagementSystem/server/service/PackageService.java
  65. 22 0
      src/main/java/space/anyi/petServiceManagementSystem/server/service/PetService.java
  66. 12 0
      src/main/java/space/anyi/petServiceManagementSystem/server/service/ScheduleService.java
  67. 24 0
      src/main/java/space/anyi/petServiceManagementSystem/server/service/UserService.java
  68. 242 0
      src/main/java/space/anyi/petServiceManagementSystem/server/service/impl/AppointmentServiceImpl.java
  69. 76 0
      src/main/java/space/anyi/petServiceManagementSystem/server/service/impl/AuthServiceImpl.java
  70. 87 0
      src/main/java/space/anyi/petServiceManagementSystem/server/service/impl/BoardingRoomServiceImpl.java
  71. 84 0
      src/main/java/space/anyi/petServiceManagementSystem/server/service/impl/PackageServiceImpl.java
  72. 190 0
      src/main/java/space/anyi/petServiceManagementSystem/server/service/impl/PetServiceImpl.java
  73. 69 0
      src/main/java/space/anyi/petServiceManagementSystem/server/service/impl/ScheduleServiceImpl.java
  74. 212 0
      src/main/java/space/anyi/petServiceManagementSystem/server/service/impl/UserServiceImpl.java
  75. 24 0
      src/main/java/space/anyi/petServiceManagementSystem/server/util/PageUtil.java
  76. 66 0
      src/main/java/space/anyi/petServiceManagementSystem/server/vo/appointment/AppointmentVo.java
  77. 32 0
      src/main/java/space/anyi/petServiceManagementSystem/server/vo/pet/DewormingVo.java
  78. 48 0
      src/main/java/space/anyi/petServiceManagementSystem/server/vo/pet/PetVo.java
  79. 32 0
      src/main/java/space/anyi/petServiceManagementSystem/server/vo/pet/VaccinationVo.java
  80. 25 0
      src/main/java/space/anyi/petServiceManagementSystem/server/vo/room/BoardingRoomVo.java
  81. 22 0
      src/main/java/space/anyi/petServiceManagementSystem/server/vo/room/RoomStatusVo.java
  82. 26 0
      src/main/java/space/anyi/petServiceManagementSystem/server/vo/schedule/ScheduleVo.java
  83. 29 0
      src/main/java/space/anyi/petServiceManagementSystem/server/vo/servicePackage/PackageVo.java
  84. 15 0
      src/main/java/space/anyi/petServiceManagementSystem/server/vo/user/AdminUserDetailVo.java
  85. 29 0
      src/main/java/space/anyi/petServiceManagementSystem/server/vo/user/UserVo.java
  86. 7 2
      src/main/resources/application.yaml
  87. 72 0
      src/main/resources/mapper/AppointmentMapper.xml
  88. 32 0
      src/main/resources/mapper/BoardingRoomMapper.xml
  89. 30 0
      src/main/resources/mapper/BoardingRoomStatusMapper.xml
  90. 22 0
      src/main/resources/mapper/DewormingRecordMapper.xml
  91. 43 0
      src/main/resources/mapper/PackageMapper.xml
  92. 38 0
      src/main/resources/mapper/PetMapper.xml
  93. 27 0
      src/main/resources/mapper/ScheduleMapper.xml
  94. 90 0
      src/main/resources/mapper/UserMapper.xml
  95. 22 0
      src/main/resources/mapper/VaccinationRecordMapper.xml

+ 193 - 0
sql/init.sql

@@ -0,0 +1,193 @@
+-- ============================================================
+-- 宠物服务管理系统 — 数据库初始化脚本
+-- 数据库版本: MySQL 8.0
+-- 数据库名: pet_service_managemen_system
+-- 字符集: utf8mb4
+-- ============================================================
+
+CREATE DATABASE IF NOT EXISTS pet_service_managemen_system
+    DEFAULT CHARACTER SET utf8mb4
+    DEFAULT COLLATE utf8mb4_unicode_ci;
+
+USE pet_service_managemen_system;
+
+-- ============================================================
+-- 1. 用户表
+-- ============================================================
+CREATE TABLE IF NOT EXISTS user (
+    id          BIGINT       NOT NULL AUTO_INCREMENT COMMENT '主键',
+    account     VARCHAR(100) NOT NULL COMMENT '登录账号',
+    username    VARCHAR(100) NOT NULL COMMENT '用户名',
+    password    VARCHAR(255) NOT NULL COMMENT 'MD5 加密密码',
+    email       VARCHAR(255) DEFAULT NULL COMMENT '邮箱',
+    phone       VARCHAR(20)  DEFAULT NULL COMMENT '联系方式',
+    avatar      VARCHAR(500) DEFAULT NULL COMMENT '头像 URL',
+    role        VARCHAR(20)  DEFAULT 'user' NOT NULL COMMENT '角色: user/admin',
+    enable      TINYINT(1)   DEFAULT 1 NOT NULL COMMENT '状态: 0禁用 1启用',
+    created_at  DATETIME     DEFAULT CURRENT_TIMESTAMP NOT NULL COMMENT '创建时间',
+    updated_at  DATETIME     DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP NOT NULL COMMENT '更新时间',
+    PRIMARY KEY (id),
+    UNIQUE KEY uk_account (account)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户表';
+
+-- ============================================================
+-- 2. 宠物表
+-- ============================================================
+CREATE TABLE IF NOT EXISTS pet (
+    id          BIGINT       NOT NULL AUTO_INCREMENT COMMENT '主键',
+    user_id     BIGINT       NOT NULL COMMENT '所属用户 ID',
+    name        VARCHAR(100) NOT NULL COMMENT '宠物名称',
+    species     VARCHAR(50)  NOT NULL COMMENT '物种: DOG/CAT/OTHER',
+    breed       VARCHAR(100) DEFAULT NULL COMMENT '品种',
+    gender      VARCHAR(10)  DEFAULT NULL COMMENT '性别: MALE/FEMALE',
+    birth_date  DATE         DEFAULT NULL COMMENT '出生日期',
+    weight      DECIMAL(5,2) DEFAULT NULL COMMENT '体重(kg)',
+    avatar      VARCHAR(500) DEFAULT NULL COMMENT '头像 URL',
+    remark      TEXT         DEFAULT NULL COMMENT '备注',
+    created_at  DATETIME     DEFAULT CURRENT_TIMESTAMP NOT NULL COMMENT '创建时间',
+    updated_at  DATETIME     DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP NOT NULL COMMENT '更新时间',
+    PRIMARY KEY (id),
+    KEY idx_user_id (user_id)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='宠物表';
+
+-- ============================================================
+-- 3. 疫苗记录表
+-- ============================================================
+CREATE TABLE IF NOT EXISTS vaccination_record (
+    id           BIGINT       NOT NULL AUTO_INCREMENT COMMENT '主键',
+    pet_id       BIGINT       NOT NULL COMMENT '关联宠物 ID',
+    vaccine_name VARCHAR(255) NOT NULL COMMENT '疫苗名称',
+    vaccine_date DATE         NOT NULL COMMENT '接种日期',
+    hospital     VARCHAR(255) DEFAULT NULL COMMENT '接种机构',
+    doctor       VARCHAR(100) DEFAULT NULL COMMENT '接种兽医',
+    remark       TEXT         DEFAULT NULL COMMENT '备注',
+    created_at   DATETIME     DEFAULT CURRENT_TIMESTAMP NOT NULL COMMENT '创建时间',
+    PRIMARY KEY (id),
+    KEY idx_pet_id (pet_id)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='疫苗记录表';
+
+-- ============================================================
+-- 4. 驱虫记录表
+-- ============================================================
+CREATE TABLE IF NOT EXISTS deworming_record (
+    id             BIGINT       NOT NULL AUTO_INCREMENT COMMENT '主键',
+    pet_id         BIGINT       NOT NULL COMMENT '关联宠物 ID',
+    deworming_name VARCHAR(255) NOT NULL COMMENT '驱虫名称',
+    deworming_date DATE         NOT NULL COMMENT '驱虫日期',
+    hospital       VARCHAR(255) DEFAULT NULL COMMENT '驱虫机构',
+    doctor         VARCHAR(100) DEFAULT NULL COMMENT '驱虫兽医',
+    remark         TEXT         DEFAULT NULL COMMENT '备注',
+    created_at     DATETIME     DEFAULT CURRENT_TIMESTAMP NOT NULL COMMENT '创建时间',
+    PRIMARY KEY (id),
+    KEY idx_pet_id (pet_id)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='驱虫记录表';
+
+-- ============================================================
+-- 5. 套餐表
+-- ============================================================
+CREATE TABLE IF NOT EXISTS package (
+    id          BIGINT        NOT NULL AUTO_INCREMENT COMMENT '主键',
+    name        VARCHAR(255)  NOT NULL COMMENT '套餐名称',
+    type        VARCHAR(20)   NOT NULL COMMENT '类型: BOARDING/GROOMING',
+    price       DECIMAL(10,2) NOT NULL COMMENT '价格',
+    duration    INT           DEFAULT NULL COMMENT '寄养天数(仅寄养套餐)',
+    description TEXT          DEFAULT NULL COMMENT '项目内容描述',
+    is_active   TINYINT(1)    DEFAULT 1 NOT NULL COMMENT '是否启用: 0禁用 1启用',
+    created_at  DATETIME      DEFAULT CURRENT_TIMESTAMP NOT NULL COMMENT '创建时间',
+    updated_at  DATETIME      DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP NOT NULL COMMENT '更新时间',
+    PRIMARY KEY (id),
+    KEY idx_type (type)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='套餐表';
+
+-- ============================================================
+-- 6. 寄养房型表
+-- ============================================================
+CREATE TABLE IF NOT EXISTS boarding_room (
+    id            BIGINT        NOT NULL AUTO_INCREMENT COMMENT '主键',
+    room_name     VARCHAR(100)  NOT NULL COMMENT '房型名称',
+    room_type     VARCHAR(50)   DEFAULT NULL COMMENT '房型分类: STANDARD/VIP/DELUXE',
+    capacity      INT           DEFAULT 1 NOT NULL COMMENT '可容纳宠物数',
+    price_per_day DECIMAL(10,2) NOT NULL COMMENT '每晚价格',
+    is_active     TINYINT(1)    DEFAULT 1 NOT NULL COMMENT '是否启用: 0禁用 1启用',
+    description   TEXT          DEFAULT NULL COMMENT '房型描述',
+    created_at    DATETIME      DEFAULT CURRENT_TIMESTAMP NOT NULL COMMENT '创建时间',
+    updated_at    DATETIME      DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP NOT NULL COMMENT '更新时间',
+    PRIMARY KEY (id)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='寄养房型表';
+
+-- ============================================================
+-- 7. 房态表
+-- ============================================================
+CREATE TABLE IF NOT EXISTS boarding_room_status (
+    id             BIGINT       NOT NULL AUTO_INCREMENT COMMENT '主键',
+    room_id        BIGINT       NOT NULL COMMENT '关联房型 ID',
+    date           DATE         NOT NULL COMMENT '日期',
+    status         VARCHAR(20)  DEFAULT 'AVAILABLE' NOT NULL COMMENT '房态: AVAILABLE/OCCUPIED/MAINTENANCE',
+    appointment_id BIGINT       DEFAULT NULL COMMENT '关联预约订单 ID',
+    created_at     DATETIME     DEFAULT CURRENT_TIMESTAMP NOT NULL COMMENT '创建时间',
+    PRIMARY KEY (id),
+    UNIQUE KEY uk_room_date (room_id, date),
+    KEY idx_appointment_id (appointment_id)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='房态表';
+
+-- ============================================================
+-- 8. 预约订单表
+-- ============================================================
+CREATE TABLE IF NOT EXISTS appointment (
+    id             BIGINT        NOT NULL AUTO_INCREMENT COMMENT '主键',
+    user_id        BIGINT        NOT NULL COMMENT '用户 ID',
+    pet_id         BIGINT        NOT NULL COMMENT '宠物 ID',
+    package_id     BIGINT        NOT NULL COMMENT '套餐 ID',
+    room_id        BIGINT        DEFAULT NULL COMMENT '寄养房型 ID',
+    type           VARCHAR(20)   NOT NULL COMMENT '类型: BOARDING/GROOMING',
+    status         VARCHAR(30)   DEFAULT 'PENDING' NOT NULL COMMENT '状态: PENDING/CONFIRMED/CANCELLED/CANCEL_REQUESTED',
+    start_date     DATE          DEFAULT NULL COMMENT '开始日期(寄养)',
+    end_date       DATE          DEFAULT NULL COMMENT '结束日期(寄养)',
+    total_price    DECIMAL(10,2) NOT NULL COMMENT '总价',
+    deposit        DECIMAL(10,2) DEFAULT 0.00 NOT NULL COMMENT '定金金额',
+    deposit_paid   TINYINT(1)    DEFAULT 0 NOT NULL COMMENT '是否支付定金: 0未支付 1已支付',
+    cancel_reason  TEXT          DEFAULT NULL COMMENT '取消原因',
+    reject_reason  VARCHAR(500)  DEFAULT NULL COMMENT '拒绝原因',
+    remark         TEXT          DEFAULT NULL COMMENT '备注',
+    created_at     DATETIME      DEFAULT CURRENT_TIMESTAMP NOT NULL COMMENT '创建时间',
+    updated_at     DATETIME      DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP NOT NULL COMMENT '更新时间',
+    PRIMARY KEY (id),
+    KEY idx_user_id (user_id),
+    KEY idx_status (status),
+    KEY idx_package_id (package_id)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='预约订单表';
+
+-- ============================================================
+-- 9. 排班表
+-- ============================================================
+CREATE TABLE IF NOT EXISTS schedule (
+    id            BIGINT       NOT NULL AUTO_INCREMENT COMMENT '主键',
+    employee_name VARCHAR(100) NOT NULL COMMENT '员工姓名',
+    work_date     DATE         NOT NULL COMMENT '排班日期',
+    time_slot     VARCHAR(20)  NOT NULL COMMENT '时段: MORNING/AFTERNOON/FULL_DAY',
+    remark        TEXT         DEFAULT NULL COMMENT '备注',
+    created_at    DATETIME     DEFAULT CURRENT_TIMESTAMP NOT NULL COMMENT '创建时间',
+    PRIMARY KEY (id),
+    KEY idx_work_date (work_date)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='排班表';
+
+-- ============================================================
+-- 初始数据
+-- ============================================================
+
+-- 默认管理员账号: admin / admin123
+INSERT INTO user (account, username, password, role, enable) VALUES
+    ('admin', '系统管理员', 'f0a2f3c9e1b8d4e7a6c5b0d9f8e7a6c5', 'admin', 1);
+
+-- 测试套餐数据
+INSERT INTO package (name, type, price, duration, description, is_active) VALUES
+    ('标准寄养', 'BOARDING', 100.00, 1, '基础寄养服务,含每日喂食、遛宠、基础清洁', 1),
+    ('豪华寄养', 'BOARDING', 200.00, 1, '豪华寄养服务,含独立空调房、每日零食、视频监控', 1),
+    ('基础洗护', 'GROOMING', 80.00, NULL, '基础洗护服务,含洗澡、吹干、剪指甲', 1),
+    ('全套洗护', 'GROOMING', 150.00, NULL, '全套洗护服务,含洗澡、美容修剪、SPA护理', 1);
+
+-- 测试房型数据
+INSERT INTO boarding_room (room_name, room_type, capacity, price_per_day, description, is_active) VALUES
+    ('标准单间', 'STANDARD', 1, 100.00, '标准独立单间,适合中小型犬猫', 1),
+    ('豪华套房', 'VIP', 1, 200.00, '豪华独立套房,含空调、摄像头、软垫', 1),
+    ('标准双间', 'STANDARD', 2, 150.00, '标准双宠间,适合同一家庭的宠物', 1);

+ 17 - 0
src/main/java/space/anyi/petServiceManagementSystem/server/common/BusinessException.java

@@ -0,0 +1,17 @@
+package space.anyi.petServiceManagementSystem.server.common;
+
+public class BusinessException extends RuntimeException {
+    private int code;
+
+    public BusinessException(int code, String message) {
+        super(message);
+        this.code = code;
+    }
+
+    public BusinessException(String message) {
+        super(message);
+        this.code = 400;
+    }
+
+    public int getCode() { return code; }
+}

+ 11 - 0
src/main/java/space/anyi/petServiceManagementSystem/server/common/ForbiddenException.java

@@ -0,0 +1,11 @@
+package space.anyi.petServiceManagementSystem.server.common;
+
+public class ForbiddenException extends RuntimeException {
+    private int code = 403;
+
+    public ForbiddenException(String message) {
+        super(message);
+    }
+
+    public int getCode() { return code; }
+}

+ 19 - 0
src/main/java/space/anyi/petServiceManagementSystem/server/common/PageVo.java

@@ -0,0 +1,19 @@
+package space.anyi.petServiceManagementSystem.server.common;
+
+import java.util.List;
+
+public class PageVo<T> {
+    private List<T> data;
+    private long total;
+    private int pageNum;
+    private int pageSize;
+
+    public List<T> getData() { return data; }
+    public void setData(List<T> data) { this.data = data; }
+    public long getTotal() { return total; }
+    public void setTotal(long total) { this.total = total; }
+    public int getPageNum() { return pageNum; }
+    public void setPageNum(int pageNum) { this.pageNum = pageNum; }
+    public int getPageSize() { return pageSize; }
+    public void setPageSize(int pageSize) { this.pageSize = pageSize; }
+}

+ 38 - 0
src/main/java/space/anyi/petServiceManagementSystem/server/common/R.java

@@ -0,0 +1,38 @@
+package space.anyi.petServiceManagementSystem.server.common;
+
+public class R<T> {
+    private int code;
+    private String message;
+    private T data;
+
+    public R() {}
+
+    public R(int code, String message, T data) {
+        this.code = code;
+        this.message = message;
+        this.data = data;
+    }
+
+    public static <T> R<T> ok(T data) {
+        return new R<>(200, "success", data);
+    }
+
+    public static <T> R<T> ok() {
+        return new R<>(200, "success", null);
+    }
+
+    public static <T> R<T> error(int code, String message) {
+        return new R<>(code, message, null);
+    }
+
+    public static <T> R<T> error(String message) {
+        return new R<>(400, message, null);
+    }
+
+    public int getCode() { return code; }
+    public void setCode(int code) { this.code = code; }
+    public String getMessage() { return message; }
+    public void setMessage(String message) { this.message = message; }
+    public T getData() { return data; }
+    public void setData(T data) { this.data = data; }
+}

+ 11 - 0
src/main/java/space/anyi/petServiceManagementSystem/server/common/ResourceNotFoundException.java

@@ -0,0 +1,11 @@
+package space.anyi.petServiceManagementSystem.server.common;
+
+public class ResourceNotFoundException extends RuntimeException {
+    private int code = 404;
+
+    public ResourceNotFoundException(String message) {
+        super(message);
+    }
+
+    public int getCode() { return code; }
+}

+ 11 - 0
src/main/java/space/anyi/petServiceManagementSystem/server/common/ResultCode.java

@@ -0,0 +1,11 @@
+package space.anyi.petServiceManagementSystem.server.common;
+
+public class ResultCode {
+    public static final int SUCCESS = 200;
+    public static final int BAD_REQUEST = 400;
+    public static final int UNAUTHORIZED = 401;
+    public static final int FORBIDDEN = 403;
+    public static final int NOT_FOUND = 404;
+    public static final int CONFLICT = 409;
+    public static final int INTERNAL_ERROR = 500;
+}

+ 11 - 0
src/main/java/space/anyi/petServiceManagementSystem/server/common/UnauthorizedException.java

@@ -0,0 +1,11 @@
+package space.anyi.petServiceManagementSystem.server.common;
+
+public class UnauthorizedException extends RuntimeException {
+    private int code = 401;
+
+    public UnauthorizedException(String message) {
+        super(message);
+    }
+
+    public int getCode() { return code; }
+}

+ 17 - 0
src/main/java/space/anyi/petServiceManagementSystem/server/config/CorsConfig.java

@@ -0,0 +1,17 @@
+package space.anyi.petServiceManagementSystem.server.config;
+
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.servlet.config.annotation.CorsRegistry;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
+
+@Configuration
+public class CorsConfig implements WebMvcConfigurer {
+    @Override
+    public void addCorsMappings(CorsRegistry registry) {
+        registry.addMapping("/**")
+                .allowedOriginPatterns("*")
+                .allowCredentials(true)
+                .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
+                .allowedHeaders("*");
+    }
+}

+ 78 - 0
src/main/java/space/anyi/petServiceManagementSystem/server/config/GlobalExceptionHandler.java

@@ -0,0 +1,78 @@
+package space.anyi.petServiceManagementSystem.server.config;
+
+import org.springframework.dao.DuplicateKeyException;
+import org.springframework.http.HttpStatus;
+import org.springframework.validation.BindException;
+import org.springframework.web.bind.MethodArgumentNotValidException;
+import org.springframework.web.bind.MissingServletRequestParameterException;
+import org.springframework.web.bind.annotation.ExceptionHandler;
+import org.springframework.web.bind.annotation.ResponseStatus;
+import org.springframework.web.bind.annotation.RestControllerAdvice;
+import space.anyi.petServiceManagementSystem.server.common.BusinessException;
+import space.anyi.petServiceManagementSystem.server.common.ForbiddenException;
+import space.anyi.petServiceManagementSystem.server.common.R;
+import space.anyi.petServiceManagementSystem.server.common.ResourceNotFoundException;
+import space.anyi.petServiceManagementSystem.server.common.UnauthorizedException;
+
+import java.util.Objects;
+
+@RestControllerAdvice
+public class GlobalExceptionHandler {
+
+    @ExceptionHandler(BusinessException.class)
+    @ResponseStatus(HttpStatus.BAD_REQUEST)
+    public R<Void> handleBusinessException(BusinessException e) {
+        return R.error(e.getCode(), e.getMessage());
+    }
+
+    @ExceptionHandler(UnauthorizedException.class)
+    @ResponseStatus(HttpStatus.UNAUTHORIZED)
+    public R<Void> handleUnauthorized(UnauthorizedException e) {
+        return R.error(e.getCode(), e.getMessage());
+    }
+
+    @ExceptionHandler(ForbiddenException.class)
+    @ResponseStatus(HttpStatus.FORBIDDEN)
+    public R<Void> handleForbidden(ForbiddenException e) {
+        return R.error(e.getCode(), e.getMessage());
+    }
+
+    @ExceptionHandler(ResourceNotFoundException.class)
+    @ResponseStatus(HttpStatus.NOT_FOUND)
+    public R<Void> handleNotFound(ResourceNotFoundException e) {
+        return R.error(e.getCode(), e.getMessage());
+    }
+
+    @ExceptionHandler(MethodArgumentNotValidException.class)
+    @ResponseStatus(HttpStatus.BAD_REQUEST)
+    public R<Void> handleValidation(MethodArgumentNotValidException e) {
+        String msg = Objects.requireNonNull(e.getBindingResult().getFieldError()).getDefaultMessage();
+        return R.error(400, msg);
+    }
+
+    @ExceptionHandler(BindException.class)
+    @ResponseStatus(HttpStatus.BAD_REQUEST)
+    public R<Void> handleBindException(BindException e) {
+        String msg = Objects.requireNonNull(e.getBindingResult().getFieldError()).getDefaultMessage();
+        return R.error(400, msg);
+    }
+
+    @ExceptionHandler(MissingServletRequestParameterException.class)
+    @ResponseStatus(HttpStatus.BAD_REQUEST)
+    public R<Void> handleMissingParam(MissingServletRequestParameterException e) {
+        return R.error(400, "缺少必填参数: " + e.getParameterName());
+    }
+
+    @ExceptionHandler(DuplicateKeyException.class)
+    @ResponseStatus(HttpStatus.CONFLICT)
+    public R<Void> handleDuplicateKey(DuplicateKeyException e) {
+        return R.error(409, "数据重复,请检查唯一字段");
+    }
+
+    @ExceptionHandler(Exception.class)
+    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
+    public R<Void> handleException(Exception e) {
+        e.printStackTrace();
+        return R.error(500, "服务器内部错误");
+    }
+}

+ 31 - 0
src/main/java/space/anyi/petServiceManagementSystem/server/config/PasswordConfig.java

@@ -0,0 +1,31 @@
+package space.anyi.petServiceManagementSystem.server.config;
+
+import org.springframework.stereotype.Component;
+
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+
+@Component
+public class PasswordConfig {
+
+    private static final String SALT = "PetService2024!@#";
+
+    public String encrypt(String password) {
+        try {
+            String toEncrypt = password + SALT;
+            MessageDigest md = MessageDigest.getInstance("MD5");
+            byte[] digest = md.digest(toEncrypt.getBytes());
+            StringBuilder sb = new StringBuilder();
+            for (byte b : digest) {
+                sb.append(String.format("%02x", b & 0xff));
+            }
+            return sb.toString();
+        } catch (NoSuchAlgorithmException e) {
+            throw new RuntimeException("MD5 encryption error", e);
+        }
+    }
+
+    public boolean matches(String rawPassword, String encryptedPassword) {
+        return encrypt(rawPassword).equals(encryptedPassword);
+    }
+}

+ 20 - 0
src/main/java/space/anyi/petServiceManagementSystem/server/config/WebMvcConfig.java

@@ -0,0 +1,20 @@
+package space.anyi.petServiceManagementSystem.server.config;
+
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
+import space.anyi.petServiceManagementSystem.server.interceptor.AdminInterceptor;
+import space.anyi.petServiceManagementSystem.server.interceptor.AuthInterceptor;
+
+@Configuration
+public class WebMvcConfig implements WebMvcConfigurer {
+    @Override
+    public void addInterceptors(InterceptorRegistry registry) {
+        registry.addInterceptor(new AuthInterceptor())
+                .addPathPatterns("/api/**")
+                .excludePathPatterns("/api/auth/**", "/api/swagger-ui/**", "/api/v3/api-docs/**");
+
+        registry.addInterceptor(new AdminInterceptor())
+                .addPathPatterns("/api/admin/**");
+    }
+}

+ 58 - 0
src/main/java/space/anyi/petServiceManagementSystem/server/controller/AppointmentController.java

@@ -0,0 +1,58 @@
+package space.anyi.petServiceManagementSystem.server.controller;
+
+import io.swagger.v3.oas.annotations.Operation;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.validation.Valid;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+import space.anyi.petServiceManagementSystem.server.common.R;
+import space.anyi.petServiceManagementSystem.server.dto.appointment.AppointmentDto;
+import space.anyi.petServiceManagementSystem.server.service.AppointmentService;
+import space.anyi.petServiceManagementSystem.server.vo.appointment.AppointmentVo;
+
+import java.util.List;
+import java.util.Map;
+
+@RestController
+@RequestMapping("/appointments")
+public class AppointmentController {
+
+    @Autowired
+    private AppointmentService appointmentService;
+
+    @PostMapping
+    @Operation(summary = "提交预约")
+    public R<AppointmentVo> create(@Valid @RequestBody AppointmentDto dto, HttpServletRequest request) {
+        Long userId = (Long) request.getAttribute("userId");
+        return R.ok(appointmentService.create(dto, userId));
+    }
+
+    @GetMapping
+    @Operation(summary = "获取我的预约列表")
+    public R<List<AppointmentVo>> list(HttpServletRequest request) {
+        Long userId = (Long) request.getAttribute("userId");
+        return R.ok(appointmentService.list(userId));
+    }
+
+    @GetMapping("/{id}")
+    @Operation(summary = "获取预约详情")
+    public R<AppointmentVo> getById(@PathVariable Long id, HttpServletRequest request) {
+        Long userId = (Long) request.getAttribute("userId");
+        return R.ok(appointmentService.getById(id, userId));
+    }
+
+    @PostMapping("/{id}/cancel")
+    @Operation(summary = "申请取消")
+    public R<Boolean> cancel(@PathVariable Long id, @RequestBody Map<String, String> body,
+                             HttpServletRequest request) {
+        Long userId = (Long) request.getAttribute("userId");
+        return R.ok(appointmentService.cancel(id, body.get("reason"), userId));
+    }
+
+    @PostMapping("/{id}/pay-deposit")
+    @Operation(summary = "模拟支付定金")
+    public R<Boolean> payDeposit(@PathVariable Long id, HttpServletRequest request) {
+        Long userId = (Long) request.getAttribute("userId");
+        return R.ok(appointmentService.payDeposit(id, userId));
+    }
+}

+ 39 - 0
src/main/java/space/anyi/petServiceManagementSystem/server/controller/AuthController.java

@@ -0,0 +1,39 @@
+package space.anyi.petServiceManagementSystem.server.controller;
+
+import io.swagger.v3.oas.annotations.Operation;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.validation.Valid;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+import space.anyi.petServiceManagementSystem.server.common.R;
+import space.anyi.petServiceManagementSystem.server.dto.auth.LoginDto;
+import space.anyi.petServiceManagementSystem.server.dto.auth.RegisterDto;
+import space.anyi.petServiceManagementSystem.server.service.AuthService;
+import space.anyi.petServiceManagementSystem.server.vo.user.UserVo;
+
+@RestController
+@RequestMapping("/auth")
+public class AuthController {
+
+    @Autowired
+    private AuthService authService;
+
+    @PostMapping("/register")
+    @Operation(summary = "用户注册")
+    public R<UserVo> register(@Valid @RequestBody RegisterDto dto, HttpServletRequest request) {
+        return R.ok(authService.register(dto, request));
+    }
+
+    @PostMapping("/login")
+    @Operation(summary = "用户登录")
+    public R<UserVo> login(@Valid @RequestBody LoginDto dto, HttpServletRequest request) {
+        return R.ok(authService.login(dto, request));
+    }
+
+    @PostMapping("/logout")
+    @Operation(summary = "退出登录")
+    public R<Void> logout(HttpServletRequest request) {
+        request.getSession().invalidate();
+        return R.ok();
+    }
+}

+ 31 - 0
src/main/java/space/anyi/petServiceManagementSystem/server/controller/BoardingRoomController.java

@@ -0,0 +1,31 @@
+package space.anyi.petServiceManagementSystem.server.controller;
+
+import io.swagger.v3.oas.annotations.Operation;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+import space.anyi.petServiceManagementSystem.server.common.R;
+import space.anyi.petServiceManagementSystem.server.service.BoardingRoomService;
+import space.anyi.petServiceManagementSystem.server.vo.room.BoardingRoomVo;
+import space.anyi.petServiceManagementSystem.server.vo.room.RoomStatusVo;
+
+import java.util.List;
+
+@RestController
+@RequestMapping("/boarding-rooms")
+public class BoardingRoomController {
+
+    @Autowired
+    private BoardingRoomService boardingRoomService;
+
+    @GetMapping
+    @Operation(summary = "获取所有房型")
+    public R<List<BoardingRoomVo>> listRooms() {
+        return R.ok(boardingRoomService.listRooms());
+    }
+
+    @GetMapping("/{id}/status")
+    @Operation(summary = "获取指定房型某月房态")
+    public R<List<RoomStatusVo>> getStatus(@PathVariable Long id, @RequestParam String month) {
+        return R.ok(boardingRoomService.getStatus(id, month));
+    }
+}

+ 30 - 0
src/main/java/space/anyi/petServiceManagementSystem/server/controller/PackageController.java

@@ -0,0 +1,30 @@
+package space.anyi.petServiceManagementSystem.server.controller;
+
+import io.swagger.v3.oas.annotations.Operation;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+import space.anyi.petServiceManagementSystem.server.common.R;
+import space.anyi.petServiceManagementSystem.server.service.PackageService;
+import space.anyi.petServiceManagementSystem.server.vo.servicePackage.PackageVo;
+
+import java.util.List;
+
+@RestController
+@RequestMapping("/packages")
+public class PackageController {
+
+    @Autowired
+    private PackageService packageService;
+
+    @GetMapping
+    @Operation(summary = "获取套餐列表")
+    public R<List<PackageVo>> list(@RequestParam(required = false) String type) {
+        return R.ok(packageService.list(type));
+    }
+
+    @GetMapping("/{id}")
+    @Operation(summary = "获取套餐详情")
+    public R<PackageVo> getById(@PathVariable Long id) {
+        return R.ok(packageService.getById(id));
+    }
+}

+ 91 - 0
src/main/java/space/anyi/petServiceManagementSystem/server/controller/PetController.java

@@ -0,0 +1,91 @@
+package space.anyi.petServiceManagementSystem.server.controller;
+
+import io.swagger.v3.oas.annotations.Operation;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.validation.Valid;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+import space.anyi.petServiceManagementSystem.server.common.R;
+import space.anyi.petServiceManagementSystem.server.dto.pet.DewormingDto;
+import space.anyi.petServiceManagementSystem.server.dto.pet.PetDto;
+import space.anyi.petServiceManagementSystem.server.dto.pet.VaccinationDto;
+import space.anyi.petServiceManagementSystem.server.service.PetService;
+import space.anyi.petServiceManagementSystem.server.vo.pet.DewormingVo;
+import space.anyi.petServiceManagementSystem.server.vo.pet.PetVo;
+import space.anyi.petServiceManagementSystem.server.vo.pet.VaccinationVo;
+
+import java.util.List;
+
+@RestController
+@RequestMapping("/pets")
+public class PetController {
+
+    @Autowired
+    private PetService petService;
+
+    @GetMapping
+    @Operation(summary = "获取当前用户宠物列表")
+    public R<List<PetVo>> list(HttpServletRequest request) {
+        Long userId = (Long) request.getAttribute("userId");
+        return R.ok(petService.list(userId));
+    }
+
+    @PostMapping
+    @Operation(summary = "新增宠物")
+    public R<PetVo> add(@Valid @RequestBody PetDto dto, HttpServletRequest request) {
+        Long userId = (Long) request.getAttribute("userId");
+        return R.ok(petService.add(dto, userId));
+    }
+
+    @GetMapping("/{id}")
+    @Operation(summary = "获取宠物详情")
+    public R<PetVo> getById(@PathVariable Long id, HttpServletRequest request) {
+        Long userId = (Long) request.getAttribute("userId");
+        return R.ok(petService.getById(id, userId));
+    }
+
+    @PutMapping("/{id}")
+    @Operation(summary = "修改宠物信息")
+    public R<Boolean> update(@PathVariable Long id, @Valid @RequestBody PetDto dto,
+                             HttpServletRequest request) {
+        Long userId = (Long) request.getAttribute("userId");
+        return R.ok(petService.update(id, dto, userId));
+    }
+
+    @DeleteMapping("/{id}")
+    @Operation(summary = "删除宠物")
+    public R<Boolean> delete(@PathVariable Long id, HttpServletRequest request) {
+        Long userId = (Long) request.getAttribute("userId");
+        return R.ok(petService.delete(id, userId));
+    }
+
+    @GetMapping("/{id}/vaccinations")
+    @Operation(summary = "获取疫苗记录")
+    public R<List<VaccinationVo>> getVaccinations(@PathVariable Long id, HttpServletRequest request) {
+        Long userId = (Long) request.getAttribute("userId");
+        return R.ok(petService.getVaccinations(id, userId));
+    }
+
+    @PostMapping("/{id}/vaccinations")
+    @Operation(summary = "新增疫苗记录")
+    public R<VaccinationVo> addVaccination(@PathVariable Long id, @Valid @RequestBody VaccinationDto dto,
+                                           HttpServletRequest request) {
+        Long userId = (Long) request.getAttribute("userId");
+        return R.ok(petService.addVaccination(id, dto, userId));
+    }
+
+    @GetMapping("/{id}/deworming")
+    @Operation(summary = "获取驱虫记录")
+    public R<List<DewormingVo>> getDeworming(@PathVariable Long id, HttpServletRequest request) {
+        Long userId = (Long) request.getAttribute("userId");
+        return R.ok(petService.getDeworming(id, userId));
+    }
+
+    @PostMapping("/{id}/deworming")
+    @Operation(summary = "新增驱虫记录")
+    public R<DewormingVo> addDeworming(@PathVariable Long id, @Valid @RequestBody DewormingDto dto,
+                                       HttpServletRequest request) {
+        Long userId = (Long) request.getAttribute("userId");
+        return R.ok(petService.addDeworming(id, dto, userId));
+    }
+}

+ 24 - 0
src/main/java/space/anyi/petServiceManagementSystem/server/controller/ScheduleController.java

@@ -0,0 +1,24 @@
+package space.anyi.petServiceManagementSystem.server.controller;
+
+import io.swagger.v3.oas.annotations.Operation;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+import space.anyi.petServiceManagementSystem.server.common.R;
+import space.anyi.petServiceManagementSystem.server.service.ScheduleService;
+import space.anyi.petServiceManagementSystem.server.vo.schedule.ScheduleVo;
+
+import java.util.List;
+
+@RestController
+@RequestMapping("/schedules")
+public class ScheduleController {
+
+    @Autowired
+    private ScheduleService scheduleService;
+
+    @GetMapping
+    @Operation(summary = "获取排班列表")
+    public R<List<ScheduleVo>> list(@RequestParam(required = false) String month) {
+        return R.ok(scheduleService.list(month));
+    }
+}

+ 80 - 0
src/main/java/space/anyi/petServiceManagementSystem/server/controller/UserController.java

@@ -0,0 +1,80 @@
+package space.anyi.petServiceManagementSystem.server.controller;
+
+import io.swagger.v3.oas.annotations.Operation;
+import jakarta.validation.Valid;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+import space.anyi.petServiceManagementSystem.server.common.PageVo;
+import space.anyi.petServiceManagementSystem.server.common.R;
+import space.anyi.petServiceManagementSystem.server.dto.room.QueryByPageParams;
+import space.anyi.petServiceManagementSystem.server.dto.user.UpdateUserAvatarDto;
+import space.anyi.petServiceManagementSystem.server.dto.user.UpdateUserPasswordDto;
+import space.anyi.petServiceManagementSystem.server.dto.user.UpdateUserStatusDto;
+import space.anyi.petServiceManagementSystem.server.dto.user.UserDto;
+import space.anyi.petServiceManagementSystem.server.service.UserService;
+import space.anyi.petServiceManagementSystem.server.vo.user.UserVo;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.stream.Collectors;
+
+@RestController
+@RequestMapping("/user")
+public class UserController {
+
+    @Autowired
+    private UserService userService;
+
+    @GetMapping
+    @Operation(summary = "分页查询用户列表")
+    public R<PageVo<UserVo>> queryByPage(@Valid QueryByPageParams params) {
+        return R.ok(userService.queryByPage(params));
+    }
+
+    @GetMapping("/{id}")
+    @Operation(summary = "按ID查询用户")
+    public R<UserVo> queryById(@PathVariable Long id) {
+        return R.ok(userService.queryById(id));
+    }
+
+    @PostMapping
+    @Operation(summary = "新增用户")
+    public R<Boolean> add(@Valid @RequestBody UserDto dto) {
+        return R.ok(userService.add(dto));
+    }
+
+    @PutMapping
+    @Operation(summary = "修改用户")
+    public R<Boolean> edit(@Valid @RequestBody UserDto dto) {
+        return R.ok(userService.edit(dto));
+    }
+
+    @DeleteMapping
+    @Operation(summary = "删除用户")
+    public R<Boolean> deleteById(@RequestParam String ids) {
+        List<Long> idList = Arrays.stream(ids.split(","))
+                .map(Long::parseLong).collect(Collectors.toList());
+        return R.ok(userService.deleteByIds(idList));
+    }
+
+    @PutMapping("/updateStatus")
+    @Operation(summary = "启用/禁用用户")
+    public R<Void> updateStatus(@Valid @RequestBody UpdateUserStatusDto dto) {
+        userService.updateStatus(dto);
+        return R.ok();
+    }
+
+    @PutMapping("/updatePassword")
+    @Operation(summary = "修改密码")
+    public R<Void> updatePassword(@Valid @RequestBody UpdateUserPasswordDto dto) {
+        userService.updatePassword(dto);
+        return R.ok();
+    }
+
+    @PutMapping("/updateAvatar")
+    @Operation(summary = "修改头像")
+    public R<Void> updateAvatar(@Valid @RequestBody UpdateUserAvatarDto dto) {
+        userService.updateAvatar(dto);
+        return R.ok();
+    }
+}

+ 39 - 0
src/main/java/space/anyi/petServiceManagementSystem/server/controller/admin/AdminAppointmentController.java

@@ -0,0 +1,39 @@
+package space.anyi.petServiceManagementSystem.server.controller.admin;
+
+import io.swagger.v3.oas.annotations.Operation;
+import jakarta.validation.Valid;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+import space.anyi.petServiceManagementSystem.server.common.PageVo;
+import space.anyi.petServiceManagementSystem.server.common.R;
+import space.anyi.petServiceManagementSystem.server.dto.appointment.HandleCancelDto;
+import space.anyi.petServiceManagementSystem.server.dto.appointment.ReviewDto;
+import space.anyi.petServiceManagementSystem.server.dto.room.QueryByPageParams;
+import space.anyi.petServiceManagementSystem.server.service.AppointmentService;
+import space.anyi.petServiceManagementSystem.server.vo.appointment.AppointmentVo;
+
+@RestController
+@RequestMapping("/admin/appointments")
+public class AdminAppointmentController {
+
+    @Autowired
+    private AppointmentService appointmentService;
+
+    @GetMapping
+    @Operation(summary = "获取全部预约(管理员)")
+    public R<PageVo<AppointmentVo>> listAll(@Valid QueryByPageParams params) {
+        return R.ok(appointmentService.listAll(params));
+    }
+
+    @PutMapping("/{id}/review")
+    @Operation(summary = "审核预约")
+    public R<Boolean> review(@PathVariable Long id, @Valid @RequestBody ReviewDto dto) {
+        return R.ok(appointmentService.review(id, dto));
+    }
+
+    @PutMapping("/{id}/handle-cancel")
+    @Operation(summary = "处理取消申请")
+    public R<Boolean> handleCancel(@PathVariable Long id, @Valid @RequestBody HandleCancelDto dto) {
+        return R.ok(appointmentService.handleCancel(id, dto));
+    }
+}

+ 23 - 0
src/main/java/space/anyi/petServiceManagementSystem/server/controller/admin/AdminBoardingRoomController.java

@@ -0,0 +1,23 @@
+package space.anyi.petServiceManagementSystem.server.controller.admin;
+
+import io.swagger.v3.oas.annotations.Operation;
+import jakarta.validation.Valid;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+import space.anyi.petServiceManagementSystem.server.common.R;
+import space.anyi.petServiceManagementSystem.server.dto.room.RoomStatusDto;
+import space.anyi.petServiceManagementSystem.server.service.BoardingRoomService;
+
+@RestController
+@RequestMapping("/admin/boarding-rooms")
+public class AdminBoardingRoomController {
+
+    @Autowired
+    private BoardingRoomService boardingRoomService;
+
+    @PutMapping("/{roomId}/status")
+    @Operation(summary = "手动修改某日房态")
+    public R<Boolean> updateStatus(@PathVariable Long roomId, @Valid @RequestBody RoomStatusDto dto) {
+        return R.ok(boardingRoomService.updateStatus(roomId, dto));
+    }
+}

+ 36 - 0
src/main/java/space/anyi/petServiceManagementSystem/server/controller/admin/AdminPackageController.java

@@ -0,0 +1,36 @@
+package space.anyi.petServiceManagementSystem.server.controller.admin;
+
+import io.swagger.v3.oas.annotations.Operation;
+import jakarta.validation.Valid;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+import space.anyi.petServiceManagementSystem.server.common.R;
+import space.anyi.petServiceManagementSystem.server.dto.servicePackage.PackageDto;
+import space.anyi.petServiceManagementSystem.server.service.PackageService;
+import space.anyi.petServiceManagementSystem.server.vo.servicePackage.PackageVo;
+
+@RestController
+@RequestMapping("/admin/packages")
+public class AdminPackageController {
+
+    @Autowired
+    private PackageService packageService;
+
+    @PostMapping
+    @Operation(summary = "新增套餐")
+    public R<PackageVo> create(@Valid @RequestBody PackageDto dto) {
+        return R.ok(packageService.create(dto));
+    }
+
+    @PutMapping("/{id}")
+    @Operation(summary = "修改套餐")
+    public R<Boolean> update(@PathVariable Long id, @Valid @RequestBody PackageDto dto) {
+        return R.ok(packageService.update(id, dto));
+    }
+
+    @DeleteMapping("/{id}")
+    @Operation(summary = "删除套餐")
+    public R<Boolean> delete(@PathVariable Long id) {
+        return R.ok(packageService.delete(id));
+    }
+}

+ 38 - 0
src/main/java/space/anyi/petServiceManagementSystem/server/controller/admin/AdminScheduleController.java

@@ -0,0 +1,38 @@
+package space.anyi.petServiceManagementSystem.server.controller.admin;
+
+import io.swagger.v3.oas.annotations.Operation;
+import jakarta.validation.Valid;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+import space.anyi.petServiceManagementSystem.server.common.R;
+import space.anyi.petServiceManagementSystem.server.dto.schedule.ScheduleDto;
+import space.anyi.petServiceManagementSystem.server.service.ScheduleService;
+import space.anyi.petServiceManagementSystem.server.vo.schedule.ScheduleVo;
+
+import java.util.List;
+
+@RestController
+@RequestMapping("/admin/schedules")
+public class AdminScheduleController {
+
+    @Autowired
+    private ScheduleService scheduleService;
+
+    @GetMapping
+    @Operation(summary = "获取排班列表")
+    public R<List<ScheduleVo>> list(@RequestParam(required = false) String month) {
+        return R.ok(scheduleService.list(month));
+    }
+
+    @PostMapping
+    @Operation(summary = "添加排班")
+    public R<ScheduleVo> create(@Valid @RequestBody ScheduleDto dto) {
+        return R.ok(scheduleService.create(dto));
+    }
+
+    @DeleteMapping("/{id}")
+    @Operation(summary = "删除排班")
+    public R<Boolean> delete(@PathVariable Long id) {
+        return R.ok(scheduleService.delete(id));
+    }
+}

+ 32 - 0
src/main/java/space/anyi/petServiceManagementSystem/server/controller/admin/AdminUserController.java

@@ -0,0 +1,32 @@
+package space.anyi.petServiceManagementSystem.server.controller.admin;
+
+import io.swagger.v3.oas.annotations.Operation;
+import jakarta.validation.Valid;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+import space.anyi.petServiceManagementSystem.server.common.PageVo;
+import space.anyi.petServiceManagementSystem.server.common.R;
+import space.anyi.petServiceManagementSystem.server.dto.room.QueryByPageParams;
+import space.anyi.petServiceManagementSystem.server.service.UserService;
+import space.anyi.petServiceManagementSystem.server.vo.user.AdminUserDetailVo;
+import space.anyi.petServiceManagementSystem.server.vo.user.UserVo;
+
+@RestController
+@RequestMapping("/admin/users")
+public class AdminUserController {
+
+    @Autowired
+    private UserService userService;
+
+    @GetMapping
+    @Operation(summary = "获取注册用户列表")
+    public R<PageVo<UserVo>> list(@Valid QueryByPageParams params) {
+        return R.ok(userService.queryByPage(params));
+    }
+
+    @GetMapping("/{id}")
+    @Operation(summary = "获取用户详情及宠物档案")
+    public R<AdminUserDetailVo> getDetail(@PathVariable Long id) {
+        return R.ok(userService.getAdminDetail(id));
+    }
+}

+ 31 - 0
src/main/java/space/anyi/petServiceManagementSystem/server/dto/appointment/AppointmentDto.java

@@ -0,0 +1,31 @@
+package space.anyi.petServiceManagementSystem.server.dto.appointment;
+
+import jakarta.validation.constraints.NotNull;
+
+import java.time.LocalDate;
+
+public class AppointmentDto {
+    @NotNull(message = "宠物ID不能为空")
+    private Long petId;
+
+    @NotNull(message = "套餐ID不能为空")
+    private Long packageId;
+
+    private Long roomId;
+    private LocalDate startDate;
+    private LocalDate endDate;
+    private String remark;
+
+    public Long getPetId() { return petId; }
+    public void setPetId(Long petId) { this.petId = petId; }
+    public Long getPackageId() { return packageId; }
+    public void setPackageId(Long packageId) { this.packageId = packageId; }
+    public Long getRoomId() { return roomId; }
+    public void setRoomId(Long roomId) { this.roomId = roomId; }
+    public LocalDate getStartDate() { return startDate; }
+    public void setStartDate(LocalDate startDate) { this.startDate = startDate; }
+    public LocalDate getEndDate() { return endDate; }
+    public void setEndDate(LocalDate endDate) { this.endDate = endDate; }
+    public String getRemark() { return remark; }
+    public void setRemark(String remark) { this.remark = remark; }
+}

+ 11 - 0
src/main/java/space/anyi/petServiceManagementSystem/server/dto/appointment/HandleCancelDto.java

@@ -0,0 +1,11 @@
+package space.anyi.petServiceManagementSystem.server.dto.appointment;
+
+import jakarta.validation.constraints.NotBlank;
+
+public class HandleCancelDto {
+    @NotBlank(message = "处理结果不能为空")
+    private String action;
+
+    public String getAction() { return action; }
+    public void setAction(String action) { this.action = action; }
+}

+ 15 - 0
src/main/java/space/anyi/petServiceManagementSystem/server/dto/appointment/ReviewDto.java

@@ -0,0 +1,15 @@
+package space.anyi.petServiceManagementSystem.server.dto.appointment;
+
+import jakarta.validation.constraints.NotBlank;
+
+public class ReviewDto {
+    @NotBlank(message = "审核结果不能为空")
+    private String action;
+
+    private String rejectReason;
+
+    public String getAction() { return action; }
+    public void setAction(String action) { this.action = action; }
+    public String getRejectReason() { return rejectReason; }
+    public void setRejectReason(String rejectReason) { this.rejectReason = rejectReason; }
+}

+ 16 - 0
src/main/java/space/anyi/petServiceManagementSystem/server/dto/auth/LoginDto.java

@@ -0,0 +1,16 @@
+package space.anyi.petServiceManagementSystem.server.dto.auth;
+
+import jakarta.validation.constraints.NotBlank;
+
+public class LoginDto {
+    @NotBlank(message = "账号不能为空")
+    private String account;
+
+    @NotBlank(message = "密码不能为空")
+    private String password;
+
+    public String getAccount() { return account; }
+    public void setAccount(String account) { this.account = account; }
+    public String getPassword() { return password; }
+    public void setPassword(String password) { this.password = password; }
+}

+ 25 - 0
src/main/java/space/anyi/petServiceManagementSystem/server/dto/auth/RegisterDto.java

@@ -0,0 +1,25 @@
+package space.anyi.petServiceManagementSystem.server.dto.auth;
+
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.Size;
+
+public class RegisterDto {
+    @NotBlank
+    @Size(min = 4, max = 20, message = "账号长度需在4-20之间")
+    private String account;
+
+    @NotBlank
+    @Size(min = 4, max = 20, message = "密码长度需在4-20之间")
+    private String password;
+
+    @NotBlank
+    @Size(min = 2, max = 32, message = "用户名长度需在2-32之间")
+    private String username;
+
+    public String getAccount() { return account; }
+    public void setAccount(String account) { this.account = account; }
+    public String getPassword() { return password; }
+    public void setPassword(String password) { this.password = password; }
+    public String getUsername() { return username; }
+    public void setUsername(String username) { this.username = username; }
+}

+ 29 - 0
src/main/java/space/anyi/petServiceManagementSystem/server/dto/pet/DewormingDto.java

@@ -0,0 +1,29 @@
+package space.anyi.petServiceManagementSystem.server.dto.pet;
+
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.NotNull;
+
+import java.time.LocalDate;
+
+public class DewormingDto {
+    @NotBlank(message = "驱虫名称不能为空")
+    private String dewormingName;
+
+    @NotNull(message = "驱虫日期不能为空")
+    private LocalDate dewormingDate;
+
+    private String hospital;
+    private String doctor;
+    private String remark;
+
+    public String getDewormingName() { return dewormingName; }
+    public void setDewormingName(String dewormingName) { this.dewormingName = dewormingName; }
+    public LocalDate getDewormingDate() { return dewormingDate; }
+    public void setDewormingDate(LocalDate dewormingDate) { this.dewormingDate = dewormingDate; }
+    public String getHospital() { return hospital; }
+    public void setHospital(String hospital) { this.hospital = hospital; }
+    public String getDoctor() { return doctor; }
+    public void setDoctor(String doctor) { this.doctor = doctor; }
+    public String getRemark() { return remark; }
+    public void setRemark(String remark) { this.remark = remark; }
+}

+ 38 - 0
src/main/java/space/anyi/petServiceManagementSystem/server/dto/pet/PetDto.java

@@ -0,0 +1,38 @@
+package space.anyi.petServiceManagementSystem.server.dto.pet;
+
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.NotNull;
+
+import java.time.LocalDate;
+
+public class PetDto {
+    @NotBlank(message = "宠物名称不能为空")
+    private String name;
+
+    @NotBlank(message = "宠物物种不能为空")
+    private String species;
+
+    private String breed;
+    private String gender;
+    private LocalDate birthDate;
+    private Double weight;
+    private String avatar;
+    private String remark;
+
+    public String getName() { return name; }
+    public void setName(String name) { this.name = name; }
+    public String getSpecies() { return species; }
+    public void setSpecies(String species) { this.species = species; }
+    public String getBreed() { return breed; }
+    public void setBreed(String breed) { this.breed = breed; }
+    public String getGender() { return gender; }
+    public void setGender(String gender) { this.gender = gender; }
+    public LocalDate getBirthDate() { return birthDate; }
+    public void setBirthDate(LocalDate birthDate) { this.birthDate = birthDate; }
+    public Double getWeight() { return weight; }
+    public void setWeight(Double weight) { this.weight = weight; }
+    public String getAvatar() { return avatar; }
+    public void setAvatar(String avatar) { this.avatar = avatar; }
+    public String getRemark() { return remark; }
+    public void setRemark(String remark) { this.remark = remark; }
+}

+ 29 - 0
src/main/java/space/anyi/petServiceManagementSystem/server/dto/pet/VaccinationDto.java

@@ -0,0 +1,29 @@
+package space.anyi.petServiceManagementSystem.server.dto.pet;
+
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.NotNull;
+
+import java.time.LocalDate;
+
+public class VaccinationDto {
+    @NotBlank(message = "疫苗名称不能为空")
+    private String vaccineName;
+
+    @NotNull(message = "接种日期不能为空")
+    private LocalDate vaccineDate;
+
+    private String hospital;
+    private String doctor;
+    private String remark;
+
+    public String getVaccineName() { return vaccineName; }
+    public void setVaccineName(String vaccineName) { this.vaccineName = vaccineName; }
+    public LocalDate getVaccineDate() { return vaccineDate; }
+    public void setVaccineDate(LocalDate vaccineDate) { this.vaccineDate = vaccineDate; }
+    public String getHospital() { return hospital; }
+    public void setHospital(String hospital) { this.hospital = hospital; }
+    public String getDoctor() { return doctor; }
+    public void setDoctor(String doctor) { this.doctor = doctor; }
+    public String getRemark() { return remark; }
+    public void setRemark(String remark) { this.remark = remark; }
+}

+ 35 - 0
src/main/java/space/anyi/petServiceManagementSystem/server/dto/room/QueryByPageParams.java

@@ -0,0 +1,35 @@
+package space.anyi.petServiceManagementSystem.server.dto.room;
+
+import jakarta.validation.constraints.Min;
+
+public class QueryByPageParams {
+    @Min(value = 1, message = "页码最小为1")
+    private int pageNum = 1;
+
+    @Min(value = 1, message = "每页条数最小为1")
+    private int pageSize = 10;
+
+    private String username;
+    private String account;
+    private String role;
+    private Integer enable;
+    private String status;
+    private String type;
+
+    public int getPageNum() { return pageNum; }
+    public void setPageNum(int pageNum) { this.pageNum = pageNum; }
+    public int getPageSize() { return pageSize; }
+    public void setPageSize(int pageSize) { this.pageSize = pageSize; }
+    public String getUsername() { return username; }
+    public void setUsername(String username) { this.username = username; }
+    public String getAccount() { return account; }
+    public void setAccount(String account) { this.account = account; }
+    public String getRole() { return role; }
+    public void setRole(String role) { this.role = role; }
+    public Integer getEnable() { return enable; }
+    public void setEnable(Integer enable) { this.enable = enable; }
+    public String getStatus() { return status; }
+    public void setStatus(String status) { this.status = status; }
+    public String getType() { return type; }
+    public void setType(String type) { this.type = type; }
+}

+ 19 - 0
src/main/java/space/anyi/petServiceManagementSystem/server/dto/room/RoomStatusDto.java

@@ -0,0 +1,19 @@
+package space.anyi.petServiceManagementSystem.server.dto.room;
+
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.NotNull;
+
+import java.time.LocalDate;
+
+public class RoomStatusDto {
+    @NotNull(message = "日期不能为空")
+    private LocalDate date;
+
+    @NotBlank(message = "房态不能为空")
+    private String status;
+
+    public LocalDate getDate() { return date; }
+    public void setDate(LocalDate date) { this.date = date; }
+    public String getStatus() { return status; }
+    public void setStatus(String status) { this.status = status; }
+}

+ 28 - 0
src/main/java/space/anyi/petServiceManagementSystem/server/dto/schedule/ScheduleDto.java

@@ -0,0 +1,28 @@
+package space.anyi.petServiceManagementSystem.server.dto.schedule;
+
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.NotNull;
+
+import java.time.LocalDate;
+
+public class ScheduleDto {
+    @NotBlank(message = "员工姓名不能为空")
+    private String employeeName;
+
+    @NotNull(message = "工作日期不能为空")
+    private LocalDate workDate;
+
+    @NotBlank(message = "时间段不能为空")
+    private String timeSlot;
+
+    private String remark;
+
+    public String getEmployeeName() { return employeeName; }
+    public void setEmployeeName(String employeeName) { this.employeeName = employeeName; }
+    public LocalDate getWorkDate() { return workDate; }
+    public void setWorkDate(LocalDate workDate) { this.workDate = workDate; }
+    public String getTimeSlot() { return timeSlot; }
+    public void setTimeSlot(String timeSlot) { this.timeSlot = timeSlot; }
+    public String getRemark() { return remark; }
+    public void setRemark(String remark) { this.remark = remark; }
+}

+ 34 - 0
src/main/java/space/anyi/petServiceManagementSystem/server/dto/servicePackage/PackageDto.java

@@ -0,0 +1,34 @@
+package space.anyi.petServiceManagementSystem.server.dto.servicePackage;
+
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.NotNull;
+
+import java.math.BigDecimal;
+
+public class PackageDto {
+    @NotBlank(message = "套餐名称不能为空")
+    private String name;
+
+    @NotBlank(message = "套餐类型不能为空")
+    private String type;
+
+    @NotNull(message = "价格不能为空")
+    private BigDecimal price;
+
+    private Integer duration;
+    private String description;
+    private Integer isActive;
+
+    public String getName() { return name; }
+    public void setName(String name) { this.name = name; }
+    public String getType() { return type; }
+    public void setType(String type) { this.type = type; }
+    public BigDecimal getPrice() { return price; }
+    public void setPrice(BigDecimal price) { this.price = price; }
+    public Integer getDuration() { return duration; }
+    public void setDuration(Integer duration) { this.duration = duration; }
+    public String getDescription() { return description; }
+    public void setDescription(String description) { this.description = description; }
+    public Integer getIsActive() { return isActive; }
+    public void setIsActive(Integer isActive) { this.isActive = isActive; }
+}

+ 15 - 0
src/main/java/space/anyi/petServiceManagementSystem/server/dto/user/UpdateUserAvatarDto.java

@@ -0,0 +1,15 @@
+package space.anyi.petServiceManagementSystem.server.dto.user;
+
+import jakarta.validation.constraints.NotBlank;
+
+public class UpdateUserAvatarDto {
+    private Long id;
+
+    @NotBlank(message = "头像不能为空")
+    private String avatar;
+
+    public Long getId() { return id; }
+    public void setId(Long id) { this.id = id; }
+    public String getAvatar() { return avatar; }
+    public void setAvatar(String avatar) { this.avatar = avatar; }
+}

+ 20 - 0
src/main/java/space/anyi/petServiceManagementSystem/server/dto/user/UpdateUserPasswordDto.java

@@ -0,0 +1,20 @@
+package space.anyi.petServiceManagementSystem.server.dto.user;
+
+import jakarta.validation.constraints.NotBlank;
+
+public class UpdateUserPasswordDto {
+    private Long id;
+
+    @NotBlank(message = "旧密码不能为空")
+    private String oldPassword;
+
+    @NotBlank(message = "新密码不能为空")
+    private String newPassword;
+
+    public Long getId() { return id; }
+    public void setId(Long id) { this.id = id; }
+    public String getOldPassword() { return oldPassword; }
+    public void setOldPassword(String oldPassword) { this.oldPassword = oldPassword; }
+    public String getNewPassword() { return newPassword; }
+    public void setNewPassword(String newPassword) { this.newPassword = newPassword; }
+}

+ 15 - 0
src/main/java/space/anyi/petServiceManagementSystem/server/dto/user/UpdateUserStatusDto.java

@@ -0,0 +1,15 @@
+package space.anyi.petServiceManagementSystem.server.dto.user;
+
+import jakarta.validation.constraints.NotNull;
+
+public class UpdateUserStatusDto {
+    private Long id;
+
+    @NotNull(message = "状态不能为空")
+    private Integer enable;
+
+    public Long getId() { return id; }
+    public void setId(Long id) { this.id = id; }
+    public Integer getEnable() { return enable; }
+    public void setEnable(Integer enable) { this.enable = enable; }
+}

+ 32 - 0
src/main/java/space/anyi/petServiceManagementSystem/server/dto/user/UserDto.java

@@ -0,0 +1,32 @@
+package space.anyi.petServiceManagementSystem.server.dto.user;
+
+public class UserDto {
+    private Long id;
+    private String account;
+    private String username;
+    private String password;
+    private String email;
+    private String phone;
+    private String avatar;
+    private String role;
+    private Integer enable;
+
+    public Long getId() { return id; }
+    public void setId(Long id) { this.id = id; }
+    public String getAccount() { return account; }
+    public void setAccount(String account) { this.account = account; }
+    public String getUsername() { return username; }
+    public void setUsername(String username) { this.username = username; }
+    public String getPassword() { return password; }
+    public void setPassword(String password) { this.password = password; }
+    public String getEmail() { return email; }
+    public void setEmail(String email) { this.email = email; }
+    public String getPhone() { return phone; }
+    public void setPhone(String phone) { this.phone = phone; }
+    public String getAvatar() { return avatar; }
+    public void setAvatar(String avatar) { this.avatar = avatar; }
+    public String getRole() { return role; }
+    public void setRole(String role) { this.role = role; }
+    public Integer getEnable() { return enable; }
+    public void setEnable(Integer enable) { this.enable = enable; }
+}

+ 60 - 0
src/main/java/space/anyi/petServiceManagementSystem/server/entity/Appointment.java

@@ -0,0 +1,60 @@
+package space.anyi.petServiceManagementSystem.server.entity;
+
+import java.math.BigDecimal;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+
+public class Appointment {
+    private Long id;
+    private Long userId;
+    private Long petId;
+    private Long packageId;
+    private Long roomId;
+    private String type;
+    private String status;
+    private LocalDate startDate;
+    private LocalDate endDate;
+    private BigDecimal totalPrice;
+    private BigDecimal deposit;
+    private Boolean depositPaid;
+    private String cancelReason;
+    private String rejectReason;
+    private String remark;
+    private LocalDateTime createdAt;
+    private LocalDateTime updatedAt;
+
+    public Long getId() { return id; }
+    public void setId(Long id) { this.id = id; }
+    public Long getUserId() { return userId; }
+    public void setUserId(Long userId) { this.userId = userId; }
+    public Long getPetId() { return petId; }
+    public void setPetId(Long petId) { this.petId = petId; }
+    public Long getPackageId() { return packageId; }
+    public void setPackageId(Long packageId) { this.packageId = packageId; }
+    public Long getRoomId() { return roomId; }
+    public void setRoomId(Long roomId) { this.roomId = roomId; }
+    public String getType() { return type; }
+    public void setType(String type) { this.type = type; }
+    public String getStatus() { return status; }
+    public void setStatus(String status) { this.status = status; }
+    public LocalDate getStartDate() { return startDate; }
+    public void setStartDate(LocalDate startDate) { this.startDate = startDate; }
+    public LocalDate getEndDate() { return endDate; }
+    public void setEndDate(LocalDate endDate) { this.endDate = endDate; }
+    public BigDecimal getTotalPrice() { return totalPrice; }
+    public void setTotalPrice(BigDecimal totalPrice) { this.totalPrice = totalPrice; }
+    public BigDecimal getDeposit() { return deposit; }
+    public void setDeposit(BigDecimal deposit) { this.deposit = deposit; }
+    public Boolean getDepositPaid() { return depositPaid; }
+    public void setDepositPaid(Boolean depositPaid) { this.depositPaid = depositPaid; }
+    public String getCancelReason() { return cancelReason; }
+    public void setCancelReason(String cancelReason) { this.cancelReason = cancelReason; }
+    public String getRejectReason() { return rejectReason; }
+    public void setRejectReason(String rejectReason) { this.rejectReason = rejectReason; }
+    public String getRemark() { return remark; }
+    public void setRemark(String remark) { this.remark = remark; }
+    public LocalDateTime getCreatedAt() { return createdAt; }
+    public void setCreatedAt(LocalDateTime createdAt) { this.createdAt = createdAt; }
+    public LocalDateTime getUpdatedAt() { return updatedAt; }
+    public void setUpdatedAt(LocalDateTime updatedAt) { this.updatedAt = updatedAt; }
+}

+ 35 - 0
src/main/java/space/anyi/petServiceManagementSystem/server/entity/BoardingRoom.java

@@ -0,0 +1,35 @@
+package space.anyi.petServiceManagementSystem.server.entity;
+
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+
+public class BoardingRoom {
+    private Long id;
+    private String roomName;
+    private String roomType;
+    private Integer capacity;
+    private BigDecimal pricePerDay;
+    private Integer isActive;
+    private String description;
+    private LocalDateTime createdAt;
+    private LocalDateTime updatedAt;
+
+    public Long getId() { return id; }
+    public void setId(Long id) { this.id = id; }
+    public String getRoomName() { return roomName; }
+    public void setRoomName(String roomName) { this.roomName = roomName; }
+    public String getRoomType() { return roomType; }
+    public void setRoomType(String roomType) { this.roomType = roomType; }
+    public Integer getCapacity() { return capacity; }
+    public void setCapacity(Integer capacity) { this.capacity = capacity; }
+    public BigDecimal getPricePerDay() { return pricePerDay; }
+    public void setPricePerDay(BigDecimal pricePerDay) { this.pricePerDay = pricePerDay; }
+    public Integer getIsActive() { return isActive; }
+    public void setIsActive(Integer isActive) { this.isActive = isActive; }
+    public String getDescription() { return description; }
+    public void setDescription(String description) { this.description = description; }
+    public LocalDateTime getCreatedAt() { return createdAt; }
+    public void setCreatedAt(LocalDateTime createdAt) { this.createdAt = createdAt; }
+    public LocalDateTime getUpdatedAt() { return updatedAt; }
+    public void setUpdatedAt(LocalDateTime updatedAt) { this.updatedAt = updatedAt; }
+}

+ 26 - 0
src/main/java/space/anyi/petServiceManagementSystem/server/entity/BoardingRoomStatus.java

@@ -0,0 +1,26 @@
+package space.anyi.petServiceManagementSystem.server.entity;
+
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+
+public class BoardingRoomStatus {
+    private Long id;
+    private Long roomId;
+    private LocalDate date;
+    private String status;
+    private Long appointmentId;
+    private LocalDateTime createdAt;
+
+    public Long getId() { return id; }
+    public void setId(Long id) { this.id = id; }
+    public Long getRoomId() { return roomId; }
+    public void setRoomId(Long roomId) { this.roomId = roomId; }
+    public LocalDate getDate() { return date; }
+    public void setDate(LocalDate date) { this.date = date; }
+    public String getStatus() { return status; }
+    public void setStatus(String status) { this.status = status; }
+    public Long getAppointmentId() { return appointmentId; }
+    public void setAppointmentId(Long appointmentId) { this.appointmentId = appointmentId; }
+    public LocalDateTime getCreatedAt() { return createdAt; }
+    public void setCreatedAt(LocalDateTime createdAt) { this.createdAt = createdAt; }
+}

+ 32 - 0
src/main/java/space/anyi/petServiceManagementSystem/server/entity/DewormingRecord.java

@@ -0,0 +1,32 @@
+package space.anyi.petServiceManagementSystem.server.entity;
+
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+
+public class DewormingRecord {
+    private Long id;
+    private Long petId;
+    private String dewormingName;
+    private LocalDate dewormingDate;
+    private String hospital;
+    private String doctor;
+    private String remark;
+    private LocalDateTime createdAt;
+
+    public Long getId() { return id; }
+    public void setId(Long id) { this.id = id; }
+    public Long getPetId() { return petId; }
+    public void setPetId(Long petId) { this.petId = petId; }
+    public String getDewormingName() { return dewormingName; }
+    public void setDewormingName(String dewormingName) { this.dewormingName = dewormingName; }
+    public LocalDate getDewormingDate() { return dewormingDate; }
+    public void setDewormingDate(LocalDate dewormingDate) { this.dewormingDate = dewormingDate; }
+    public String getHospital() { return hospital; }
+    public void setHospital(String hospital) { this.hospital = hospital; }
+    public String getDoctor() { return doctor; }
+    public void setDoctor(String doctor) { this.doctor = doctor; }
+    public String getRemark() { return remark; }
+    public void setRemark(String remark) { this.remark = remark; }
+    public LocalDateTime getCreatedAt() { return createdAt; }
+    public void setCreatedAt(LocalDateTime createdAt) { this.createdAt = createdAt; }
+}

+ 44 - 0
src/main/java/space/anyi/petServiceManagementSystem/server/entity/Pet.java

@@ -0,0 +1,44 @@
+package space.anyi.petServiceManagementSystem.server.entity;
+
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+
+public class Pet {
+    private Long id;
+    private Long userId;
+    private String name;
+    private String species;
+    private String breed;
+    private String gender;
+    private LocalDate birthDate;
+    private Double weight;
+    private String avatar;
+    private String remark;
+    private LocalDateTime createdAt;
+    private LocalDateTime updatedAt;
+
+    public Long getId() { return id; }
+    public void setId(Long id) { this.id = id; }
+    public Long getUserId() { return userId; }
+    public void setUserId(Long userId) { this.userId = userId; }
+    public String getName() { return name; }
+    public void setName(String name) { this.name = name; }
+    public String getSpecies() { return species; }
+    public void setSpecies(String species) { this.species = species; }
+    public String getBreed() { return breed; }
+    public void setBreed(String breed) { this.breed = breed; }
+    public String getGender() { return gender; }
+    public void setGender(String gender) { this.gender = gender; }
+    public LocalDate getBirthDate() { return birthDate; }
+    public void setBirthDate(LocalDate birthDate) { this.birthDate = birthDate; }
+    public Double getWeight() { return weight; }
+    public void setWeight(Double weight) { this.weight = weight; }
+    public String getAvatar() { return avatar; }
+    public void setAvatar(String avatar) { this.avatar = avatar; }
+    public String getRemark() { return remark; }
+    public void setRemark(String remark) { this.remark = remark; }
+    public LocalDateTime getCreatedAt() { return createdAt; }
+    public void setCreatedAt(LocalDateTime createdAt) { this.createdAt = createdAt; }
+    public LocalDateTime getUpdatedAt() { return updatedAt; }
+    public void setUpdatedAt(LocalDateTime updatedAt) { this.updatedAt = updatedAt; }
+}

+ 26 - 0
src/main/java/space/anyi/petServiceManagementSystem/server/entity/Schedule.java

@@ -0,0 +1,26 @@
+package space.anyi.petServiceManagementSystem.server.entity;
+
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+
+public class Schedule {
+    private Long id;
+    private String employeeName;
+    private LocalDate workDate;
+    private String timeSlot;
+    private String remark;
+    private LocalDateTime createdAt;
+
+    public Long getId() { return id; }
+    public void setId(Long id) { this.id = id; }
+    public String getEmployeeName() { return employeeName; }
+    public void setEmployeeName(String employeeName) { this.employeeName = employeeName; }
+    public LocalDate getWorkDate() { return workDate; }
+    public void setWorkDate(LocalDate workDate) { this.workDate = workDate; }
+    public String getTimeSlot() { return timeSlot; }
+    public void setTimeSlot(String timeSlot) { this.timeSlot = timeSlot; }
+    public String getRemark() { return remark; }
+    public void setRemark(String remark) { this.remark = remark; }
+    public LocalDateTime getCreatedAt() { return createdAt; }
+    public void setCreatedAt(LocalDateTime createdAt) { this.createdAt = createdAt; }
+}

+ 35 - 0
src/main/java/space/anyi/petServiceManagementSystem/server/entity/ServicePackage.java

@@ -0,0 +1,35 @@
+package space.anyi.petServiceManagementSystem.server.entity;
+
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+
+public class ServicePackage {
+    private Long id;
+    private String name;
+    private String type;
+    private BigDecimal price;
+    private Integer duration;
+    private String description;
+    private Integer isActive;
+    private LocalDateTime createdAt;
+    private LocalDateTime updatedAt;
+
+    public Long getId() { return id; }
+    public void setId(Long id) { this.id = id; }
+    public String getName() { return name; }
+    public void setName(String name) { this.name = name; }
+    public String getType() { return type; }
+    public void setType(String type) { this.type = type; }
+    public BigDecimal getPrice() { return price; }
+    public void setPrice(BigDecimal price) { this.price = price; }
+    public Integer getDuration() { return duration; }
+    public void setDuration(Integer duration) { this.duration = duration; }
+    public String getDescription() { return description; }
+    public void setDescription(String description) { this.description = description; }
+    public Integer getIsActive() { return isActive; }
+    public void setIsActive(Integer isActive) { this.isActive = isActive; }
+    public LocalDateTime getCreatedAt() { return createdAt; }
+    public void setCreatedAt(LocalDateTime createdAt) { this.createdAt = createdAt; }
+    public LocalDateTime getUpdatedAt() { return updatedAt; }
+    public void setUpdatedAt(LocalDateTime updatedAt) { this.updatedAt = updatedAt; }
+}

+ 40 - 0
src/main/java/space/anyi/petServiceManagementSystem/server/entity/User.java

@@ -0,0 +1,40 @@
+package space.anyi.petServiceManagementSystem.server.entity;
+
+import java.time.LocalDateTime;
+
+public class User {
+    private Long id;
+    private String account;
+    private String username;
+    private String password;
+    private String email;
+    private String phone;
+    private String avatar;
+    private String role;
+    private Integer enable;
+    private LocalDateTime createdAt;
+    private LocalDateTime updatedAt;
+
+    public Long getId() { return id; }
+    public void setId(Long id) { this.id = id; }
+    public String getAccount() { return account; }
+    public void setAccount(String account) { this.account = account; }
+    public String getUsername() { return username; }
+    public void setUsername(String username) { this.username = username; }
+    public String getPassword() { return password; }
+    public void setPassword(String password) { this.password = password; }
+    public String getEmail() { return email; }
+    public void setEmail(String email) { this.email = email; }
+    public String getPhone() { return phone; }
+    public void setPhone(String phone) { this.phone = phone; }
+    public String getAvatar() { return avatar; }
+    public void setAvatar(String avatar) { this.avatar = avatar; }
+    public String getRole() { return role; }
+    public void setRole(String role) { this.role = role; }
+    public Integer getEnable() { return enable; }
+    public void setEnable(Integer enable) { this.enable = enable; }
+    public LocalDateTime getCreatedAt() { return createdAt; }
+    public void setCreatedAt(LocalDateTime createdAt) { this.createdAt = createdAt; }
+    public LocalDateTime getUpdatedAt() { return updatedAt; }
+    public void setUpdatedAt(LocalDateTime updatedAt) { this.updatedAt = updatedAt; }
+}

+ 32 - 0
src/main/java/space/anyi/petServiceManagementSystem/server/entity/VaccinationRecord.java

@@ -0,0 +1,32 @@
+package space.anyi.petServiceManagementSystem.server.entity;
+
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+
+public class VaccinationRecord {
+    private Long id;
+    private Long petId;
+    private String vaccineName;
+    private LocalDate vaccineDate;
+    private String hospital;
+    private String doctor;
+    private String remark;
+    private LocalDateTime createdAt;
+
+    public Long getId() { return id; }
+    public void setId(Long id) { this.id = id; }
+    public Long getPetId() { return petId; }
+    public void setPetId(Long petId) { this.petId = petId; }
+    public String getVaccineName() { return vaccineName; }
+    public void setVaccineName(String vaccineName) { this.vaccineName = vaccineName; }
+    public LocalDate getVaccineDate() { return vaccineDate; }
+    public void setVaccineDate(LocalDate vaccineDate) { this.vaccineDate = vaccineDate; }
+    public String getHospital() { return hospital; }
+    public void setHospital(String hospital) { this.hospital = hospital; }
+    public String getDoctor() { return doctor; }
+    public void setDoctor(String doctor) { this.doctor = doctor; }
+    public String getRemark() { return remark; }
+    public void setRemark(String remark) { this.remark = remark; }
+    public LocalDateTime getCreatedAt() { return createdAt; }
+    public void setCreatedAt(LocalDateTime createdAt) { this.createdAt = createdAt; }
+}

+ 17 - 0
src/main/java/space/anyi/petServiceManagementSystem/server/interceptor/AdminInterceptor.java

@@ -0,0 +1,17 @@
+package space.anyi.petServiceManagementSystem.server.interceptor;
+
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+import org.springframework.web.servlet.HandlerInterceptor;
+import space.anyi.petServiceManagementSystem.server.common.ForbiddenException;
+
+public class AdminInterceptor implements HandlerInterceptor {
+    @Override
+    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
+        String role = (String) request.getAttribute("role");
+        if (!"admin".equals(role)) {
+            throw new ForbiddenException("无权访问,需要管理员权限");
+        }
+        return true;
+    }
+}

+ 25 - 0
src/main/java/space/anyi/petServiceManagementSystem/server/interceptor/AuthInterceptor.java

@@ -0,0 +1,25 @@
+package space.anyi.petServiceManagementSystem.server.interceptor;
+
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+import jakarta.servlet.http.HttpSession;
+import org.springframework.web.servlet.HandlerInterceptor;
+import space.anyi.petServiceManagementSystem.server.common.UnauthorizedException;
+import space.anyi.petServiceManagementSystem.server.vo.user.UserVo;
+
+public class AuthInterceptor implements HandlerInterceptor {
+    @Override
+    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
+        HttpSession session = request.getSession(false);
+        if (session == null) {
+            throw new UnauthorizedException("未登录");
+        }
+        UserVo user = (UserVo) session.getAttribute("user");
+        if (user == null) {
+            throw new UnauthorizedException("未登录或Session已过期");
+        }
+        request.setAttribute("userId", user.getId());
+        request.setAttribute("role", user.getRole());
+        return true;
+    }
+}

+ 21 - 0
src/main/java/space/anyi/petServiceManagementSystem/server/mapper/AppointmentMapper.java

@@ -0,0 +1,21 @@
+package space.anyi.petServiceManagementSystem.server.mapper;
+
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+import space.anyi.petServiceManagementSystem.server.entity.Appointment;
+
+import java.util.List;
+
+@Mapper
+public interface AppointmentMapper {
+    int insert(Appointment appointment);
+    Appointment selectById(Long id);
+    List<Appointment> selectByUserId(Long userId);
+    List<Appointment> selectAllByPage(@Param("offset") int offset, @Param("pageSize") int pageSize,
+                                       @Param("status") String status, @Param("type") String type);
+    long countAll(@Param("status") String status, @Param("type") String type);
+    int updateStatus(@Param("id") Long id, @Param("status") String status);
+    int updateDepositPaid(@Param("id") Long id, @Param("depositPaid") Boolean depositPaid);
+    int updateCancelReason(@Param("id") Long id, @Param("cancelReason") String cancelReason);
+    int update(Appointment appointment);
+}

+ 14 - 0
src/main/java/space/anyi/petServiceManagementSystem/server/mapper/BoardingRoomMapper.java

@@ -0,0 +1,14 @@
+package space.anyi.petServiceManagementSystem.server.mapper;
+
+import org.apache.ibatis.annotations.Mapper;
+import space.anyi.petServiceManagementSystem.server.entity.BoardingRoom;
+
+import java.util.List;
+
+@Mapper
+public interface BoardingRoomMapper {
+    int insert(BoardingRoom room);
+    BoardingRoom selectById(Long id);
+    List<BoardingRoom> selectActive();
+    int update(BoardingRoom room);
+}

+ 18 - 0
src/main/java/space/anyi/petServiceManagementSystem/server/mapper/BoardingRoomStatusMapper.java

@@ -0,0 +1,18 @@
+package space.anyi.petServiceManagementSystem.server.mapper;
+
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+import space.anyi.petServiceManagementSystem.server.entity.BoardingRoomStatus;
+
+import java.time.LocalDate;
+import java.util.List;
+
+@Mapper
+public interface BoardingRoomStatusMapper {
+    int insertOrUpdate(BoardingRoomStatus status);
+    List<BoardingRoomStatus> selectByRoomAndDateRange(@Param("roomId") Long roomId,
+                                                       @Param("startDate") LocalDate startDate,
+                                                       @Param("endDate") LocalDate endDate);
+    int deleteByAppointmentId(Long appointmentId);
+    int insertBatch(@Param("list") List<BoardingRoomStatus> list);
+}

+ 14 - 0
src/main/java/space/anyi/petServiceManagementSystem/server/mapper/DewormingRecordMapper.java

@@ -0,0 +1,14 @@
+package space.anyi.petServiceManagementSystem.server.mapper;
+
+import org.apache.ibatis.annotations.Mapper;
+import space.anyi.petServiceManagementSystem.server.entity.DewormingRecord;
+
+import java.util.List;
+
+@Mapper
+public interface DewormingRecordMapper {
+    int insert(DewormingRecord record);
+    List<DewormingRecord> selectByPetId(Long petId);
+    int deleteByPetId(Long petId);
+    int deleteById(Long id);
+}

+ 16 - 0
src/main/java/space/anyi/petServiceManagementSystem/server/mapper/PackageMapper.java

@@ -0,0 +1,16 @@
+package space.anyi.petServiceManagementSystem.server.mapper;
+
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+import space.anyi.petServiceManagementSystem.server.entity.ServicePackage;
+
+import java.util.List;
+
+@Mapper
+public interface PackageMapper {
+    int insert(ServicePackage pkg);
+    ServicePackage selectById(Long id);
+    List<ServicePackage> selectByType(@Param("type") String type);
+    int update(ServicePackage pkg);
+    int deleteById(Long id);
+}

+ 16 - 0
src/main/java/space/anyi/petServiceManagementSystem/server/mapper/PetMapper.java

@@ -0,0 +1,16 @@
+package space.anyi.petServiceManagementSystem.server.mapper;
+
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+import space.anyi.petServiceManagementSystem.server.entity.Pet;
+
+import java.util.List;
+
+@Mapper
+public interface PetMapper {
+    int insert(Pet pet);
+    Pet selectById(Long id);
+    List<Pet> selectByUserId(Long userId);
+    int update(Pet pet);
+    int deleteById(Long id);
+}

+ 19 - 0
src/main/java/space/anyi/petServiceManagementSystem/server/mapper/ScheduleMapper.java

@@ -0,0 +1,19 @@
+package space.anyi.petServiceManagementSystem.server.mapper;
+
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+import space.anyi.petServiceManagementSystem.server.entity.Schedule;
+
+import java.time.LocalDate;
+import java.util.List;
+
+@Mapper
+public interface ScheduleMapper {
+    int insert(Schedule schedule);
+    List<Schedule> selectByMonth(@Param("startDate") LocalDate startDate,
+                                  @Param("endDate") LocalDate endDate);
+    int deleteById(Long id);
+    int countConflict(@Param("employeeName") String employeeName,
+                      @Param("workDate") LocalDate workDate,
+                      @Param("timeSlot") String timeSlot);
+}

+ 24 - 0
src/main/java/space/anyi/petServiceManagementSystem/server/mapper/UserMapper.java

@@ -0,0 +1,24 @@
+package space.anyi.petServiceManagementSystem.server.mapper;
+
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+import space.anyi.petServiceManagementSystem.server.entity.User;
+
+import java.util.List;
+
+@Mapper
+public interface UserMapper {
+    int insert(User user);
+    User selectById(Long id);
+    User selectByAccount(String account);
+    int update(User user);
+    int deleteByIds(@Param("ids") List<Long> ids);
+    List<User> selectByPage(@Param("offset") int offset, @Param("pageSize") int pageSize,
+                            @Param("username") String username, @Param("account") String account,
+                            @Param("role") String role, @Param("enable") Integer enable);
+    long countByCondition(@Param("username") String username, @Param("account") String account,
+                          @Param("role") String role, @Param("enable") Integer enable);
+    int updatePassword(@Param("id") Long id, @Param("password") String password);
+    int updateStatus(@Param("id") Long id, @Param("enable") Integer enable);
+    int updateAvatar(@Param("id") Long id, @Param("avatar") String avatar);
+}

+ 15 - 0
src/main/java/space/anyi/petServiceManagementSystem/server/mapper/VaccinationRecordMapper.java

@@ -0,0 +1,15 @@
+package space.anyi.petServiceManagementSystem.server.mapper;
+
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+import space.anyi.petServiceManagementSystem.server.entity.VaccinationRecord;
+
+import java.util.List;
+
+@Mapper
+public interface VaccinationRecordMapper {
+    int insert(VaccinationRecord record);
+    List<VaccinationRecord> selectByPetId(Long petId);
+    int deleteByPetId(Long petId);
+    int deleteById(Long id);
+}

+ 21 - 0
src/main/java/space/anyi/petServiceManagementSystem/server/service/AppointmentService.java

@@ -0,0 +1,21 @@
+package space.anyi.petServiceManagementSystem.server.service;
+
+import space.anyi.petServiceManagementSystem.server.common.PageVo;
+import space.anyi.petServiceManagementSystem.server.dto.appointment.AppointmentDto;
+import space.anyi.petServiceManagementSystem.server.dto.appointment.HandleCancelDto;
+import space.anyi.petServiceManagementSystem.server.dto.appointment.ReviewDto;
+import space.anyi.petServiceManagementSystem.server.dto.room.QueryByPageParams;
+import space.anyi.petServiceManagementSystem.server.vo.appointment.AppointmentVo;
+
+import java.util.List;
+
+public interface AppointmentService {
+    AppointmentVo create(AppointmentDto dto, Long userId);
+    List<AppointmentVo> list(Long userId);
+    AppointmentVo getById(Long id, Long userId);
+    Boolean cancel(Long id, String reason, Long userId);
+    Boolean payDeposit(Long id, Long userId);
+    PageVo<AppointmentVo> listAll(QueryByPageParams params);
+    Boolean review(Long id, ReviewDto dto);
+    Boolean handleCancel(Long id, HandleCancelDto dto);
+}

+ 11 - 0
src/main/java/space/anyi/petServiceManagementSystem/server/service/AuthService.java

@@ -0,0 +1,11 @@
+package space.anyi.petServiceManagementSystem.server.service;
+
+import jakarta.servlet.http.HttpServletRequest;
+import space.anyi.petServiceManagementSystem.server.dto.auth.LoginDto;
+import space.anyi.petServiceManagementSystem.server.dto.auth.RegisterDto;
+import space.anyi.petServiceManagementSystem.server.vo.user.UserVo;
+
+public interface AuthService {
+    UserVo register(RegisterDto dto, HttpServletRequest request);
+    UserVo login(LoginDto dto, HttpServletRequest request);
+}

+ 13 - 0
src/main/java/space/anyi/petServiceManagementSystem/server/service/BoardingRoomService.java

@@ -0,0 +1,13 @@
+package space.anyi.petServiceManagementSystem.server.service;
+
+import space.anyi.petServiceManagementSystem.server.dto.room.RoomStatusDto;
+import space.anyi.petServiceManagementSystem.server.vo.room.BoardingRoomVo;
+import space.anyi.petServiceManagementSystem.server.vo.room.RoomStatusVo;
+
+import java.util.List;
+
+public interface BoardingRoomService {
+    List<BoardingRoomVo> listRooms();
+    List<RoomStatusVo> getStatus(Long roomId, String month);
+    Boolean updateStatus(Long roomId, RoomStatusDto dto);
+}

+ 14 - 0
src/main/java/space/anyi/petServiceManagementSystem/server/service/PackageService.java

@@ -0,0 +1,14 @@
+package space.anyi.petServiceManagementSystem.server.service;
+
+import space.anyi.petServiceManagementSystem.server.dto.servicePackage.PackageDto;
+import space.anyi.petServiceManagementSystem.server.vo.servicePackage.PackageVo;
+
+import java.util.List;
+
+public interface PackageService {
+    List<PackageVo> list(String type);
+    PackageVo getById(Long id);
+    PackageVo create(PackageDto dto);
+    Boolean update(Long id, PackageDto dto);
+    Boolean delete(Long id);
+}

+ 22 - 0
src/main/java/space/anyi/petServiceManagementSystem/server/service/PetService.java

@@ -0,0 +1,22 @@
+package space.anyi.petServiceManagementSystem.server.service;
+
+import space.anyi.petServiceManagementSystem.server.dto.pet.DewormingDto;
+import space.anyi.petServiceManagementSystem.server.dto.pet.PetDto;
+import space.anyi.petServiceManagementSystem.server.dto.pet.VaccinationDto;
+import space.anyi.petServiceManagementSystem.server.vo.pet.DewormingVo;
+import space.anyi.petServiceManagementSystem.server.vo.pet.PetVo;
+import space.anyi.petServiceManagementSystem.server.vo.pet.VaccinationVo;
+
+import java.util.List;
+
+public interface PetService {
+    List<PetVo> list(Long userId);
+    PetVo add(PetDto dto, Long userId);
+    PetVo getById(Long id, Long userId);
+    Boolean update(Long id, PetDto dto, Long userId);
+    Boolean delete(Long id, Long userId);
+    List<VaccinationVo> getVaccinations(Long petId, Long userId);
+    VaccinationVo addVaccination(Long petId, VaccinationDto dto, Long userId);
+    List<DewormingVo> getDeworming(Long petId, Long userId);
+    DewormingVo addDeworming(Long petId, DewormingDto dto, Long userId);
+}

+ 12 - 0
src/main/java/space/anyi/petServiceManagementSystem/server/service/ScheduleService.java

@@ -0,0 +1,12 @@
+package space.anyi.petServiceManagementSystem.server.service;
+
+import space.anyi.petServiceManagementSystem.server.dto.schedule.ScheduleDto;
+import space.anyi.petServiceManagementSystem.server.vo.schedule.ScheduleVo;
+
+import java.util.List;
+
+public interface ScheduleService {
+    List<ScheduleVo> list(String month);
+    ScheduleVo create(ScheduleDto dto);
+    Boolean delete(Long id);
+}

+ 24 - 0
src/main/java/space/anyi/petServiceManagementSystem/server/service/UserService.java

@@ -0,0 +1,24 @@
+package space.anyi.petServiceManagementSystem.server.service;
+
+import space.anyi.petServiceManagementSystem.server.common.PageVo;
+import space.anyi.petServiceManagementSystem.server.dto.room.QueryByPageParams;
+import space.anyi.petServiceManagementSystem.server.dto.user.UpdateUserAvatarDto;
+import space.anyi.petServiceManagementSystem.server.dto.user.UpdateUserPasswordDto;
+import space.anyi.petServiceManagementSystem.server.dto.user.UpdateUserStatusDto;
+import space.anyi.petServiceManagementSystem.server.dto.user.UserDto;
+import space.anyi.petServiceManagementSystem.server.vo.user.AdminUserDetailVo;
+import space.anyi.petServiceManagementSystem.server.vo.user.UserVo;
+
+import java.util.List;
+
+public interface UserService {
+    PageVo<UserVo> queryByPage(QueryByPageParams params);
+    UserVo queryById(Long id);
+    Boolean add(UserDto dto);
+    Boolean edit(UserDto dto);
+    Boolean deleteByIds(List<Long> ids);
+    void updateStatus(UpdateUserStatusDto dto);
+    void updatePassword(UpdateUserPasswordDto dto);
+    void updateAvatar(UpdateUserAvatarDto dto);
+    AdminUserDetailVo getAdminDetail(Long id);
+}

+ 242 - 0
src/main/java/space/anyi/petServiceManagementSystem/server/service/impl/AppointmentServiceImpl.java

@@ -0,0 +1,242 @@
+package space.anyi.petServiceManagementSystem.server.service.impl;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import space.anyi.petServiceManagementSystem.server.common.BusinessException;
+import space.anyi.petServiceManagementSystem.server.common.PageVo;
+import space.anyi.petServiceManagementSystem.server.dto.appointment.AppointmentDto;
+import space.anyi.petServiceManagementSystem.server.dto.appointment.HandleCancelDto;
+import space.anyi.petServiceManagementSystem.server.dto.appointment.ReviewDto;
+import space.anyi.petServiceManagementSystem.server.dto.room.QueryByPageParams;
+import space.anyi.petServiceManagementSystem.server.entity.*;
+import space.anyi.petServiceManagementSystem.server.mapper.*;
+import space.anyi.petServiceManagementSystem.server.service.AppointmentService;
+import space.anyi.petServiceManagementSystem.server.util.PageUtil;
+import space.anyi.petServiceManagementSystem.server.vo.appointment.AppointmentVo;
+
+import java.math.BigDecimal;
+import java.time.LocalDate;
+import java.time.temporal.ChronoUnit;
+import java.util.List;
+import java.util.stream.Collectors;
+
+@Service
+public class AppointmentServiceImpl implements AppointmentService {
+
+    @Autowired
+    private AppointmentMapper appointmentMapper;
+
+    @Autowired
+    private PetMapper petMapper;
+
+    @Autowired
+    private PackageMapper packageMapper;
+
+    @Autowired
+    private BoardingRoomMapper boardingRoomMapper;
+
+    @Autowired
+    private BoardingRoomStatusMapper boardingRoomStatusMapper;
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public AppointmentVo create(AppointmentDto dto, Long userId) {
+        ServicePackage pkg = packageMapper.selectById(dto.getPackageId());
+        if (pkg == null || pkg.getIsActive() == 0) {
+            throw new BusinessException("套餐不存在或已下架");
+        }
+
+        Pet pet = petMapper.selectById(dto.getPetId());
+        if (pet == null || !pet.getUserId().equals(userId)) {
+            throw new BusinessException("宠物不存在或无权访问");
+        }
+
+        Appointment appointment = new Appointment();
+        appointment.setUserId(userId);
+        appointment.setPetId(dto.getPetId());
+        appointment.setPackageId(dto.getPackageId());
+        appointment.setType(pkg.getType());
+        appointment.setStatus("PENDING");
+        appointment.setDepositPaid(false);
+        appointment.setRemark(dto.getRemark());
+
+        if ("BOARDING".equals(pkg.getType())) {
+            if (dto.getRoomId() == null || dto.getStartDate() == null || dto.getEndDate() == null) {
+                throw new BusinessException("寄养预约需要指定房型、开始日期和结束日期");
+            }
+            BoardingRoom room = boardingRoomMapper.selectById(dto.getRoomId());
+            if (room == null || room.getIsActive() == 0) {
+                throw new BusinessException("房型不存在或已停用");
+            }
+            appointment.setRoomId(dto.getRoomId());
+            appointment.setStartDate(dto.getStartDate());
+            appointment.setEndDate(dto.getEndDate());
+
+            long days = ChronoUnit.DAYS.between(dto.getStartDate(), dto.getEndDate());
+            if (days <= 0) {
+                throw new BusinessException("结束日期必须晚于开始日期");
+            }
+
+            List<BoardingRoomStatus> statusList = boardingRoomStatusMapper.selectByRoomAndDateRange(
+                    dto.getRoomId(), dto.getStartDate(), dto.getEndDate().minusDays(1));
+            for (BoardingRoomStatus s : statusList) {
+                if (!"AVAILABLE".equals(s.getStatus())) {
+                    throw new BusinessException("所选日期范围内房型不可用");
+                }
+            }
+
+            BigDecimal totalPrice = room.getPricePerDay().multiply(BigDecimal.valueOf(days));
+            appointment.setTotalPrice(totalPrice);
+
+        } else {
+            appointment.setStartDate(dto.getStartDate());
+            appointment.setTotalPrice(pkg.getPrice());
+        }
+
+        BigDecimal deposit = appointment.getTotalPrice().multiply(new BigDecimal("0.3"));
+        appointment.setDeposit(deposit);
+
+        appointmentMapper.insert(appointment);
+        return toAppointmentVo(appointment);
+    }
+
+    @Override
+    public List<AppointmentVo> list(Long userId) {
+        return appointmentMapper.selectByUserId(userId).stream()
+                .map(this::toAppointmentVo).collect(Collectors.toList());
+    }
+
+    @Override
+    public AppointmentVo getById(Long id, Long userId) {
+        Appointment appointment = appointmentMapper.selectById(id);
+        if (appointment == null || !appointment.getUserId().equals(userId)) {
+            throw new BusinessException("订单不存在或无权访问");
+        }
+        return toAppointmentVo(appointment);
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public Boolean cancel(Long id, String reason, Long userId) {
+        Appointment appointment = appointmentMapper.selectById(id);
+        if (appointment == null || !appointment.getUserId().equals(userId)) {
+            throw new BusinessException("订单不存在或无权访问");
+        }
+        if (!"PENDING".equals(appointment.getStatus()) && !"CONFIRMED".equals(appointment.getStatus())) {
+            throw new BusinessException("当前订单状态不允许取消");
+        }
+        appointmentMapper.updateCancelReason(id, reason != null ? reason : "用户取消");
+        return true;
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public Boolean payDeposit(Long id, Long userId) {
+        Appointment appointment = appointmentMapper.selectById(id);
+        if (appointment == null || !appointment.getUserId().equals(userId)) {
+            throw new BusinessException("订单不存在或无权访问");
+        }
+        if (appointment.getDepositPaid()) {
+            throw new BusinessException("定金已支付");
+        }
+        if (!"PENDING".equals(appointment.getStatus())) {
+            throw new BusinessException("当前订单状态不允许支付");
+        }
+        appointmentMapper.updateDepositPaid(id, true);
+        return true;
+    }
+
+    @Override
+    public PageVo<AppointmentVo> listAll(QueryByPageParams params) {
+        int offset = (params.getPageNum() - 1) * params.getPageSize();
+        return PageUtil.page(params.getPageNum(), params.getPageSize(),
+                () -> appointmentMapper.selectAllByPage(offset, params.getPageSize(),
+                        params.getStatus(), params.getType()).stream()
+                        .map(this::toAppointmentVo).collect(Collectors.toList()),
+                () -> appointmentMapper.countAll(params.getStatus(), params.getType()));
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public Boolean review(Long id, ReviewDto dto) {
+        Appointment appointment = appointmentMapper.selectById(id);
+        if (appointment == null) {
+            throw new BusinessException("订单不存在");
+        }
+        if (!"PENDING".equals(appointment.getStatus())) {
+            throw new BusinessException("当前订单状态不允许审核");
+        }
+
+        if ("APPROVE".equals(dto.getAction())) {
+            appointmentMapper.updateStatus(id, "CONFIRMED");
+            if ("BOARDING".equals(appointment.getType()) && appointment.getRoomId() != null) {
+                if (appointment.getStartDate() != null && appointment.getEndDate() != null) {
+                    List<LocalDate> dates = appointment.getStartDate().datesUntil(appointment.getEndDate())
+                            .collect(Collectors.toList());
+                    List<BoardingRoomStatus> statusList = dates.stream().map(date -> {
+                        BoardingRoomStatus s = new BoardingRoomStatus();
+                        s.setRoomId(appointment.getRoomId());
+                        s.setDate(date);
+                        s.setStatus("OCCUPIED");
+                        s.setAppointmentId(id);
+                        return s;
+                    }).collect(Collectors.toList());
+                    boardingRoomStatusMapper.insertBatch(statusList);
+                }
+            }
+        } else if ("REJECT".equals(dto.getAction())) {
+            appointment.setStatus("CANCELLED");
+            appointment.setRejectReason(dto.getRejectReason());
+            appointmentMapper.update(appointment);
+        } else {
+            throw new BusinessException("审核操作无效");
+        }
+        return true;
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public Boolean handleCancel(Long id, HandleCancelDto dto) {
+        Appointment appointment = appointmentMapper.selectById(id);
+        if (appointment == null) {
+            throw new BusinessException("订单不存在");
+        }
+        if (!"CANCEL_REQUESTED".equals(appointment.getStatus())) {
+            throw new BusinessException("当前订单状态不允许处理取消");
+        }
+
+        if ("AGREE".equals(dto.getAction())) {
+            appointmentMapper.updateStatus(id, "CANCELLED");
+            if ("BOARDING".equals(appointment.getType())) {
+                boardingRoomStatusMapper.deleteByAppointmentId(id);
+            }
+        } else if ("REJECT".equals(dto.getAction())) {
+            appointmentMapper.updateStatus(id, "CONFIRMED");
+        } else {
+            throw new BusinessException("处理取消操作无效");
+        }
+        return true;
+    }
+
+    private AppointmentVo toAppointmentVo(Appointment a) {
+        AppointmentVo vo = new AppointmentVo();
+        vo.setId(a.getId());
+        vo.setUserId(a.getUserId());
+        vo.setPetId(a.getPetId());
+        vo.setPackageId(a.getPackageId());
+        vo.setRoomId(a.getRoomId());
+        vo.setType(a.getType());
+        vo.setStatus(a.getStatus());
+        vo.setStartDate(a.getStartDate());
+        vo.setEndDate(a.getEndDate());
+        vo.setTotalPrice(a.getTotalPrice());
+        vo.setDeposit(a.getDeposit());
+        vo.setDepositPaid(a.getDepositPaid());
+        vo.setCancelReason(a.getCancelReason());
+        vo.setRejectReason(a.getRejectReason());
+        vo.setRemark(a.getRemark());
+        vo.setCreatedAt(a.getCreatedAt());
+        return vo;
+    }
+}

+ 76 - 0
src/main/java/space/anyi/petServiceManagementSystem/server/service/impl/AuthServiceImpl.java

@@ -0,0 +1,76 @@
+package space.anyi.petServiceManagementSystem.server.service.impl;
+
+import jakarta.servlet.http.HttpServletRequest;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import space.anyi.petServiceManagementSystem.server.common.BusinessException;
+import space.anyi.petServiceManagementSystem.server.config.PasswordConfig;
+import space.anyi.petServiceManagementSystem.server.dto.auth.LoginDto;
+import space.anyi.petServiceManagementSystem.server.dto.auth.RegisterDto;
+import space.anyi.petServiceManagementSystem.server.entity.User;
+import space.anyi.petServiceManagementSystem.server.mapper.UserMapper;
+import space.anyi.petServiceManagementSystem.server.service.AuthService;
+import space.anyi.petServiceManagementSystem.server.vo.user.UserVo;
+
+@Service
+public class AuthServiceImpl implements AuthService {
+
+    @Autowired
+    private UserMapper userMapper;
+
+    @Autowired
+    private PasswordConfig passwordConfig;
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public UserVo register(RegisterDto dto, HttpServletRequest request) {
+        User exist = userMapper.selectByAccount(dto.getAccount());
+        if (exist != null) {
+            throw new BusinessException("账号已存在");
+        }
+
+        User user = new User();
+        user.setAccount(dto.getAccount());
+        user.setPassword(passwordConfig.encrypt(dto.getPassword()));
+        user.setUsername(dto.getUsername());
+        user.setRole("user");
+        user.setEnable(1);
+        userMapper.insert(user);
+
+        UserVo vo = toUserVo(user);
+        request.getSession().setAttribute("user", vo);
+        return vo;
+    }
+
+    @Override
+    public UserVo login(LoginDto dto, HttpServletRequest request) {
+        User user = userMapper.selectByAccount(dto.getAccount());
+        if (user == null) {
+            throw new BusinessException("账号或密码错误");
+        }
+        if (user.getEnable() == 0) {
+            throw new BusinessException("账号已被禁用");
+        }
+        if (!passwordConfig.matches(dto.getPassword(), user.getPassword())) {
+            throw new BusinessException("账号或密码错误");
+        }
+
+        UserVo vo = toUserVo(user);
+        request.getSession().setAttribute("user", vo);
+        return vo;
+    }
+
+    private UserVo toUserVo(User user) {
+        UserVo vo = new UserVo();
+        vo.setId(user.getId());
+        vo.setUsername(user.getUsername());
+        vo.setAccount(user.getAccount());
+        vo.setEmail(user.getEmail());
+        vo.setPhone(user.getPhone());
+        vo.setAvatar(user.getAvatar());
+        vo.setRole(user.getRole());
+        vo.setEnable(user.getEnable());
+        return vo;
+    }
+}

+ 87 - 0
src/main/java/space/anyi/petServiceManagementSystem/server/service/impl/BoardingRoomServiceImpl.java

@@ -0,0 +1,87 @@
+package space.anyi.petServiceManagementSystem.server.service.impl;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import space.anyi.petServiceManagementSystem.server.dto.room.RoomStatusDto;
+import space.anyi.petServiceManagementSystem.server.entity.BoardingRoom;
+import space.anyi.petServiceManagementSystem.server.entity.BoardingRoomStatus;
+import space.anyi.petServiceManagementSystem.server.mapper.BoardingRoomMapper;
+import space.anyi.petServiceManagementSystem.server.mapper.BoardingRoomStatusMapper;
+import space.anyi.petServiceManagementSystem.server.service.BoardingRoomService;
+import space.anyi.petServiceManagementSystem.server.vo.room.BoardingRoomVo;
+import space.anyi.petServiceManagementSystem.server.vo.room.RoomStatusVo;
+
+import java.time.LocalDate;
+import java.time.YearMonth;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+@Service
+public class BoardingRoomServiceImpl implements BoardingRoomService {
+
+    @Autowired
+    private BoardingRoomMapper boardingRoomMapper;
+
+    @Autowired
+    private BoardingRoomStatusMapper boardingRoomStatusMapper;
+
+    @Override
+    public List<BoardingRoomVo> listRooms() {
+        return boardingRoomMapper.selectActive().stream()
+                .map(this::toBoardingRoomVo).collect(Collectors.toList());
+    }
+
+    @Override
+    public List<RoomStatusVo> getStatus(Long roomId, String month) {
+        YearMonth yearMonth = YearMonth.parse(month);
+        LocalDate startDate = yearMonth.atDay(1);
+        LocalDate endDate = yearMonth.atEndOfMonth();
+
+        List<BoardingRoomStatus> statusList = boardingRoomStatusMapper
+                .selectByRoomAndDateRange(roomId, startDate, endDate);
+        Map<LocalDate, BoardingRoomStatus> statusMap = statusList.stream()
+                .collect(Collectors.toMap(BoardingRoomStatus::getDate, s -> s));
+
+        List<RoomStatusVo> result = new ArrayList<>();
+        for (int day = 1; day <= yearMonth.lengthOfMonth(); day++) {
+            LocalDate date = yearMonth.atDay(day);
+            RoomStatusVo vo = new RoomStatusVo();
+            vo.setRoomId(roomId);
+            vo.setDate(date);
+            if (statusMap.containsKey(date)) {
+                BoardingRoomStatus s = statusMap.get(date);
+                vo.setId(s.getId());
+                vo.setStatus(s.getStatus());
+                vo.setAppointmentId(s.getAppointmentId());
+            } else {
+                vo.setStatus("AVAILABLE");
+            }
+            result.add(vo);
+        }
+        return result;
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public Boolean updateStatus(Long roomId, RoomStatusDto dto) {
+        BoardingRoomStatus status = new BoardingRoomStatus();
+        status.setRoomId(roomId);
+        status.setDate(dto.getDate());
+        status.setStatus(dto.getStatus());
+        return boardingRoomStatusMapper.insertOrUpdate(status) > 0;
+    }
+
+    private BoardingRoomVo toBoardingRoomVo(BoardingRoom room) {
+        BoardingRoomVo vo = new BoardingRoomVo();
+        vo.setId(room.getId());
+        vo.setRoomName(room.getRoomName());
+        vo.setRoomType(room.getRoomType());
+        vo.setCapacity(room.getCapacity());
+        vo.setPricePerDay(room.getPricePerDay());
+        vo.setDescription(room.getDescription());
+        return vo;
+    }
+}

+ 84 - 0
src/main/java/space/anyi/petServiceManagementSystem/server/service/impl/PackageServiceImpl.java

@@ -0,0 +1,84 @@
+package space.anyi.petServiceManagementSystem.server.service.impl;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import space.anyi.petServiceManagementSystem.server.common.BusinessException;
+import space.anyi.petServiceManagementSystem.server.dto.servicePackage.PackageDto;
+import space.anyi.petServiceManagementSystem.server.entity.ServicePackage;
+import space.anyi.petServiceManagementSystem.server.mapper.PackageMapper;
+import space.anyi.petServiceManagementSystem.server.service.PackageService;
+import space.anyi.petServiceManagementSystem.server.vo.servicePackage.PackageVo;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+@Service
+public class PackageServiceImpl implements PackageService {
+
+    @Autowired
+    private PackageMapper packageMapper;
+
+    @Override
+    public List<PackageVo> list(String type) {
+        return packageMapper.selectByType(type).stream()
+                .map(this::toPackageVo).collect(Collectors.toList());
+    }
+
+    @Override
+    public PackageVo getById(Long id) {
+        ServicePackage pkg = packageMapper.selectById(id);
+        return pkg != null ? toPackageVo(pkg) : null;
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public PackageVo create(PackageDto dto) {
+        if (dto.getIsActive() == null) {
+            dto.setIsActive(1);
+        }
+        ServicePackage pkg = new ServicePackage();
+        pkg.setName(dto.getName());
+        pkg.setType(dto.getType());
+        pkg.setPrice(dto.getPrice());
+        pkg.setDuration(dto.getDuration());
+        pkg.setDescription(dto.getDescription());
+        pkg.setIsActive(dto.getIsActive());
+        packageMapper.insert(pkg);
+        return toPackageVo(pkg);
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public Boolean update(Long id, PackageDto dto) {
+        ServicePackage pkg = packageMapper.selectById(id);
+        if (pkg == null) {
+            throw new BusinessException("套餐不存在");
+        }
+        pkg.setName(dto.getName());
+        pkg.setType(dto.getType());
+        pkg.setPrice(dto.getPrice());
+        pkg.setDuration(dto.getDuration());
+        pkg.setDescription(dto.getDescription());
+        pkg.setIsActive(dto.getIsActive());
+        return packageMapper.update(pkg) > 0;
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public Boolean delete(Long id) {
+        return packageMapper.deleteById(id) > 0;
+    }
+
+    private PackageVo toPackageVo(ServicePackage pkg) {
+        PackageVo vo = new PackageVo();
+        vo.setId(pkg.getId());
+        vo.setName(pkg.getName());
+        vo.setType(pkg.getType());
+        vo.setPrice(pkg.getPrice());
+        vo.setDuration(pkg.getDuration());
+        vo.setDescription(pkg.getDescription());
+        vo.setCreatedAt(pkg.getCreatedAt());
+        return vo;
+    }
+}

+ 190 - 0
src/main/java/space/anyi/petServiceManagementSystem/server/service/impl/PetServiceImpl.java

@@ -0,0 +1,190 @@
+package space.anyi.petServiceManagementSystem.server.service.impl;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import space.anyi.petServiceManagementSystem.server.common.BusinessException;
+import space.anyi.petServiceManagementSystem.server.dto.pet.DewormingDto;
+import space.anyi.petServiceManagementSystem.server.dto.pet.PetDto;
+import space.anyi.petServiceManagementSystem.server.dto.pet.VaccinationDto;
+import space.anyi.petServiceManagementSystem.server.entity.DewormingRecord;
+import space.anyi.petServiceManagementSystem.server.entity.Pet;
+import space.anyi.petServiceManagementSystem.server.entity.VaccinationRecord;
+import space.anyi.petServiceManagementSystem.server.mapper.DewormingRecordMapper;
+import space.anyi.petServiceManagementSystem.server.mapper.PetMapper;
+import space.anyi.petServiceManagementSystem.server.mapper.VaccinationRecordMapper;
+import space.anyi.petServiceManagementSystem.server.service.PetService;
+import space.anyi.petServiceManagementSystem.server.vo.pet.DewormingVo;
+import space.anyi.petServiceManagementSystem.server.vo.pet.PetVo;
+import space.anyi.petServiceManagementSystem.server.vo.pet.VaccinationVo;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+@Service
+public class PetServiceImpl implements PetService {
+
+    @Autowired
+    private PetMapper petMapper;
+
+    @Autowired
+    private VaccinationRecordMapper vaccinationRecordMapper;
+
+    @Autowired
+    private DewormingRecordMapper dewormingRecordMapper;
+
+    @Override
+    public List<PetVo> list(Long userId) {
+        return petMapper.selectByUserId(userId).stream()
+                .map(this::toPetVo).collect(Collectors.toList());
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public PetVo add(PetDto dto, Long userId) {
+        Pet pet = new Pet();
+        pet.setUserId(userId);
+        pet.setName(dto.getName());
+        pet.setSpecies(dto.getSpecies());
+        pet.setBreed(dto.getBreed());
+        pet.setGender(dto.getGender());
+        pet.setBirthDate(dto.getBirthDate());
+        pet.setWeight(dto.getWeight());
+        pet.setAvatar(dto.getAvatar());
+        pet.setRemark(dto.getRemark());
+        petMapper.insert(pet);
+        return toPetVo(pet);
+    }
+
+    @Override
+    public PetVo getById(Long id, Long userId) {
+        Pet pet = petMapper.selectById(id);
+        if (pet == null || !pet.getUserId().equals(userId)) {
+            throw new BusinessException("宠物不存在或无权访问");
+        }
+        return toPetVo(pet);
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public Boolean update(Long id, PetDto dto, Long userId) {
+        Pet pet = petMapper.selectById(id);
+        if (pet == null || !pet.getUserId().equals(userId)) {
+            throw new BusinessException("宠物不存在或无权访问");
+        }
+        pet.setName(dto.getName());
+        pet.setSpecies(dto.getSpecies());
+        pet.setBreed(dto.getBreed());
+        pet.setGender(dto.getGender());
+        pet.setBirthDate(dto.getBirthDate());
+        pet.setWeight(dto.getWeight());
+        pet.setAvatar(dto.getAvatar());
+        pet.setRemark(dto.getRemark());
+        return petMapper.update(pet) > 0;
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public Boolean delete(Long id, Long userId) {
+        Pet pet = petMapper.selectById(id);
+        if (pet == null || !pet.getUserId().equals(userId)) {
+            throw new BusinessException("宠物不存在或无权访问");
+        }
+        vaccinationRecordMapper.deleteByPetId(id);
+        dewormingRecordMapper.deleteByPetId(id);
+        return petMapper.deleteById(id) > 0;
+    }
+
+    @Override
+    public List<VaccinationVo> getVaccinations(Long petId, Long userId) {
+        checkPetOwnership(petId, userId);
+        return vaccinationRecordMapper.selectByPetId(petId).stream()
+                .map(this::toVaccinationVo).collect(Collectors.toList());
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public VaccinationVo addVaccination(Long petId, VaccinationDto dto, Long userId) {
+        checkPetOwnership(petId, userId);
+        VaccinationRecord record = new VaccinationRecord();
+        record.setPetId(petId);
+        record.setVaccineName(dto.getVaccineName());
+        record.setVaccineDate(dto.getVaccineDate());
+        record.setHospital(dto.getHospital());
+        record.setDoctor(dto.getDoctor());
+        record.setRemark(dto.getRemark());
+        vaccinationRecordMapper.insert(record);
+        return toVaccinationVo(record);
+    }
+
+    @Override
+    public List<DewormingVo> getDeworming(Long petId, Long userId) {
+        checkPetOwnership(petId, userId);
+        return dewormingRecordMapper.selectByPetId(petId).stream()
+                .map(this::toDewormingVo).collect(Collectors.toList());
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public DewormingVo addDeworming(Long petId, DewormingDto dto, Long userId) {
+        checkPetOwnership(petId, userId);
+        DewormingRecord record = new DewormingRecord();
+        record.setPetId(petId);
+        record.setDewormingName(dto.getDewormingName());
+        record.setDewormingDate(dto.getDewormingDate());
+        record.setHospital(dto.getHospital());
+        record.setDoctor(dto.getDoctor());
+        record.setRemark(dto.getRemark());
+        dewormingRecordMapper.insert(record);
+        return toDewormingVo(record);
+    }
+
+    private void checkPetOwnership(Long petId, Long userId) {
+        Pet pet = petMapper.selectById(petId);
+        if (pet == null || !pet.getUserId().equals(userId)) {
+            throw new BusinessException("宠物不存在或无权访问");
+        }
+    }
+
+    private PetVo toPetVo(Pet pet) {
+        PetVo vo = new PetVo();
+        vo.setId(pet.getId());
+        vo.setUserId(pet.getUserId());
+        vo.setName(pet.getName());
+        vo.setSpecies(pet.getSpecies());
+        vo.setBreed(pet.getBreed());
+        vo.setGender(pet.getGender());
+        vo.setBirthDate(pet.getBirthDate());
+        vo.setWeight(pet.getWeight());
+        vo.setAvatar(pet.getAvatar());
+        vo.setRemark(pet.getRemark());
+        vo.setCreatedAt(pet.getCreatedAt());
+        return vo;
+    }
+
+    private VaccinationVo toVaccinationVo(VaccinationRecord r) {
+        VaccinationVo vo = new VaccinationVo();
+        vo.setId(r.getId());
+        vo.setPetId(r.getPetId());
+        vo.setVaccineName(r.getVaccineName());
+        vo.setVaccineDate(r.getVaccineDate());
+        vo.setHospital(r.getHospital());
+        vo.setDoctor(r.getDoctor());
+        vo.setRemark(r.getRemark());
+        vo.setCreatedAt(r.getCreatedAt());
+        return vo;
+    }
+
+    private DewormingVo toDewormingVo(DewormingRecord r) {
+        DewormingVo vo = new DewormingVo();
+        vo.setId(r.getId());
+        vo.setPetId(r.getPetId());
+        vo.setDewormingName(r.getDewormingName());
+        vo.setDewormingDate(r.getDewormingDate());
+        vo.setHospital(r.getHospital());
+        vo.setDoctor(r.getDoctor());
+        vo.setRemark(r.getRemark());
+        vo.setCreatedAt(r.getCreatedAt());
+        return vo;
+    }
+}

+ 69 - 0
src/main/java/space/anyi/petServiceManagementSystem/server/service/impl/ScheduleServiceImpl.java

@@ -0,0 +1,69 @@
+package space.anyi.petServiceManagementSystem.server.service.impl;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import space.anyi.petServiceManagementSystem.server.common.BusinessException;
+import space.anyi.petServiceManagementSystem.server.dto.schedule.ScheduleDto;
+import space.anyi.petServiceManagementSystem.server.entity.Schedule;
+import space.anyi.petServiceManagementSystem.server.mapper.ScheduleMapper;
+import space.anyi.petServiceManagementSystem.server.service.ScheduleService;
+import space.anyi.petServiceManagementSystem.server.vo.schedule.ScheduleVo;
+
+import java.time.LocalDate;
+import java.time.YearMonth;
+import java.util.List;
+import java.util.stream.Collectors;
+
+@Service
+public class ScheduleServiceImpl implements ScheduleService {
+
+    @Autowired
+    private ScheduleMapper scheduleMapper;
+
+    @Override
+    public List<ScheduleVo> list(String month) {
+        if (month != null && !month.isEmpty()) {
+            YearMonth yearMonth = YearMonth.parse(month);
+            LocalDate startDate = yearMonth.atDay(1);
+            LocalDate endDate = yearMonth.atEndOfMonth();
+            return scheduleMapper.selectByMonth(startDate, endDate).stream()
+                    .map(this::toScheduleVo).collect(Collectors.toList());
+        }
+        return scheduleMapper.selectByMonth(LocalDate.MIN, LocalDate.MAX).stream()
+                .map(this::toScheduleVo).collect(Collectors.toList());
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public ScheduleVo create(ScheduleDto dto) {
+        int conflict = scheduleMapper.countConflict(dto.getEmployeeName(), dto.getWorkDate(), dto.getTimeSlot());
+        if (conflict > 0) {
+            throw new BusinessException("该员工在此时间段已有排班");
+        }
+        Schedule schedule = new Schedule();
+        schedule.setEmployeeName(dto.getEmployeeName());
+        schedule.setWorkDate(dto.getWorkDate());
+        schedule.setTimeSlot(dto.getTimeSlot());
+        schedule.setRemark(dto.getRemark());
+        scheduleMapper.insert(schedule);
+        return toScheduleVo(schedule);
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public Boolean delete(Long id) {
+        return scheduleMapper.deleteById(id) > 0;
+    }
+
+    private ScheduleVo toScheduleVo(Schedule s) {
+        ScheduleVo vo = new ScheduleVo();
+        vo.setId(s.getId());
+        vo.setEmployeeName(s.getEmployeeName());
+        vo.setWorkDate(s.getWorkDate());
+        vo.setTimeSlot(s.getTimeSlot());
+        vo.setRemark(s.getRemark());
+        vo.setCreatedAt(s.getCreatedAt());
+        return vo;
+    }
+}

+ 212 - 0
src/main/java/space/anyi/petServiceManagementSystem/server/service/impl/UserServiceImpl.java

@@ -0,0 +1,212 @@
+package space.anyi.petServiceManagementSystem.server.service.impl;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import space.anyi.petServiceManagementSystem.server.common.BusinessException;
+import space.anyi.petServiceManagementSystem.server.common.PageVo;
+import space.anyi.petServiceManagementSystem.server.config.PasswordConfig;
+import space.anyi.petServiceManagementSystem.server.dto.room.QueryByPageParams;
+import space.anyi.petServiceManagementSystem.server.dto.user.UpdateUserAvatarDto;
+import space.anyi.petServiceManagementSystem.server.dto.user.UpdateUserPasswordDto;
+import space.anyi.petServiceManagementSystem.server.dto.user.UpdateUserStatusDto;
+import space.anyi.petServiceManagementSystem.server.dto.user.UserDto;
+import space.anyi.petServiceManagementSystem.server.entity.DewormingRecord;
+import space.anyi.petServiceManagementSystem.server.entity.Pet;
+import space.anyi.petServiceManagementSystem.server.entity.User;
+import space.anyi.petServiceManagementSystem.server.entity.VaccinationRecord;
+import space.anyi.petServiceManagementSystem.server.mapper.UserMapper;
+import space.anyi.petServiceManagementSystem.server.service.UserService;
+import space.anyi.petServiceManagementSystem.server.mapper.DewormingRecordMapper;
+import space.anyi.petServiceManagementSystem.server.mapper.PetMapper;
+import space.anyi.petServiceManagementSystem.server.mapper.VaccinationRecordMapper;
+import space.anyi.petServiceManagementSystem.server.util.PageUtil;
+import space.anyi.petServiceManagementSystem.server.vo.pet.DewormingVo;
+import space.anyi.petServiceManagementSystem.server.vo.pet.PetVo;
+import space.anyi.petServiceManagementSystem.server.vo.pet.VaccinationVo;
+import space.anyi.petServiceManagementSystem.server.vo.user.AdminUserDetailVo;
+import space.anyi.petServiceManagementSystem.server.vo.user.UserVo;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+@Service
+public class UserServiceImpl implements UserService {
+
+    @Autowired
+    private UserMapper userMapper;
+
+    @Autowired
+    private PasswordConfig passwordConfig;
+
+    @Autowired
+    private PetMapper petMapper;
+
+    @Autowired
+    private VaccinationRecordMapper vaccinationRecordMapper;
+
+    @Autowired
+    private DewormingRecordMapper dewormingRecordMapper;
+
+    @Override
+    public PageVo<UserVo> queryByPage(QueryByPageParams params) {
+        int offset = (params.getPageNum() - 1) * params.getPageSize();
+        return PageUtil.page(params.getPageNum(), params.getPageSize(),
+                () -> userMapper.selectByPage(offset, params.getPageSize(),
+                        params.getUsername(), params.getAccount(),
+                        params.getRole(), params.getEnable())
+                        .stream().map(this::toUserVo).collect(Collectors.toList()),
+                () -> userMapper.countByCondition(params.getUsername(), params.getAccount(),
+                        params.getRole(), params.getEnable()));
+    }
+
+    @Override
+    public UserVo queryById(Long id) {
+        User user = userMapper.selectById(id);
+        return user != null ? toUserVo(user) : null;
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public Boolean add(UserDto dto) {
+        User exist = userMapper.selectByAccount(dto.getAccount());
+        if (exist != null) {
+            throw new BusinessException("账号已存在");
+        }
+        User user = new User();
+        user.setAccount(dto.getAccount());
+        user.setUsername(dto.getUsername());
+        user.setPassword(passwordConfig.encrypt(dto.getPassword()));
+        user.setEmail(dto.getEmail());
+        user.setPhone(dto.getPhone());
+        user.setAvatar(dto.getAvatar());
+        user.setRole(dto.getRole() != null ? dto.getRole() : "user");
+        user.setEnable(dto.getEnable() != null ? dto.getEnable() : 1);
+        return userMapper.insert(user) > 0;
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public Boolean edit(UserDto dto) {
+        User user = new User();
+        user.setId(dto.getId());
+        user.setUsername(dto.getUsername());
+        if (dto.getPassword() != null && !dto.getPassword().isEmpty()) {
+            user.setPassword(passwordConfig.encrypt(dto.getPassword()));
+        }
+        user.setEmail(dto.getEmail());
+        user.setPhone(dto.getPhone());
+        user.setAvatar(dto.getAvatar());
+        user.setRole(dto.getRole());
+        user.setEnable(dto.getEnable());
+        return userMapper.update(user) > 0;
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public Boolean deleteByIds(List<Long> ids) {
+        return userMapper.deleteByIds(ids) > 0;
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public void updateStatus(UpdateUserStatusDto dto) {
+        userMapper.updateStatus(dto.getId(), dto.getEnable());
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public void updatePassword(UpdateUserPasswordDto dto) {
+        User user = userMapper.selectById(dto.getId());
+        if (user == null) {
+            throw new BusinessException("用户不存在");
+        }
+        if (!passwordConfig.matches(dto.getOldPassword(), user.getPassword())) {
+            throw new BusinessException("旧密码错误");
+        }
+        userMapper.updatePassword(dto.getId(), passwordConfig.encrypt(dto.getNewPassword()));
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public void updateAvatar(UpdateUserAvatarDto dto) {
+        userMapper.updateAvatar(dto.getId(), dto.getAvatar());
+    }
+
+    @Override
+    public AdminUserDetailVo getAdminDetail(Long id) {
+        User user = userMapper.selectById(id);
+        if (user == null) {
+            throw new BusinessException("用户不存在");
+        }
+        List<Pet> pets = petMapper.selectByUserId(id);
+        List<PetVo> petVos = pets.stream().map(pet -> {
+            PetVo petVo = toPetVo(pet);
+            petVo.setVaccinations(vaccinationRecordMapper.selectByPetId(pet.getId()).stream()
+                    .map(this::toVaccinationVo).collect(Collectors.toList()));
+            petVo.setDewormings(dewormingRecordMapper.selectByPetId(pet.getId()).stream()
+                    .map(this::toDewormingVo).collect(Collectors.toList()));
+            return petVo;
+        }).collect(Collectors.toList());
+
+        AdminUserDetailVo detail = new AdminUserDetailVo();
+        detail.setUser(toUserVo(user));
+        detail.setPets(petVos);
+        return detail;
+    }
+
+    private PetVo toPetVo(Pet pet) {
+        PetVo vo = new PetVo();
+        vo.setId(pet.getId());
+        vo.setUserId(pet.getUserId());
+        vo.setName(pet.getName());
+        vo.setSpecies(pet.getSpecies());
+        vo.setBreed(pet.getBreed());
+        vo.setGender(pet.getGender());
+        vo.setBirthDate(pet.getBirthDate());
+        vo.setWeight(pet.getWeight());
+        vo.setAvatar(pet.getAvatar());
+        vo.setRemark(pet.getRemark());
+        vo.setCreatedAt(pet.getCreatedAt());
+        return vo;
+    }
+
+    private VaccinationVo toVaccinationVo(VaccinationRecord r) {
+        VaccinationVo vo = new VaccinationVo();
+        vo.setId(r.getId());
+        vo.setPetId(r.getPetId());
+        vo.setVaccineName(r.getVaccineName());
+        vo.setVaccineDate(r.getVaccineDate());
+        vo.setHospital(r.getHospital());
+        vo.setDoctor(r.getDoctor());
+        vo.setRemark(r.getRemark());
+        vo.setCreatedAt(r.getCreatedAt());
+        return vo;
+    }
+
+    private DewormingVo toDewormingVo(DewormingRecord r) {
+        DewormingVo vo = new DewormingVo();
+        vo.setId(r.getId());
+        vo.setPetId(r.getPetId());
+        vo.setDewormingName(r.getDewormingName());
+        vo.setDewormingDate(r.getDewormingDate());
+        vo.setHospital(r.getHospital());
+        vo.setDoctor(r.getDoctor());
+        vo.setRemark(r.getRemark());
+        vo.setCreatedAt(r.getCreatedAt());
+        return vo;
+    }
+
+    private UserVo toUserVo(User user) {
+        UserVo vo = new UserVo();
+        vo.setId(user.getId());
+        vo.setUsername(user.getUsername());
+        vo.setAccount(user.getAccount());
+        vo.setEmail(user.getEmail());
+        vo.setPhone(user.getPhone());
+        vo.setAvatar(user.getAvatar());
+        vo.setRole(user.getRole());
+        vo.setEnable(user.getEnable());
+        return vo;
+    }
+}

+ 24 - 0
src/main/java/space/anyi/petServiceManagementSystem/server/util/PageUtil.java

@@ -0,0 +1,24 @@
+package space.anyi.petServiceManagementSystem.server.util;
+
+import space.anyi.petServiceManagementSystem.server.common.PageVo;
+
+import java.util.List;
+import java.util.function.Supplier;
+
+public class PageUtil {
+    public static <T> PageVo<T> page(
+            int pageNum, int pageSize,
+            Supplier<List<T>> queryList,
+            Supplier<Long> queryCount
+    ) {
+        List<T> list = queryList.get();
+        long total = queryCount.get();
+
+        PageVo<T> pageVo = new PageVo<>();
+        pageVo.setData(list);
+        pageVo.setTotal(total);
+        pageVo.setPageNum(pageNum);
+        pageVo.setPageSize(pageSize);
+        return pageVo;
+    }
+}

+ 66 - 0
src/main/java/space/anyi/petServiceManagementSystem/server/vo/appointment/AppointmentVo.java

@@ -0,0 +1,66 @@
+package space.anyi.petServiceManagementSystem.server.vo.appointment;
+
+import java.math.BigDecimal;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+
+public class AppointmentVo {
+    private Long id;
+    private Long userId;
+    private Long petId;
+    private String petName;
+    private Long packageId;
+    private String packageName;
+    private Long roomId;
+    private String roomName;
+    private String type;
+    private String status;
+    private LocalDate startDate;
+    private LocalDate endDate;
+    private BigDecimal totalPrice;
+    private BigDecimal deposit;
+    private Boolean depositPaid;
+    private String cancelReason;
+    private String rejectReason;
+    private String remark;
+    private LocalDateTime createdAt;
+
+    public Long getId() { return id; }
+    public void setId(Long id) { this.id = id; }
+    public Long getUserId() { return userId; }
+    public void setUserId(Long userId) { this.userId = userId; }
+    public Long getPetId() { return petId; }
+    public void setPetId(Long petId) { this.petId = petId; }
+    public String getPetName() { return petName; }
+    public void setPetName(String petName) { this.petName = petName; }
+    public Long getPackageId() { return packageId; }
+    public void setPackageId(Long packageId) { this.packageId = packageId; }
+    public String getPackageName() { return packageName; }
+    public void setPackageName(String packageName) { this.packageName = packageName; }
+    public Long getRoomId() { return roomId; }
+    public void setRoomId(Long roomId) { this.roomId = roomId; }
+    public String getRoomName() { return roomName; }
+    public void setRoomName(String roomName) { this.roomName = roomName; }
+    public String getType() { return type; }
+    public void setType(String type) { this.type = type; }
+    public String getStatus() { return status; }
+    public void setStatus(String status) { this.status = status; }
+    public LocalDate getStartDate() { return startDate; }
+    public void setStartDate(LocalDate startDate) { this.startDate = startDate; }
+    public LocalDate getEndDate() { return endDate; }
+    public void setEndDate(LocalDate endDate) { this.endDate = endDate; }
+    public BigDecimal getTotalPrice() { return totalPrice; }
+    public void setTotalPrice(BigDecimal totalPrice) { this.totalPrice = totalPrice; }
+    public BigDecimal getDeposit() { return deposit; }
+    public void setDeposit(BigDecimal deposit) { this.deposit = deposit; }
+    public Boolean getDepositPaid() { return depositPaid; }
+    public void setDepositPaid(Boolean depositPaid) { this.depositPaid = depositPaid; }
+    public String getCancelReason() { return cancelReason; }
+    public void setCancelReason(String cancelReason) { this.cancelReason = cancelReason; }
+    public String getRejectReason() { return rejectReason; }
+    public void setRejectReason(String rejectReason) { this.rejectReason = rejectReason; }
+    public String getRemark() { return remark; }
+    public void setRemark(String remark) { this.remark = remark; }
+    public LocalDateTime getCreatedAt() { return createdAt; }
+    public void setCreatedAt(LocalDateTime createdAt) { this.createdAt = createdAt; }
+}

+ 32 - 0
src/main/java/space/anyi/petServiceManagementSystem/server/vo/pet/DewormingVo.java

@@ -0,0 +1,32 @@
+package space.anyi.petServiceManagementSystem.server.vo.pet;
+
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+
+public class DewormingVo {
+    private Long id;
+    private Long petId;
+    private String dewormingName;
+    private LocalDate dewormingDate;
+    private String hospital;
+    private String doctor;
+    private String remark;
+    private LocalDateTime createdAt;
+
+    public Long getId() { return id; }
+    public void setId(Long id) { this.id = id; }
+    public Long getPetId() { return petId; }
+    public void setPetId(Long petId) { this.petId = petId; }
+    public String getDewormingName() { return dewormingName; }
+    public void setDewormingName(String dewormingName) { this.dewormingName = dewormingName; }
+    public LocalDate getDewormingDate() { return dewormingDate; }
+    public void setDewormingDate(LocalDate dewormingDate) { this.dewormingDate = dewormingDate; }
+    public String getHospital() { return hospital; }
+    public void setHospital(String hospital) { this.hospital = hospital; }
+    public String getDoctor() { return doctor; }
+    public void setDoctor(String doctor) { this.doctor = doctor; }
+    public String getRemark() { return remark; }
+    public void setRemark(String remark) { this.remark = remark; }
+    public LocalDateTime getCreatedAt() { return createdAt; }
+    public void setCreatedAt(LocalDateTime createdAt) { this.createdAt = createdAt; }
+}

+ 48 - 0
src/main/java/space/anyi/petServiceManagementSystem/server/vo/pet/PetVo.java

@@ -0,0 +1,48 @@
+package space.anyi.petServiceManagementSystem.server.vo.pet;
+
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.util.List;
+
+public class PetVo {
+    private Long id;
+    private Long userId;
+    private String name;
+    private String species;
+    private String breed;
+    private String gender;
+    private LocalDate birthDate;
+    private Double weight;
+    private String avatar;
+    private String remark;
+    private LocalDateTime createdAt;
+    private List<VaccinationVo> vaccinations;
+    private List<DewormingVo> dewormings;
+
+    public Long getId() { return id; }
+    public void setId(Long id) { this.id = id; }
+    public Long getUserId() { return userId; }
+    public void setUserId(Long userId) { this.userId = userId; }
+    public String getName() { return name; }
+    public void setName(String name) { this.name = name; }
+    public String getSpecies() { return species; }
+    public void setSpecies(String species) { this.species = species; }
+    public String getBreed() { return breed; }
+    public void setBreed(String breed) { this.breed = breed; }
+    public String getGender() { return gender; }
+    public void setGender(String gender) { this.gender = gender; }
+    public LocalDate getBirthDate() { return birthDate; }
+    public void setBirthDate(LocalDate birthDate) { this.birthDate = birthDate; }
+    public Double getWeight() { return weight; }
+    public void setWeight(Double weight) { this.weight = weight; }
+    public String getAvatar() { return avatar; }
+    public void setAvatar(String avatar) { this.avatar = avatar; }
+    public String getRemark() { return remark; }
+    public void setRemark(String remark) { this.remark = remark; }
+    public LocalDateTime getCreatedAt() { return createdAt; }
+    public void setCreatedAt(LocalDateTime createdAt) { this.createdAt = createdAt; }
+    public List<VaccinationVo> getVaccinations() { return vaccinations; }
+    public void setVaccinations(List<VaccinationVo> vaccinations) { this.vaccinations = vaccinations; }
+    public List<DewormingVo> getDewormings() { return dewormings; }
+    public void setDewormings(List<DewormingVo> dewormings) { this.dewormings = dewormings; }
+}

+ 32 - 0
src/main/java/space/anyi/petServiceManagementSystem/server/vo/pet/VaccinationVo.java

@@ -0,0 +1,32 @@
+package space.anyi.petServiceManagementSystem.server.vo.pet;
+
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+
+public class VaccinationVo {
+    private Long id;
+    private Long petId;
+    private String vaccineName;
+    private LocalDate vaccineDate;
+    private String hospital;
+    private String doctor;
+    private String remark;
+    private LocalDateTime createdAt;
+
+    public Long getId() { return id; }
+    public void setId(Long id) { this.id = id; }
+    public Long getPetId() { return petId; }
+    public void setPetId(Long petId) { this.petId = petId; }
+    public String getVaccineName() { return vaccineName; }
+    public void setVaccineName(String vaccineName) { this.vaccineName = vaccineName; }
+    public LocalDate getVaccineDate() { return vaccineDate; }
+    public void setVaccineDate(LocalDate vaccineDate) { this.vaccineDate = vaccineDate; }
+    public String getHospital() { return hospital; }
+    public void setHospital(String hospital) { this.hospital = hospital; }
+    public String getDoctor() { return doctor; }
+    public void setDoctor(String doctor) { this.doctor = doctor; }
+    public String getRemark() { return remark; }
+    public void setRemark(String remark) { this.remark = remark; }
+    public LocalDateTime getCreatedAt() { return createdAt; }
+    public void setCreatedAt(LocalDateTime createdAt) { this.createdAt = createdAt; }
+}

+ 25 - 0
src/main/java/space/anyi/petServiceManagementSystem/server/vo/room/BoardingRoomVo.java

@@ -0,0 +1,25 @@
+package space.anyi.petServiceManagementSystem.server.vo.room;
+
+import java.math.BigDecimal;
+
+public class BoardingRoomVo {
+    private Long id;
+    private String roomName;
+    private String roomType;
+    private Integer capacity;
+    private BigDecimal pricePerDay;
+    private String description;
+
+    public Long getId() { return id; }
+    public void setId(Long id) { this.id = id; }
+    public String getRoomName() { return roomName; }
+    public void setRoomName(String roomName) { this.roomName = roomName; }
+    public String getRoomType() { return roomType; }
+    public void setRoomType(String roomType) { this.roomType = roomType; }
+    public Integer getCapacity() { return capacity; }
+    public void setCapacity(Integer capacity) { this.capacity = capacity; }
+    public BigDecimal getPricePerDay() { return pricePerDay; }
+    public void setPricePerDay(BigDecimal pricePerDay) { this.pricePerDay = pricePerDay; }
+    public String getDescription() { return description; }
+    public void setDescription(String description) { this.description = description; }
+}

+ 22 - 0
src/main/java/space/anyi/petServiceManagementSystem/server/vo/room/RoomStatusVo.java

@@ -0,0 +1,22 @@
+package space.anyi.petServiceManagementSystem.server.vo.room;
+
+import java.time.LocalDate;
+
+public class RoomStatusVo {
+    private Long id;
+    private Long roomId;
+    private LocalDate date;
+    private String status;
+    private Long appointmentId;
+
+    public Long getId() { return id; }
+    public void setId(Long id) { this.id = id; }
+    public Long getRoomId() { return roomId; }
+    public void setRoomId(Long roomId) { this.roomId = roomId; }
+    public LocalDate getDate() { return date; }
+    public void setDate(LocalDate date) { this.date = date; }
+    public String getStatus() { return status; }
+    public void setStatus(String status) { this.status = status; }
+    public Long getAppointmentId() { return appointmentId; }
+    public void setAppointmentId(Long appointmentId) { this.appointmentId = appointmentId; }
+}

+ 26 - 0
src/main/java/space/anyi/petServiceManagementSystem/server/vo/schedule/ScheduleVo.java

@@ -0,0 +1,26 @@
+package space.anyi.petServiceManagementSystem.server.vo.schedule;
+
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+
+public class ScheduleVo {
+    private Long id;
+    private String employeeName;
+    private LocalDate workDate;
+    private String timeSlot;
+    private String remark;
+    private LocalDateTime createdAt;
+
+    public Long getId() { return id; }
+    public void setId(Long id) { this.id = id; }
+    public String getEmployeeName() { return employeeName; }
+    public void setEmployeeName(String employeeName) { this.employeeName = employeeName; }
+    public LocalDate getWorkDate() { return workDate; }
+    public void setWorkDate(LocalDate workDate) { this.workDate = workDate; }
+    public String getTimeSlot() { return timeSlot; }
+    public void setTimeSlot(String timeSlot) { this.timeSlot = timeSlot; }
+    public String getRemark() { return remark; }
+    public void setRemark(String remark) { this.remark = remark; }
+    public LocalDateTime getCreatedAt() { return createdAt; }
+    public void setCreatedAt(LocalDateTime createdAt) { this.createdAt = createdAt; }
+}

+ 29 - 0
src/main/java/space/anyi/petServiceManagementSystem/server/vo/servicePackage/PackageVo.java

@@ -0,0 +1,29 @@
+package space.anyi.petServiceManagementSystem.server.vo.servicePackage;
+
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+
+public class PackageVo {
+    private Long id;
+    private String name;
+    private String type;
+    private BigDecimal price;
+    private Integer duration;
+    private String description;
+    private LocalDateTime createdAt;
+
+    public Long getId() { return id; }
+    public void setId(Long id) { this.id = id; }
+    public String getName() { return name; }
+    public void setName(String name) { this.name = name; }
+    public String getType() { return type; }
+    public void setType(String type) { this.type = type; }
+    public BigDecimal getPrice() { return price; }
+    public void setPrice(BigDecimal price) { this.price = price; }
+    public Integer getDuration() { return duration; }
+    public void setDuration(Integer duration) { this.duration = duration; }
+    public String getDescription() { return description; }
+    public void setDescription(String description) { this.description = description; }
+    public LocalDateTime getCreatedAt() { return createdAt; }
+    public void setCreatedAt(LocalDateTime createdAt) { this.createdAt = createdAt; }
+}

+ 15 - 0
src/main/java/space/anyi/petServiceManagementSystem/server/vo/user/AdminUserDetailVo.java

@@ -0,0 +1,15 @@
+package space.anyi.petServiceManagementSystem.server.vo.user;
+
+import space.anyi.petServiceManagementSystem.server.vo.pet.PetVo;
+
+import java.util.List;
+
+public class AdminUserDetailVo {
+    private UserVo user;
+    private List<PetVo> pets;
+
+    public UserVo getUser() { return user; }
+    public void setUser(UserVo user) { this.user = user; }
+    public List<PetVo> getPets() { return pets; }
+    public void setPets(List<PetVo> pets) { this.pets = pets; }
+}

+ 29 - 0
src/main/java/space/anyi/petServiceManagementSystem/server/vo/user/UserVo.java

@@ -0,0 +1,29 @@
+package space.anyi.petServiceManagementSystem.server.vo.user;
+
+public class UserVo {
+    private Long id;
+    private String username;
+    private String account;
+    private String email;
+    private String phone;
+    private String avatar;
+    private String role;
+    private Integer enable;
+
+    public Long getId() { return id; }
+    public void setId(Long id) { this.id = id; }
+    public String getUsername() { return username; }
+    public void setUsername(String username) { this.username = username; }
+    public String getAccount() { return account; }
+    public void setAccount(String account) { this.account = account; }
+    public String getEmail() { return email; }
+    public void setEmail(String email) { this.email = email; }
+    public String getPhone() { return phone; }
+    public void setPhone(String phone) { this.phone = phone; }
+    public String getAvatar() { return avatar; }
+    public void setAvatar(String avatar) { this.avatar = avatar; }
+    public String getRole() { return role; }
+    public void setRole(String role) { this.role = role; }
+    public Integer getEnable() { return enable; }
+    public void setEnable(Integer enable) { this.enable = enable; }
+}

+ 7 - 2
src/main/resources/application.yaml

@@ -20,6 +20,11 @@ springdoc:
     path: /swagger-ui.html
 
 server:
+  port: 8080
   servlet:
-    context-path: /api
-  port: 8080
+    context-path: /
+    session:
+      timeout: 1800
+      cookie:
+        http-only: true
+        same-site: lax

+ 72 - 0
src/main/resources/mapper/AppointmentMapper.xml

@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="space.anyi.petServiceManagementSystem.server.mapper.AppointmentMapper">
+
+    <insert id="insert" useGeneratedKeys="true" keyProperty="id">
+        INSERT INTO appointment (user_id, pet_id, package_id, room_id, type, status,
+                                  start_date, end_date, total_price, deposit, deposit_paid,
+                                  cancel_reason, reject_reason, remark, created_at, updated_at)
+        VALUES (#{userId}, #{petId}, #{packageId}, #{roomId}, #{type}, #{status},
+                #{startDate}, #{endDate}, #{totalPrice}, #{deposit}, #{depositPaid},
+                #{cancelReason}, #{rejectReason}, #{remark}, NOW(), NOW())
+    </insert>
+
+    <select id="selectById" resultType="Appointment">
+        SELECT * FROM appointment WHERE id = #{id}
+    </select>
+
+    <select id="selectByUserId" resultType="Appointment">
+        SELECT * FROM appointment WHERE user_id = #{userId} ORDER BY created_at DESC
+    </select>
+
+    <select id="selectAllByPage" resultType="Appointment">
+        SELECT * FROM appointment
+        <where>
+            <if test="status != null and status != ''">
+                AND status = #{status}
+            </if>
+            <if test="type != null and type != ''">
+                AND type = #{type}
+            </if>
+        </where>
+        ORDER BY created_at DESC
+        LIMIT #{offset}, #{pageSize}
+    </select>
+
+    <select id="countAll" resultType="long">
+        SELECT COUNT(*) FROM appointment
+        <where>
+            <if test="status != null and status != ''">
+                AND status = #{status}
+            </if>
+            <if test="type != null and type != ''">
+                AND type = #{type}
+            </if>
+        </where>
+    </select>
+
+    <update id="updateStatus">
+        UPDATE appointment SET status = #{status}, updated_at = NOW() WHERE id = #{id}
+    </update>
+
+    <update id="updateDepositPaid">
+        UPDATE appointment SET deposit_paid = #{depositPaid}, updated_at = NOW() WHERE id = #{id}
+    </update>
+
+    <update id="updateCancelReason">
+        UPDATE appointment SET cancel_reason = #{cancelReason}, status = 'CANCEL_REQUESTED', updated_at = NOW() WHERE id = #{id}
+    </update>
+
+    <update id="update">
+        UPDATE appointment
+        <set>
+            <if test="status != null">status = #{status},</if>
+            <if test="depositPaid != null">deposit_paid = #{depositPaid},</if>
+            <if test="cancelReason != null">cancel_reason = #{cancelReason},</if>
+            <if test="rejectReason != null">reject_reason = #{rejectReason},</if>
+            updated_at = NOW()
+        </set>
+        WHERE id = #{id}
+    </update>
+
+</mapper>

+ 32 - 0
src/main/resources/mapper/BoardingRoomMapper.xml

@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="space.anyi.petServiceManagementSystem.server.mapper.BoardingRoomMapper">
+
+    <insert id="insert" useGeneratedKeys="true" keyProperty="id">
+        INSERT INTO boarding_room (room_name, room_type, capacity, price_per_day, is_active, description, created_at, updated_at)
+        VALUES (#{roomName}, #{roomType}, #{capacity}, #{pricePerDay}, #{isActive}, #{description}, NOW(), NOW())
+    </insert>
+
+    <select id="selectById" resultType="BoardingRoom">
+        SELECT * FROM boarding_room WHERE id = #{id}
+    </select>
+
+    <select id="selectActive" resultType="BoardingRoom">
+        SELECT * FROM boarding_room WHERE is_active = 1 ORDER BY created_at ASC
+    </select>
+
+    <update id="update">
+        UPDATE boarding_room
+        <set>
+            <if test="roomName != null">room_name = #{roomName},</if>
+            <if test="roomType != null">room_type = #{roomType},</if>
+            <if test="capacity != null">capacity = #{capacity},</if>
+            <if test="pricePerDay != null">price_per_day = #{pricePerDay},</if>
+            <if test="isActive != null">is_active = #{isActive},</if>
+            <if test="description != null">description = #{description},</if>
+            updated_at = NOW()
+        </set>
+        WHERE id = #{id}
+    </update>
+
+</mapper>

+ 30 - 0
src/main/resources/mapper/BoardingRoomStatusMapper.xml

@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="space.anyi.petServiceManagementSystem.server.mapper.BoardingRoomStatusMapper">
+
+    <insert id="insertOrUpdate">
+        INSERT INTO boarding_room_status (room_id, date, status, appointment_id, created_at)
+        VALUES (#{roomId}, #{date}, #{status}, #{appointmentId}, NOW())
+        ON DUPLICATE KEY UPDATE status = #{status}, appointment_id = #{appointmentId}
+    </insert>
+
+    <select id="selectByRoomAndDateRange" resultType="BoardingRoomStatus">
+        SELECT * FROM boarding_room_status
+        WHERE room_id = #{roomId}
+          AND date BETWEEN #{startDate} AND #{endDate}
+        ORDER BY date ASC
+    </select>
+
+    <delete id="deleteByAppointmentId">
+        DELETE FROM boarding_room_status WHERE appointment_id = #{appointmentId}
+    </delete>
+
+    <insert id="insertBatch">
+        INSERT INTO boarding_room_status (room_id, date, status, appointment_id, created_at)
+        VALUES
+        <foreach item="item" collection="list" separator=",">
+            (#{item.roomId}, #{item.date}, #{item.status}, #{item.appointmentId}, NOW())
+        </foreach>
+    </insert>
+
+</mapper>

+ 22 - 0
src/main/resources/mapper/DewormingRecordMapper.xml

@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="space.anyi.petServiceManagementSystem.server.mapper.DewormingRecordMapper">
+
+    <insert id="insert" useGeneratedKeys="true" keyProperty="id">
+        INSERT INTO deworming_record (pet_id, deworming_name, deworming_date, hospital, doctor, remark, created_at)
+        VALUES (#{petId}, #{dewormingName}, #{dewormingDate}, #{hospital}, #{doctor}, #{remark}, NOW())
+    </insert>
+
+    <select id="selectByPetId" resultType="DewormingRecord">
+        SELECT * FROM deworming_record WHERE pet_id = #{petId} ORDER BY deworming_date DESC
+    </select>
+
+    <delete id="deleteByPetId">
+        DELETE FROM deworming_record WHERE pet_id = #{petId}
+    </delete>
+
+    <delete id="deleteById">
+        DELETE FROM deworming_record WHERE id = #{id}
+    </delete>
+
+</mapper>

+ 43 - 0
src/main/resources/mapper/PackageMapper.xml

@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="space.anyi.petServiceManagementSystem.server.mapper.PackageMapper">
+
+    <insert id="insert" useGeneratedKeys="true" keyProperty="id">
+        INSERT INTO package (name, type, price, duration, description, is_active, created_at, updated_at)
+        VALUES (#{name}, #{type}, #{price}, #{duration}, #{description}, #{isActive}, NOW(), NOW())
+    </insert>
+
+    <select id="selectById" resultType="ServicePackage">
+        SELECT * FROM package WHERE id = #{id}
+    </select>
+
+    <select id="selectByType" resultType="ServicePackage">
+        SELECT * FROM package
+        <where>
+            is_active = 1
+            <if test="type != null and type != ''">
+                AND type = #{type}
+            </if>
+        </where>
+        ORDER BY created_at DESC
+    </select>
+
+    <update id="update">
+        UPDATE package
+        <set>
+            <if test="name != null">name = #{name},</if>
+            <if test="type != null">type = #{type},</if>
+            <if test="price != null">price = #{price},</if>
+            <if test="duration != null">duration = #{duration},</if>
+            <if test="description != null">description = #{description},</if>
+            <if test="isActive != null">is_active = #{isActive},</if>
+            updated_at = NOW()
+        </set>
+        WHERE id = #{id}
+    </update>
+
+    <update id="deleteById">
+        UPDATE package SET is_active = 0, updated_at = NOW() WHERE id = #{id}
+    </update>
+
+</mapper>

+ 38 - 0
src/main/resources/mapper/PetMapper.xml

@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="space.anyi.petServiceManagementSystem.server.mapper.PetMapper">
+
+    <insert id="insert" useGeneratedKeys="true" keyProperty="id">
+        INSERT INTO pet (user_id, name, species, breed, gender, birth_date, weight, avatar, remark, created_at, updated_at)
+        VALUES (#{userId}, #{name}, #{species}, #{breed}, #{gender}, #{birthDate}, #{weight}, #{avatar}, #{remark}, NOW(), NOW())
+    </insert>
+
+    <select id="selectById" resultType="Pet">
+        SELECT * FROM pet WHERE id = #{id}
+    </select>
+
+    <select id="selectByUserId" resultType="Pet">
+        SELECT * FROM pet WHERE user_id = #{userId} ORDER BY created_at DESC
+    </select>
+
+    <update id="update">
+        UPDATE pet
+        <set>
+            <if test="name != null">name = #{name},</if>
+            <if test="species != null">species = #{species},</if>
+            <if test="breed != null">breed = #{breed},</if>
+            <if test="gender != null">gender = #{gender},</if>
+            <if test="birthDate != null">birth_date = #{birthDate},</if>
+            <if test="weight != null">weight = #{weight},</if>
+            <if test="avatar != null">avatar = #{avatar},</if>
+            <if test="remark != null">remark = #{remark},</if>
+            updated_at = NOW()
+        </set>
+        WHERE id = #{id}
+    </update>
+
+    <delete id="deleteById">
+        DELETE FROM pet WHERE id = #{id}
+    </delete>
+
+</mapper>

+ 27 - 0
src/main/resources/mapper/ScheduleMapper.xml

@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="space.anyi.petServiceManagementSystem.server.mapper.ScheduleMapper">
+
+    <insert id="insert" useGeneratedKeys="true" keyProperty="id">
+        INSERT INTO schedule (employee_name, work_date, time_slot, remark, created_at)
+        VALUES (#{employeeName}, #{workDate}, #{timeSlot}, #{remark}, NOW())
+    </insert>
+
+    <select id="selectByMonth" resultType="Schedule">
+        SELECT * FROM schedule
+        WHERE work_date BETWEEN #{startDate} AND #{endDate}
+        ORDER BY work_date ASC, time_slot ASC
+    </select>
+
+    <delete id="deleteById">
+        DELETE FROM schedule WHERE id = #{id}
+    </delete>
+
+    <select id="countConflict" resultType="int">
+        SELECT COUNT(*) FROM schedule
+        WHERE employee_name = #{employeeName}
+          AND work_date = #{workDate}
+          AND time_slot = #{timeSlot}
+    </select>
+
+</mapper>

+ 90 - 0
src/main/resources/mapper/UserMapper.xml

@@ -0,0 +1,90 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="space.anyi.petServiceManagementSystem.server.mapper.UserMapper">
+
+    <insert id="insert" useGeneratedKeys="true" keyProperty="id">
+        INSERT INTO user (account, username, password, email, phone, avatar, role, enable, created_at, updated_at)
+        VALUES (#{account}, #{username}, #{password}, #{email}, #{phone}, #{avatar}, #{role}, #{enable}, NOW(), NOW())
+    </insert>
+
+    <select id="selectById" resultType="User">
+        SELECT * FROM user WHERE id = #{id}
+    </select>
+
+    <select id="selectByAccount" resultType="User">
+        SELECT * FROM user WHERE account = #{account}
+    </select>
+
+    <update id="update">
+        UPDATE user
+        <set>
+            <if test="username != null">username = #{username},</if>
+            <if test="password != null">password = #{password},</if>
+            <if test="email != null">email = #{email},</if>
+            <if test="phone != null">phone = #{phone},</if>
+            <if test="avatar != null">avatar = #{avatar},</if>
+            <if test="role != null">role = #{role},</if>
+            <if test="enable != null">enable = #{enable},</if>
+            updated_at = NOW()
+        </set>
+        WHERE id = #{id}
+    </update>
+
+    <delete id="deleteByIds">
+        DELETE FROM user WHERE id IN
+        <foreach item="id" collection="ids" open="(" separator="," close=")">
+            #{id}
+        </foreach>
+    </delete>
+
+    <select id="selectByPage" resultType="User">
+        SELECT * FROM user
+        <where>
+            <if test="username != null and username != ''">
+                AND username LIKE CONCAT('%', #{username}, '%')
+            </if>
+            <if test="account != null and account != ''">
+                AND account LIKE CONCAT('%', #{account}, '%')
+            </if>
+            <if test="role != null and role != ''">
+                AND role = #{role}
+            </if>
+            <if test="enable != null">
+                AND enable = #{enable}
+            </if>
+        </where>
+        ORDER BY created_at DESC
+        LIMIT #{offset}, #{pageSize}
+    </select>
+
+    <select id="countByCondition" resultType="long">
+        SELECT COUNT(*) FROM user
+        <where>
+            <if test="username != null and username != ''">
+                AND username LIKE CONCAT('%', #{username}, '%')
+            </if>
+            <if test="account != null and account != ''">
+                AND account LIKE CONCAT('%', #{account}, '%')
+            </if>
+            <if test="role != null and role != ''">
+                AND role = #{role}
+            </if>
+            <if test="enable != null">
+                AND enable = #{enable}
+            </if>
+        </where>
+    </select>
+
+    <update id="updatePassword">
+        UPDATE user SET password = #{password}, updated_at = NOW() WHERE id = #{id}
+    </update>
+
+    <update id="updateStatus">
+        UPDATE user SET enable = #{enable}, updated_at = NOW() WHERE id = #{id}
+    </update>
+
+    <update id="updateAvatar">
+        UPDATE user SET avatar = #{avatar}, updated_at = NOW() WHERE id = #{id}
+    </update>
+
+</mapper>

+ 22 - 0
src/main/resources/mapper/VaccinationRecordMapper.xml

@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="space.anyi.petServiceManagementSystem.server.mapper.VaccinationRecordMapper">
+
+    <insert id="insert" useGeneratedKeys="true" keyProperty="id">
+        INSERT INTO vaccination_record (pet_id, vaccine_name, vaccine_date, hospital, doctor, remark, created_at)
+        VALUES (#{petId}, #{vaccineName}, #{vaccineDate}, #{hospital}, #{doctor}, #{remark}, NOW())
+    </insert>
+
+    <select id="selectByPetId" resultType="VaccinationRecord">
+        SELECT * FROM vaccination_record WHERE pet_id = #{petId} ORDER BY vaccine_date DESC
+    </select>
+
+    <delete id="deleteByPetId">
+        DELETE FROM vaccination_record WHERE pet_id = #{petId}
+    </delete>
+
+    <delete id="deleteById">
+        DELETE FROM vaccination_record WHERE id = #{id}
+    </delete>
+
+</mapper>