基于SpringBoot的美容院业务与数据管理系统 - 源码深度解析

JavaJavaScriptMavenHTMLCSSSSM框架MySQLSpringboot框架
2026-03-204 浏览

文章摘要

本项目是一款基于SpringBoot框架开发的美容院业务与数据管理系统,旨在通过数字化手段解决传统美容院在运营中普遍存在的业务流转依赖手工记录、客户信息分散、服务进度难以跟踪以及经营数据统计效率低下等核心痛点。系统通过将预约、客户档案、服务项目、产品库存及财务流水等核心环节进行一体化整合,显著降低了...

在美容与健康服务行业高速发展的今天,传统美容院的运营管理方式正面临数字化升级的迫切需求。手工记录客户档案、电话预约确认、纸质单据流转不仅效率低下,还极易出现信息错漏,导致客户体验不佳和内部管理混乱。针对这些痛点,我们设计并实现了一套基于SpringBoot框架的“容韵”美容院智能运营平台,该系统将现代软件工程理念与美容院实际业务场景深度融合,实现了从客户接待到经营分析的全流程数字化管理。

系统采用经典的B/S架构,前端使用HTML、CSS和JavaScript构建响应式用户界面,后端以SpringBoot作为核心应用框架,极大简化了项目的初始配置与部署流程。数据持久层采用Spring Data JPA技术,通过对象关系映射(ORM)机制实现了Java实体与MySQL数据库表的无缝对接。Maven作为项目构建和依赖管理工具,确保了第三方库版本的统一管理。这种技术选型既保证了系统的稳定性和可扩展性,又降低了后续的维护成本。

数据库架构设计与核心表分析

系统共设计7张核心数据表,涵盖了会员管理、服务项目、库存产品、护理记录等关键业务领域。数据库设计遵循第三范式,通过外键约束确保数据的完整性和一致性。

会员信息表(member)的设计体现了客户管理的专业性:

