feat: 新增宠物管理模块及相关服务更新

This commit is contained in:
MaDaLei 2026-04-14 21:47:24 +08:00
parent b4cbbb6939
commit 5b19922fd3
11 changed files with 263 additions and 3 deletions

View File

@ -67,6 +67,9 @@ 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);

View File

@ -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<String, Object> 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<String, Object> create(@RequestBody Pet pet) {
return petService.create(pet);
}
@PutMapping("/update")
public Map<String, Object> update(@RequestBody Map<String, Object> 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<String, Object> delete(@RequestParam Long id,
@RequestParam Long operatorUserId,
@RequestParam String role) {
return petService.delete(id, operatorUserId, role);
}
}

View File

@ -17,8 +17,9 @@ public class ServiceTypeController {
private final ServiceTypeService serviceTypeService;
@GetMapping("/list")
public Map<String, Object> list(@RequestParam Long storeId) {
List<ServiceType> list = serviceTypeService.getByStoreId(storeId);
public Map<String, Object> list(@RequestParam(required = false) Long storeId) {
List<ServiceType> list =
storeId == null ? serviceTypeService.listSystemDefaultsOnly() : serviceTypeService.getByStoreId(storeId);
return Map.of("code", 200, "data", list);
}

View File

@ -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<String, Object> list() {
List<Store> all = storeService.listAll();
List<Map<String, Object>> rows = all.stream().map(s -> {
Map<String, Object> 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<String, Object> get(@RequestParam Long id) {
Store store = storeService.findById(id);

View File

@ -26,6 +26,10 @@ public class Appointment {
@Column(name = "user_id")
private Long userId;
/** 关联宠物档案(可选,用于本店「服务过的宠物」统计) */
@Column(name = "pet_id")
private Long petId;
/** 技师ID开始服务时赋值 */
@Column(name = "assigned_user_id")
private Long assignedUserId;

View File

@ -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;
}

View File

@ -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<Pet, Long> {
List<Pet> 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<Pet> findDistinctPetsServedAtStore(@Param("storeId") Long storeId);
}

View File

@ -8,4 +8,7 @@ import java.util.List;
public interface ServiceTypeMapper extends JpaRepository<ServiceType, Long> {
List<ServiceType> findByStoreIdOrStoreIdIsNull(Long storeId);
List<ServiceType> findByStoreId(Long storeId);
/** 系统内置store_id 为空) */
List<ServiceType> findByStoreIdIsNull();
}

View File

@ -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<Pet> listByOwner(Long ownerUserId) {
if (ownerUserId == null) {
return List.of();
}
return petMapper.findByOwnerUserIdOrderByUpdateTimeDesc(ownerUserId);
}
/** 商家/员工:本店预约中关联过的宠物(预约带 pet_id */
public List<Pet> listByStoreServed(Long storeId) {
if (storeId == null) {
return List.of();
}
return petMapper.findDistinctPetsServedAtStore(storeId);
}
public Map<String, Object> 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<String, Object> update(Long operatorUserId, String role, Pet input) {
if (input.getId() == null) {
return Map.of("code", 400, "message", "缺少宠物ID");
}
Optional<Pet> 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<String, Object> delete(Long petId, Long operatorUserId, String role) {
Optional<Pet> 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", "已删除");
}
}

View File

@ -18,6 +18,11 @@ public class ServiceTypeService {
return serviceTypeMapper.findByStoreIdOrStoreIdIsNull(storeId);
}
/** 仅系统默认(未传门店或 C 端尚未绑定门店时用于展示可选名称) */
public List<ServiceType> listSystemDefaultsOnly() {
return serviceTypeMapper.findByStoreIdIsNull();
}
/** 老板新增服务类型 */
public ServiceType create(Long storeId, String name) {
ServiceType st = new ServiceType();

View File

@ -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<Store> listAll() {
return storeMapper.findAll();
}
public Store update(Store store) {
store.setUpdateTime(LocalDateTime.now());
return storeMapper.save(store);