在美容与健康服务行业高速发展的今天,传统美容院的运营管理方式正面临数字化升级的迫切需求。手工记录客户档案、电话预约确认、纸质单据流转不仅效率低下,还极易出现信息错漏,导致客户体验不佳和内部管理混乱。针对这些痛点,我们设计并实现了一套基于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", "系统内部错误");
}
}
未来功能扩展方向
基于当前系统架构和业务需求,可以考虑以下扩展方向:
移动端应用开发:开发基于React Native或Flutter的移动端应用,支持美容师现场记录服务过程和客户自助预约查询。技术实现上可以基于现有的RESTful API构建,通过JWT令牌实现安全认证。
**