CREATE TABLE `member` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(255) NOT NULL COMMENT '会员姓名',
  `gender` varchar(10) DEFAULT NULL COMMENT '性别',
  `age` int(11) DEFAULT NULL COMMENT '年龄',
  `phone` varchar(20) NOT NULL COMMENT '手机号',
  `membership_level` varchar(50) DEFAULT NULL COMMENT '会员等级',
  `balance` decimal(10,2) DEFAULT '0.00' COMMENT '账户余额',
  `total_consumption` decimal(10,2) DEFAULT '0.00' COMMENT '累计消费',
  `last_visit_date` datetime DEFAULT NULL COMMENT '最后到店日期',
  `create_time` datetime DEFAULT CURRENT_TIMESTAMP,
  `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`),
  UNIQUE KEY `uk_phone` (`phone`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='会员信息表';

该表设计具有多个亮点:通过唯一索引确保手机号的唯一性,避免重复注册;包含会员等级和余额字段,支持预付费会员制商业模式;last_visit_date字段为客户关怀和精准营销提供数据支持;create_time和update_time的自动维护机制为操作审计提供了便利。

服务记录表(service_record)的设计展现了业务流转的完整性:

CREATE TABLE `service_record` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `member_id` int(11) NOT NULL COMMENT '会员ID',
  `service_item_id` int(11) NOT NULL COMMENT '服务项目ID',
  `beautician_id` int(11) NOT NULL COMMENT '美容师ID',
  `service_date` datetime NOT NULL COMMENT '服务日期',
  `duration` int(11) DEFAULT NULL COMMENT '服务时长(分钟)',
  `amount` decimal(10,2) NOT NULL COMMENT '服务金额',
  `status` varchar(20) DEFAULT 'completed' COMMENT '服务状态',
  `notes` text COMMENT '服务备注',
  `create_time` datetime DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`),
  KEY `idx_member_id` (`member_id`),
  KEY `idx_service_date` (`service_date`),
  CONSTRAINT `fk_service_member` FOREIGN KEY (`member_id`) REFERENCES `member` (`id`),
  CONSTRAINT `fk_service_item` FOREIGN KEY (`service_item_id`) REFERENCES `service_item` (`id`),
  CONSTRAINT `fk_beautician` FOREIGN KEY (`beautician_id`) REFERENCES `beautician` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='服务记录表';

该表通过外键约束确保了数据的引用完整性,status字段支持服务状态跟踪(如预约、进行中、完成、取消等),duration字段为美容师绩效评估提供依据,复合索引设计优化了按会员和日期范围的查询性能。

核心实体模型与JPA映射

系统采用JPA进行对象关系映射,实体类的设计充分体现了领域驱动设计(DDD)的思想。以下是会员实体(Member)的完整定义:

@Entity
@Table(name = "member")
@Data
@EqualsAndHashCode(callSuper = false)
public class Member extends BaseEntity {
    
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;
    
    @Column(name = "name", nullable = false)
    private String name;
    
    @Column(name = "gender")
    private String gender;
    
    @Column(name = "age")
    private Integer age;
    
    @Column(name = "phone", nullable = false, unique = true)
    private String phone;
    
    @Column(name = "membership_level")
    private String membershipLevel;
    
    @Column(name = "balance", precision = 10, scale = 2)
    private BigDecimal balance = BigDecimal.ZERO;
    
    @Column(name = "total_consumption", precision = 10, scale = 2)
    private BigDecimal totalConsumption = BigDecimal.ZERO;
    
    @Column(name = "last_visit_date")
    private LocalDateTime lastVisitDate;
    
    @OneToMany(mappedBy = "member", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
    private List<ServiceRecord> serviceRecords = new ArrayList<>();
    
    @OneToMany(mappedBy = "member", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
    private List<NursingRecord> nursingRecords = new ArrayList<>();
}

实体类通过注解实现了数据库表字段的精确映射,@OneToMany注解定义了会员与服务记录、护理记录之间的一对多关系,LAZY加载策略优化了查询性能。BaseEntity基类封装了createTime和updateTime等公共字段,体现了代码复用思想。

业务逻辑层设计与核心功能实现

系统业务层采用服务(Service)模式进行组织,每个核心业务领域都有对应的Service类负责处理业务逻辑。以下是预约管理服务的核心实现:

@Service
@Transactional
public class AppointmentService {
    
    @Autowired
    private AppointmentRepository appointmentRepository;
    
    @Autowired
    private MemberRepository memberRepository;
    
    @Autowired
    private BeauticianRepository beauticianRepository;
    
    public Appointment createAppointment(AppointmentDTO appointmentDTO) {
        // 参数验证
        if (appointmentDTO.getAppointmentTime() == null) {
            throw new BusinessException("预约时间不能为空");
        }
        
        // 检查会员是否存在
        Member member = memberRepository.findById(appointmentDTO.getMemberId())
                .orElseThrow(() -> new BusinessException("会员不存在"));
        
        // 检查美容师是否可用
        Beautician beautician = beauticianRepository.findById(appointmentDTO.getBeauticianId())
                .orElseThrow(() -> new BusinessException("美容师不存在"));
        
        // 检查时间冲突
        boolean isConflict = appointmentRepository.existsByBeauticianIdAndAppointmentTime(
                appointmentDTO.getBeauticianId(), appointmentDTO.getAppointmentTime());
        if (isConflict) {
            throw new BusinessException("该时间段美容师已有预约");
        }
        
        // 创建预约记录
        Appointment appointment = new Appointment();
        appointment.setMember(member);
        appointment.setBeautician(beautician);
        appointment.setAppointmentTime(appointmentDTO.getAppointmentTime());
        appointment.setExpectedDuration(appointmentDTO.getExpectedDuration());
        appointment.setStatus(AppointmentStatus.PENDING);
        appointment.setNotes(appointmentDTO.getNotes());
        
        return appointmentRepository.save(appointment);
    }
    
    public void confirmAppointment(Integer appointmentId) {
        Appointment appointment = appointmentRepository.findById(appointmentId)
                .orElseThrow(() -> new BusinessException("预约记录不存在"));
        
        if (appointment.getStatus() != AppointmentStatus.PENDING) {
            throw new BusinessException("只能确认待处理的预约");
        }
        
        appointment.setStatus(AppointmentStatus.CONFIRMED);
        appointment.setUpdateTime(LocalDateTime.now());
        appointmentRepository.save(appointment);
    }
}

该服务类通过@Transactional注解确保业务操作的原子性,实现了完整的预约创建和确认流程。参数验证、业务规则检查(如时间冲突检测)、状态流转控制等关键逻辑都得到了妥善处理。

预约管理界面

库存管理模块采用策略模式实现不同类别的产品管理,以下是产品入库的核心逻辑:

@Service
public class InventoryService {
    
    @Autowired
    private ProductRepository productRepository;
    
    @Autowired
    private StockTransactionRepository stockTransactionRepository;
    
    public void stockIn(StockInDTO stockInDTO) {
        Product product = productRepository.findById(stockInDTO.getProductId())
                .orElseThrow(() -> new BusinessException("产品不存在"));
        
        // 更新库存数量
        int newQuantity = product.getQuantity() + stockInDTO.getQuantity();
        product.setQuantity(newQuantity);
        product.setUpdateTime(LocalDateTime.now());
        
        // 记录库存交易
        StockTransaction transaction = new StockTransaction();
        transaction.setProduct(product);
        transaction.setQuantity(stockInDTO.getQuantity());
        transaction.setTransactionType(TransactionType.INBOUND);
        transaction.setUnitPrice(stockInDTO.getUnitPrice());
        transaction.setTotalAmount(stockInDTO.getUnitPrice().multiply(
                BigDecimal.valueOf(stockInDTO.getQuantity())));
        transaction.setTransactionDate(LocalDateTime.now());
        transaction.setNotes(stockInDTO.getNotes());
        
        productRepository.save(product);
        stockTransactionRepository.save(transaction);
    }
    
    public void stockOut(StockOutDTO stockOutDTO) {
        Product product = productRepository.findById(stockOutDTO.getProductId())
                .orElseThrow(() -> new BusinessException("产品不存在"));
        
        if (product.getQuantity() < stockOutDTO.getQuantity()) {
            throw new BusinessException("库存不足");
        }
        
        // 更新库存数量
        int newQuantity = product.getQuantity() - stockOutDTO.getQuantity();
        product.setQuantity(newQuantity);
        product.setUpdateTime(LocalDateTime.now());
        
        // 记录库存交易
        StockTransaction transaction = new StockTransaction();
        transaction.setProduct(product);
        transaction.setQuantity(stockOutDTO.getQuantity());
        transaction.setTransactionType(TransactionType.OUTBOUND);
        transaction.setUnitPrice(stockOutDTO.getUnitPrice());
        transaction.setTotalAmount(stockOutDTO.getUnitPrice().multiply(
                BigDecimal.valueOf(stockOutDTO.getQuantity())));
        transaction.setTransactionDate(LocalDateTime.now());
        transaction.setNotes(stockOutDTO.getNotes());
        
        productRepository.save(product);
        stockTransactionRepository.save(transaction);
    }
}

产品入库界面

系统通过JPA Specification实现动态查询构建,支持多条件组合检索。以下是会员查询的规格定义:

public class MemberSpecifications {
    
    public static Specification<Member> hasName(String name) {
        return (root, query, criteriaBuilder) -> {
            if (StringUtils.isEmpty(name)) {
                return criteriaBuilder.conjunction();
            }
            return criteriaBuilder.like(root.get("name"), "%" + name + "%");
        };
    }
    
    public static Specification<Member> hasPhone(String phone) {
        return (root, query, criteriaBuilder) -> {
            if (StringUtils.isEmpty(phone)) {
                return criteriaBuilder.conjunction();
            }
            return criteriaBuilder.like(root.get("phone"), "%" + phone + "%");
        };
    }
    
    public static Specification<Member> hasMembershipLevel(String level) {
        return (root, query, criteriaBuilder) -> {
            if (StringUtils.isEmpty(level)) {
                return criteriaBuilder.conjunction();
            }
            return criteriaBuilder.equal(root.get("membershipLevel"), level);
        };
    }
    
    public static Specification<Member> visitedAfter(LocalDateTime date) {
        return (root, query, criteriaBuilder) -> {
            if (date == null) {
                return criteriaBuilder.conjunction();
            }
            return criteriaBuilder.greaterThanOrEqualTo(root.get("lastVisitDate"), date);
        };
    }
    
    public static Specification<Member> combineSpecifications(
            String name, String phone, String level, LocalDateTime visitDate) {
        
        return Specification.where(hasName(name))
                .and(hasPhone(phone))
                .and(hasMembershipLevel(level))
                .and(visitedAfter(visitDate));
    }
}

在控制器层,通过组合不同的规格条件实现灵活的查询功能:

@RestController
@RequestMapping("/api/members")
public class MemberController {
    
    @Autowired
    private MemberService memberService;
    
    @GetMapping
    public Page<Member> getMembers(
            @RequestParam(required = false) String name,
            @RequestParam(required = false) String phone,
            @RequestParam(required = false) String membershipLevel,
            @RequestParam(required = false) @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate visitDate,
            @PageableDefault(sort = "createTime", direction = Direction.DESC) Pageable pageable) {
        
        LocalDateTime visitDateTime = visitDate != null ? 
                visitDate.atStartOfDay() : null;
        
        Specification<Member> spec = MemberSpecifications.combineSpecifications(
                name, phone, membershipLevel, visitDateTime);
        
        return memberService.findAll(spec, pageable);
    }
}

会员列表界面

财务统计模块利用SpringBoot的定时任务机制,自动生成每日业绩报表:

@Component
public class DailyReportScheduler {
    
    @Autowired
    private SalesRecordService salesRecordService;
    
    @Autowired
    private ReportService reportService;
    
    @Scheduled(cron = "0 0 23 * * ?") // 每天23点执行
    public void generateDailyReport() {
        LocalDate reportDate = LocalDate.now().minusDays(1); // 统计前一天的数据
        
        // 获取当日销售数据
        List<SalesRecord> dailySales = salesRecordService.findByDate(reportDate);
        
        // 计算关键指标
        BigDecimal totalRevenue = dailySales.stream()
                .map(SalesRecord::getAmount)
                .reduce(BigDecimal.ZERO, BigDecimal::add);
        
        long customerCount = dailySales.stream()
                .map(SalesRecord::getMemberId)
                .distinct()
                .count();
        
        // 生成报表记录
        DailyReport report = new DailyReport();
        report.setReportDate(reportDate);
        report.setTotalRevenue(totalRevenue);
        report.setCustomerCount((int) customerCount);
        report.setSalesCount(dailySales.size());
        report.setGenerateTime(LocalDateTime.now());
        
        reportService.saveDailyReport(report);
    }
}

销售记录界面

护理记录管理模块支持详细的服务过程跟踪:

@Service
public class NursingRecordService {
    
    @Autowired
    private NursingRecordRepository nursingRecordRepository;
    
    public NursingRecord createNursingRecord(NursingRecordDTO recordDTO) {
        // 验证会员和美容师信息
        Member member = memberRepository.findById(recordDTO.getMemberId())
                .orElseThrow(() -> new BusinessException("会员不存在"));
        
        Beautician beautician = beauticianRepository.findById(recordDTO.getBeauticianId())
                .orElseThrow(() -> new BusinessException("美容师不存在"));
        
        // 创建护理记录
        NursingRecord record = new NursingRecord();
        record.setMember(member);
        record.setBeautician(beautician);
        record.setServiceDate(LocalDateTime.now());
        record.setServiceItems(recordDTO.getServiceItems());
        record.setProductsUsed(recordDTO.getProductsUsed());
        record.setDuration(recordDTO.getDuration());
        record.setAmount(recordDTO.getAmount());
        record.setSkinCondition(recordDTO.getSkinCondition());
        record.setNotes(recordDTO.getNotes());
        record.setBeforePhotos(recordDTO.getBeforePhotos());
        record.setAfterPhotos(recordDTO.getAfterPhotos());
        
        // 更新会员的最后到店时间
        member.setLastVisitDate(LocalDateTime.now());
        memberRepository.save(member);
        
        return nursingRecordRepository.save(record);
    }
}

护理记录界面

系统架构优化与扩展性设计

系统采用分层架构设计,明确划分了表示层、业务逻辑层和数据访问层的职责边界。通过依赖注入(DI)和控制反转(IoC)机制,实现了组件之间的松耦合。以下是对外API接口的统一响应格式设计:

@Data
public class ApiResponse<T> {
    private boolean success;
    private String code;
    private String message;
    private T data;
    private Long timestamp;
    
    public static <T> ApiResponse<T> success(T data) {
        ApiResponse<T> response = new ApiResponse<>();
        response.setSuccess(true);
        response.setCode("200");
        response.setMessage("success");
        response.setData(data);
        response.setTimestamp(System.currentTimeMillis());
        return response;
    }
    
    public static ApiResponse<Object> error(String code, String message) {
        ApiResponse<Object> response = new ApiResponse<>();
        response.setSuccess(false);
        response.setCode(code);
        response.setMessage(message);
        response.setTimestamp(System.currentTimeMillis());
        return response;
    }
}

@ControllerAdvice
public class GlobalExceptionHandler {
    
    @ExceptionHandler(BusinessException.class)
    @ResponseBody
    public ApiResponse<Object> handleBusinessException(BusinessException e) {
        return ApiResponse.error("BUSINESS_ERROR", e.getMessage());
    }
    
    @ExceptionHandler(Exception.class)
    @ResponseBody
    public ApiResponse<Object> handleException(Exception e) {
        return ApiResponse.error("SYSTEM_ERROR", "系统内部错误");
    }
}

未来功能扩展方向

基于当前系统架构和业务需求,可以考虑以下扩展方向:

  1. 移动端应用开发:开发基于React Native或Flutter的移动端应用,支持美容师现场记录服务过程和客户自助预约查询。技术实现上可以基于现有的RESTful API构建,通过JWT令牌实现安全认证。

  2. **

本文关键词
SpringBoot美容院管理系统数据库设计会员管理服务记录

上下篇

上一篇
没有更多文章
下一篇
没有更多文章