From 5b19922fd3936a7b7b019ef35f04e5efa7adf54f Mon Sep 17 00:00:00 2001 From: MaDaLei Date: Tue, 14 Apr 2026 21:47:24 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=96=B0=E5=A2=9E=E5=AE=A0=E7=89=A9?= =?UTF-8?q?=E7=AE=A1=E7=90=86=E6=A8=A1=E5=9D=97=E5=8F=8A=E7=9B=B8=E5=85=B3?= =?UTF-8?q?=E6=9C=8D=E5=8A=A1=E6=9B=B4=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/AppointmentController.java | 5 +- .../petstore/controller/PetController.java | 58 +++++++++++ .../controller/ServiceTypeController.java | 5 +- .../petstore/controller/StoreController.java | 22 +++++ .../java/com/petstore/entity/Appointment.java | 4 + src/main/java/com/petstore/entity/Pet.java | 43 ++++++++ .../java/com/petstore/mapper/PetMapper.java | 17 ++++ .../petstore/mapper/ServiceTypeMapper.java | 3 + .../java/com/petstore/service/PetService.java | 98 +++++++++++++++++++ .../petstore/service/ServiceTypeService.java | 5 + .../com/petstore/service/StoreService.java | 6 ++ 11 files changed, 263 insertions(+), 3 deletions(-) create mode 100644 src/main/java/com/petstore/controller/PetController.java create mode 100644 src/main/java/com/petstore/entity/Pet.java create mode 100644 src/main/java/com/petstore/mapper/PetMapper.java create mode 100644 src/main/java/com/petstore/service/PetService.java diff --git a/src/main/java/com/petstore/controller/AppointmentController.java b/src/main/java/com/petstore/controller/AppointmentController.java index fdcc9a0..1c650e8 100644 --- a/src/main/java/com/petstore/controller/AppointmentController.java +++ b/src/main/java/com/petstore/controller/AppointmentController.java @@ -67,7 +67,10 @@ public class AppointmentController { if (params.containsKey("remark") && params.get("remark") != null) { appointment.setRemark(params.get("remark").toString()); } - + if (params.containsKey("petId") && params.get("petId") != null && !params.get("petId").toString().isBlank()) { + appointment.setPetId(Long.valueOf(params.get("petId").toString())); + } + Appointment created = appointmentService.create(appointment); return Map.of("code", 200, "message", "创建成功", "data", created); } diff --git a/src/main/java/com/petstore/controller/PetController.java b/src/main/java/com/petstore/controller/PetController.java new file mode 100644 index 0000000..05636e4 --- /dev/null +++ b/src/main/java/com/petstore/controller/PetController.java @@ -0,0 +1,58 @@ +package com.petstore.controller; + +import com.petstore.entity.Pet; +import com.petstore.service.PetService; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.*; + +import java.util.HashMap; +import java.util.Map; + +@RestController +@RequestMapping("/api/pet") +@RequiredArgsConstructor +@CrossOrigin +public class PetController { + private final PetService petService; + + /** + * 列表:传 ownerUserId= 查该客户全部宠物;传 storeId= 查本店预约关联出现过的宠物(商家/员工) + */ + @GetMapping("/list") + public Map list(@RequestParam(required = false) Long ownerUserId, + @RequestParam(required = false) Long storeId) { + if (ownerUserId != null) { + return Map.of("code", 200, "data", petService.listByOwner(ownerUserId)); + } + if (storeId != null) { + return Map.of("code", 200, "data", petService.listByStoreServed(storeId)); + } + return Map.of("code", 400, "message", "请传 ownerUserId 或 storeId"); + } + + @PostMapping("/create") + public Map create(@RequestBody Pet pet) { + return petService.create(pet); + } + + @PutMapping("/update") + public Map update(@RequestBody Map body) { + Long operatorUserId = Long.valueOf(body.get("operatorUserId").toString()); + String role = body.get("role").toString(); + Pet input = new Pet(); + input.setId(Long.valueOf(body.get("id").toString())); + if (body.containsKey("name")) input.setName(body.get("name").toString()); + if (body.containsKey("petType")) input.setPetType(body.get("petType").toString()); + if (body.containsKey("breed")) input.setBreed(body.get("breed") != null ? body.get("breed").toString() : null); + if (body.containsKey("avatar")) input.setAvatar(body.get("avatar") != null ? body.get("avatar").toString() : null); + if (body.containsKey("remark")) input.setRemark(body.get("remark") != null ? body.get("remark").toString() : null); + return petService.update(operatorUserId, role, input); + } + + @DeleteMapping("/delete") + public Map delete(@RequestParam Long id, + @RequestParam Long operatorUserId, + @RequestParam String role) { + return petService.delete(id, operatorUserId, role); + } +} diff --git a/src/main/java/com/petstore/controller/ServiceTypeController.java b/src/main/java/com/petstore/controller/ServiceTypeController.java index 2733a62..e08b845 100644 --- a/src/main/java/com/petstore/controller/ServiceTypeController.java +++ b/src/main/java/com/petstore/controller/ServiceTypeController.java @@ -17,8 +17,9 @@ public class ServiceTypeController { private final ServiceTypeService serviceTypeService; @GetMapping("/list") - public Map list(@RequestParam Long storeId) { - List list = serviceTypeService.getByStoreId(storeId); + public Map list(@RequestParam(required = false) Long storeId) { + List list = + storeId == null ? serviceTypeService.listSystemDefaultsOnly() : serviceTypeService.getByStoreId(storeId); return Map.of("code", 200, "data", list); } diff --git a/src/main/java/com/petstore/controller/StoreController.java b/src/main/java/com/petstore/controller/StoreController.java index 5048fd9..f19224b 100644 --- a/src/main/java/com/petstore/controller/StoreController.java +++ b/src/main/java/com/petstore/controller/StoreController.java @@ -6,7 +6,9 @@ import lombok.RequiredArgsConstructor; import org.springframework.web.bind.annotation.*; import java.util.HashMap; +import java.util.List; import java.util.Map; +import java.util.stream.Collectors; @RestController @RequestMapping("/api/store") @@ -25,6 +27,26 @@ public class StoreController { return result; } + /** + * 客户预约:门店列表(不含邀请码等敏感字段) + */ + @GetMapping("/list") + public Map list() { + List all = storeService.listAll(); + List> rows = all.stream().map(s -> { + Map m = new HashMap<>(); + m.put("id", s.getId()); + m.put("name", s.getName()); + m.put("address", s.getAddress()); + m.put("latitude", s.getLatitude()); + m.put("longitude", s.getLongitude()); + m.put("phone", s.getPhone()); + m.put("logo", s.getLogo()); + return m; + }).collect(Collectors.toList()); + return Map.of("code", 200, "data", rows); + } + @GetMapping("/get") public Map get(@RequestParam Long id) { Store store = storeService.findById(id); diff --git a/src/main/java/com/petstore/entity/Appointment.java b/src/main/java/com/petstore/entity/Appointment.java index d0f4b2e..630bc60 100644 --- a/src/main/java/com/petstore/entity/Appointment.java +++ b/src/main/java/com/petstore/entity/Appointment.java @@ -25,6 +25,10 @@ public class Appointment { @Column(name = "user_id") private Long userId; + + /** 关联宠物档案(可选,用于本店「服务过的宠物」统计) */ + @Column(name = "pet_id") + private Long petId; /** 技师ID,开始服务时赋值 */ @Column(name = "assigned_user_id") diff --git a/src/main/java/com/petstore/entity/Pet.java b/src/main/java/com/petstore/entity/Pet.java new file mode 100644 index 0000000..c8e67a0 --- /dev/null +++ b/src/main/java/com/petstore/entity/Pet.java @@ -0,0 +1,43 @@ +package com.petstore.entity; + +import jakarta.persistence.*; +import lombok.Data; + +import java.time.LocalDateTime; + +/** + * 宠物档案(归属某客户;商家/员工通过预约关联查看本店服务过的宠物) + */ +@Data +@Entity +@Table(name = "t_pet") +public class Pet { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + /** 昵称 */ + private String name; + + /** 类型:猫/狗/其他 */ + @Column(name = "pet_type") + private String petType; + + /** 品种(可选) */ + private String breed; + + /** 头像相对路径 */ + private String avatar; + + /** 所属用户(客户) */ + @Column(name = "owner_user_id") + private Long ownerUserId; + + private String remark; + + @Column(name = "create_time") + private LocalDateTime createTime; + + @Column(name = "update_time") + private LocalDateTime updateTime; +} diff --git a/src/main/java/com/petstore/mapper/PetMapper.java b/src/main/java/com/petstore/mapper/PetMapper.java new file mode 100644 index 0000000..1835122 --- /dev/null +++ b/src/main/java/com/petstore/mapper/PetMapper.java @@ -0,0 +1,17 @@ +package com.petstore.mapper; + +import com.petstore.entity.Pet; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; + +import java.util.List; + +public interface PetMapper extends JpaRepository { + + List findByOwnerUserIdOrderByUpdateTimeDesc(Long ownerUserId); + + /** 本店预约中关联过的宠物(预约.pet_id 指向该宠物) */ + @Query("SELECT DISTINCT p FROM Pet p, Appointment a WHERE a.petId = p.id AND a.storeId = :storeId ORDER BY p.updateTime DESC") + List findDistinctPetsServedAtStore(@Param("storeId") Long storeId); +} diff --git a/src/main/java/com/petstore/mapper/ServiceTypeMapper.java b/src/main/java/com/petstore/mapper/ServiceTypeMapper.java index 045d735..af84356 100644 --- a/src/main/java/com/petstore/mapper/ServiceTypeMapper.java +++ b/src/main/java/com/petstore/mapper/ServiceTypeMapper.java @@ -8,4 +8,7 @@ import java.util.List; public interface ServiceTypeMapper extends JpaRepository { List findByStoreIdOrStoreIdIsNull(Long storeId); List findByStoreId(Long storeId); + + /** 系统内置(store_id 为空) */ + List findByStoreIdIsNull(); } diff --git a/src/main/java/com/petstore/service/PetService.java b/src/main/java/com/petstore/service/PetService.java new file mode 100644 index 0000000..3a80bd9 --- /dev/null +++ b/src/main/java/com/petstore/service/PetService.java @@ -0,0 +1,98 @@ +package com.petstore.service; + +import com.petstore.entity.Pet; +import com.petstore.mapper.PetMapper; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +import java.time.LocalDateTime; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +@Service +@RequiredArgsConstructor +public class PetService { + private final PetMapper petMapper; + + /** 客户:仅自己的宠物 */ + public List listByOwner(Long ownerUserId) { + if (ownerUserId == null) { + return List.of(); + } + return petMapper.findByOwnerUserIdOrderByUpdateTimeDesc(ownerUserId); + } + + /** 商家/员工:本店预约中关联过的宠物(预约带 pet_id) */ + public List listByStoreServed(Long storeId) { + if (storeId == null) { + return List.of(); + } + return petMapper.findDistinctPetsServedAtStore(storeId); + } + + public Map create(Pet pet) { + if (pet.getName() == null || pet.getName().isBlank()) { + return Map.of("code", 400, "message", "请填写宠物名字"); + } + if (pet.getPetType() == null || pet.getPetType().isBlank()) { + return Map.of("code", 400, "message", "请选择宠物类型"); + } + if (pet.getOwnerUserId() == null) { + return Map.of("code", 400, "message", "缺少所属用户"); + } + pet.setCreateTime(LocalDateTime.now()); + pet.setUpdateTime(LocalDateTime.now()); + Pet saved = petMapper.save(pet); + return Map.of("code", 200, "message", "保存成功", "data", saved); + } + + public Map update(Long operatorUserId, String role, Pet input) { + if (input.getId() == null) { + return Map.of("code", 400, "message", "缺少宠物ID"); + } + Optional opt = petMapper.findById(input.getId()); + if (opt.isEmpty()) { + return Map.of("code", 404, "message", "宠物不存在"); + } + Pet pet = opt.get(); + if ("customer".equals(role)) { + if (!pet.getOwnerUserId().equals(operatorUserId)) { + return Map.of("code", 403, "message", "无权修改该宠物"); + } + } else { + return Map.of("code", 403, "message", "仅客户可编辑自己的宠物档案"); + } + if (input.getName() != null && !input.getName().isBlank()) { + pet.setName(input.getName()); + } + if (input.getPetType() != null && !input.getPetType().isBlank()) { + pet.setPetType(input.getPetType()); + } + if (input.getBreed() != null) { + pet.setBreed(input.getBreed()); + } + if (input.getAvatar() != null) { + pet.setAvatar(input.getAvatar()); + } + if (input.getRemark() != null) { + pet.setRemark(input.getRemark()); + } + pet.setUpdateTime(LocalDateTime.now()); + petMapper.save(pet); + return Map.of("code", 200, "message", "更新成功", "data", pet); + } + + public Map delete(Long petId, Long operatorUserId, String role) { + Optional opt = petMapper.findById(petId); + if (opt.isEmpty()) { + return Map.of("code", 404, "message", "宠物不存在"); + } + Pet pet = opt.get(); + if (!"customer".equals(role) || !pet.getOwnerUserId().equals(operatorUserId)) { + return Map.of("code", 403, "message", "无权删除"); + } + petMapper.deleteById(petId); + return Map.of("code", 200, "message", "已删除"); + } +} diff --git a/src/main/java/com/petstore/service/ServiceTypeService.java b/src/main/java/com/petstore/service/ServiceTypeService.java index 9953d32..b3bc4a4 100644 --- a/src/main/java/com/petstore/service/ServiceTypeService.java +++ b/src/main/java/com/petstore/service/ServiceTypeService.java @@ -18,6 +18,11 @@ public class ServiceTypeService { return serviceTypeMapper.findByStoreIdOrStoreIdIsNull(storeId); } + /** 仅系统默认(未传门店或 C 端尚未绑定门店时用于展示可选名称) */ + public List listSystemDefaultsOnly() { + return serviceTypeMapper.findByStoreIdIsNull(); + } + /** 老板新增服务类型 */ public ServiceType create(Long storeId, String name) { ServiceType st = new ServiceType(); diff --git a/src/main/java/com/petstore/service/StoreService.java b/src/main/java/com/petstore/service/StoreService.java index e6f31d7..c7938ed 100644 --- a/src/main/java/com/petstore/service/StoreService.java +++ b/src/main/java/com/petstore/service/StoreService.java @@ -6,6 +6,7 @@ import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import java.time.LocalDateTime; +import java.util.List; import java.util.UUID; @Service @@ -28,6 +29,11 @@ public class StoreService { return storeMapper.findByInviteCode(code); } + /** C 端预约:列出全部门店供选择 */ + public List listAll() { + return storeMapper.findAll(); + } + public Store update(Store store) { store.setUpdateTime(LocalDateTime.now()); return storeMapper.save(store);