Java全栈开发:宠物医院管理系统项目实战
Java全栈开发:宠物医院管理系统项目实战
项目介绍
本文将介绍一个基于Spring Boot + Vue.js的宠物医院管理系统的开发过程。该系统主要用于帮助宠物医院管理日常运营,包括患者管理、预约挂号、处方开具等功能。
技术栈
后端技术
- Spring Boot 2.7.x
- Spring Security
- MyBatis Plus
- MySQL 8.0
- Redis
- JWT
前端技术
- Vue 3
- Element Plus
- Axios
- Vuex
- Vue Router
核心功能模块
1. 用户管理模块
- 用户注册与登录
- 角色权限管理
- 个人信息维护
2. 宠物档案管理
@Data
@TableName("pet_info")
public class Pet {
@TableId(type = IdType.AUTO)
private Long id;
private String name;
private String species;
private Integer age;
private String gender;
private Long ownerId;
private String medicalHistory;
// ...其他字段
}
3. 预约挂号模块
@Service
public class AppointmentService {
@Autowired
private AppointmentMapper appointmentMapper;
public boolean createAppointment(AppointmentDTO dto) {
// 检查医生排班情况
if(!checkDoctorAvailable(dto.getDoctorId(), dto.getAppointmentTime())) {
throw new BusinessException("医生当前时段已约满");
}
// 创建预约记录
return appointmentMapper.insert(dto) > 0;
}
}
4. 诊疗管理模块
@RestController
@RequestMapping("/api/treatment")
public class TreatmentController {
@PostMapping("/prescription")
public Result createPrescription(@RequestBody PrescriptionDTO dto) {
// 处理处方信息
prescriptionService.create(dto);
return Result.success();
}
}
5. 库存管理模块
@Service
public class InventoryService {
public void updateStock(Long medicineId, Integer quantity) {
// 检查库存
Medicine medicine = medicineMapper.selectById(medicineId);
if(medicine.getStock() < quantity) {
throw new BusinessException("库存不足");
}
// 更新库存
medicine.setStock(medicine.getStock() - quantity);
medicineMapper.updateById(medicine);
}
}
数据库设计
主要数据表
- 用户表(user)
- 宠物信息表(pet_info)
- 预约记录表(appointment)
- 诊疗记录表(treatment)
- 处方表(prescription)
- 药品库存表(medicine_inventory)
系统架构
整体架构
pet-hospital/
├── pet-hospital-backend/ # 后端项目
│ ├── common/ # 公共模块
│ ├── system/ # 系统模块
│ └── business/ # 业务模块
└── pet-hospital-frontend/ # 前端项目
├── src/
├── public/
└── package.json
关键技术要点
1. 权限控制
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/api/auth/**").permitAll()
.anyRequest().authenticated()
.and()
.addFilter(new JwtAuthenticationFilter(authenticationManager()));
}
}
2. 缓存处理
@Service
public class PetService {
@Cacheable(value = "pet", key = "#id")
public PetVO getPetInfo(Long id) {
return petMapper.selectVOById(id);
}
}
3. 异步处理
@Service
public class NotificationService {
@Async
public void sendAppointmentReminder(AppointmentDTO dto) {
// 发送预约提醒
// 可以是短信、邮件等
}
}
部署方案
-
后端服务部署
- 使用Docker容器化部署
- Nginx反向代理
- Redis集群
-
前端部署
- Nginx托管静态资源
- CDN加速
项目亮点
- 采用前后端分离架构,提高开发效率
- 使用Redis缓存提升系统性能
- 实现了完整的权限控制体系
- 支持异步处理大量并发请求
- 良好的异常处理机制
总结
本项目采用主流的Java全栈技术栈,实现了一个功能完整的宠物医院管理系统。通过这个项目,不仅可以学习到完整的全栈开发流程,还能掌握项目开发中的各种最佳实践。
后续优化方向
- 引入微服务架构
- 添加更多的数据分析功能
- 优化系统性能
- 增加移动端适配
- 引入人工智能辅助诊断
宠物医院预约挂号模块详细设计
一、数据库设计
1. 预约表(appointment)
CREATE TABLE `appointment` (
`id` bigint NOT NULL AUTO_INCREMENT,
`pet_id` bigint NOT NULL COMMENT '宠物ID',
`doctor_id` bigint NOT NULL COMMENT '医生ID',
`appointment_time` datetime NOT NULL COMMENT '预约时间',
`status` tinyint NOT NULL DEFAULT '0' COMMENT '状态:0-待确认 1-已确认 2-已完成 3-已取消',
`symptom_desc` varchar(500) COMMENT '症状描述',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
KEY `idx_pet_id` (`pet_id`),
KEY `idx_doctor_id` (`doctor_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
2. 医生排班表(doctor_schedule)
CREATE TABLE `doctor_schedule` (
`id` bigint NOT NULL AUTO_INCREMENT,
`doctor_id` bigint NOT NULL COMMENT '医生ID',
`work_date` date NOT NULL COMMENT '工作日期',
`period` tinyint NOT NULL COMMENT '时段:1-上午 2-下午',
`max_appointments` int NOT NULL COMMENT '最大预约数',
`current_appointments` int NOT NULL DEFAULT '0' COMMENT '当前预约数',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_doctor_date_period` (`doctor_id`,`work_date`,`period`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
二、后端实现
1. 实体类
@Data
@TableName("appointment")
public class Appointment {
@TableId(type = IdType.AUTO)
private Long id;
private Long petId;
private Long doctorId;
private LocalDateTime appointmentTime;
private Integer status;
private String symptomDesc;
private LocalDateTime createTime;
private LocalDateTime updateTime;
}
@Data
@TableName("doctor_schedule")
public class DoctorSchedule {
@TableId(type = IdType.AUTO)
private Long id;
private Long doctorId;
private LocalDate workDate;
private Integer period;
private Integer maxAppointments;
private Integer currentAppointments;
}
2. Service层实现
@Service
@Slf4j
public class AppointmentService {
@Autowired
private AppointmentMapper appointmentMapper;
@Autowired
private DoctorScheduleMapper scheduleMapper;
@Transactional
public Result createAppointment(AppointmentDTO dto) {
// 1. 检查医生排班
DoctorSchedule schedule = checkDoctorSchedule(dto);
if (schedule == null) {
return Result.error("医生当天未排班");
}
// 2. 检查预约数量
if (schedule.getCurrentAppointments() >= schedule.getMaxAppointments()) {
return Result.error("该时段预约已满");
}
// 3. 创建预约记录
Appointment appointment = new Appointment();
BeanUtils.copyProperties(dto, appointment);
appointment.setStatus(AppointmentStatus.PENDING.getCode());
// 4. 更新排班预约数
schedule.setCurrentAppointments(schedule.getCurrentAppointments() + 1);
// 5. 保存数据
try {
appointmentMapper.insert(appointment);
scheduleMapper.updateById(schedule);
// 6. 异步发送通知
sendNotification(appointment);
return Result.success(appointment);
} catch (Exception e) {
log.error("创建预约失败", e);
throw new BusinessException("预约失败,请稍后重试");
}
}
@Async
private void sendNotification(Appointment appointment) {
// 发送短信通知
// 发送微信通知
// 发送邮件通知
}
}
3. Controller层实现
@RestController
@RequestMapping("/api/appointment")
public class AppointmentController {
@Autowired
private AppointmentService appointmentService;
@PostMapping("/create")
public Result create(@RequestBody @Validated AppointmentDTO dto) {
return appointmentService.createAppointment(dto);
}
@GetMapping("/list")
public Result list(@RequestParam Long petId) {
return appointmentService.getAppointmentsByPetId(petId);
}
@PostMapping("/cancel/{id}")
public Result cancel(@PathVariable Long id) {
return appointmentService.cancelAppointment(id);
}
}
三、前端实现
1. 预约表单组件
<template>
<el-form :model="form" :rules="rules" ref="appointmentForm">
<el-form-item label="宠物信息" prop="petId">
<el-select v-model="form.petId" placeholder="请选择宠物">
<el-option
v-for="pet in petList"
:key="pet.id"
:label="pet.name"
:value="pet.id"
/>
</el-select>
</el-form-item>
<el-form-item label="预约日期" prop="appointmentDate">
<el-date-picker
v-model="form.appointmentDate"
type="date"
placeholder="选择日期"
:disabled-date="disabledDate"
@change="handleDateChange"
/>
</el-form-item>
<el-form-item label="预约时段" prop="period">
<el-radio-group v-model="form.period">
<el-radio :label="1">上午</el-radio>
<el-radio :label="2">下午</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="选择医生" prop="doctorId">
<el-select
v-model="form.doctorId"
placeholder="请选择医生"
:loading="doctorLoading"
>
<el-option
v-for="doctor in availableDoctors"
:key="doctor.id"
:label="doctor.name"
:value="doctor.id"
>
<span>{{ doctor.name }}</span>
<span style="float: right; color: #8492a6; font-size: 13px">
剩余号源: {{ doctor.remainingQuota }}
</span>
</el-option>
</el-select>
</el-form-item>
<el-form-item label="症状描述" prop="symptomDesc">
<el-input
type="textarea"
v-model="form.symptomDesc"
:rows="3"
placeholder="请描述宠物症状"
/>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="submitForm">提交预约</el-button>
<el-button @click="resetForm">重置</el-button>
</el-form-item>
</el-form>
</template>
<script>
export default {
data() {
return {
form: {
petId: '',
appointmentDate: '',
period: 1,
doctorId: '',
symptomDesc: ''
},
rules: {
petId: [{ required: true, message: '请选择宠物', trigger: 'change' }],
appointmentDate: [{ required: true, message: '请选择预约日期', trigger: 'change' }],
doctorId: [{ required: true, message: '请选择医生', trigger: 'change' }],
symptomDesc: [{ required: true, message: '请描述症状', trigger: 'blur' }]
},
petList: [],
availableDoctors: [],
doctorLoading: false
}
},
methods: {
async submitForm() {
try {
await this.$refs.appointmentForm.validate()
const res = await this.createAppointment(this.form)
if (res.success) {
this.$message.success('预约成功')
this.resetForm()
}
} catch (error) {
this.$message.error(error.message || '预约失败')
}
}
}
}
</script>
2. 预约列表组件
<template>
<div class="appointment-list">
<el-table :data="appointments" style="width: 100%">
<el-table-column prop="appointmentTime" label="预约时间" />
<el-table-column prop="doctorName" label="医生" />
<el-table-column prop="status" label="状态">
<template #default="scope">
<el-tag :type="getStatusType(scope.row.status)">
{{ getStatusText(scope.row.status) }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="操作">
<template #default="scope">
<el-button
v-if="scope.row.status === 0"
type="danger"
size="small"
@click="cancelAppointment(scope.row.id)"
>
取消预约
</el-button>
</template>
</el-table-column>
</el-table>
</div>
</template>
四、业务流程
- 用户选择预约日期和时段
- 系统查询可预约医生列表
- 用户选择医生并填写症状描述
- 提交预约信息
- 系统校验并创建预约记录
- 发送预约确认通知
五、其他功能
1. 预约提醒
@Component
@Slf4j
public class AppointmentReminder {
@Scheduled(cron = "0 0 20 * * ?") // 每天晚上8点执行
public void sendReminders() {
// 查询明天的预约
List<Appointment> tomorrowAppointments = appointmentMapper.findTomorrowAppointments();
// 发送提醒
for (Appointment appointment : tomorrowAppointments) {
try {
notificationService.sendReminder(appointment);
} catch (Exception e) {
log.error("发送预约提醒失败", e);
}
}
}
}
2. 预约统计
@Service
public class AppointmentStatisticsService {
public Map<String, Object> getStatistics(LocalDate startDate, LocalDate endDate) {
Map<String, Object> statistics = new HashMap<>();
// 总预约数
statistics.put("totalAppointments", appointmentMapper.countByDateRange(startDate, endDate));
// 各状态预约数量
statistics.put("statusDistribution", appointmentMapper.groupByStatus(startDate, endDate));
// 医生预约排名
statistics.put("doctorRanking", appointmentMapper.getDoctorRanking(startDate, endDate));
return statistics;
}
}
六、注意事项
- 并发控制:使用数据库乐观锁或Redis分布式锁
- 事务管理:确保预约创建和排班更新的原子性
- 异常处理:完善的异常处理机制
- 参数校验:前后端都需要进行参数验证
- 性能优化:合理使用缓存,避免频繁查询
七、扩展功能
- 在线支付
- 智能推荐医生
- 预约变更
- 就诊提醒
- 预约评价
这样的预约挂号模块设计,既考虑了基本功能的完整性,又包含了性能优化和业务扩展的考虑,可以满足大多数宠物医院的需求。
宠物医院库存管理模块详细设计
一、数据库设计
1. 药品信息表(medicine)
CREATE TABLE `medicine` (
`id` bigint NOT NULL AUTO_INCREMENT,
`code` varchar(32) NOT NULL COMMENT '药品编码',
`name` varchar(100) NOT NULL COMMENT '药品名称',
`specification` varchar(50) NOT NULL COMMENT '规格',
`unit` varchar(20) NOT NULL COMMENT '单位',
`category_id` bigint NOT NULL COMMENT '分类ID',
`manufacturer` varchar(100) COMMENT '生产厂家',
`shelf_life` int COMMENT '保质期(月)',
`storage_condition` varchar(50) COMMENT '存储条件',
`retail_price` decimal(10,2) NOT NULL COMMENT '零售价',
`purchase_price` decimal(10,2) NOT NULL COMMENT '采购价',
`min_stock` int NOT NULL COMMENT '最低库存',
`max_stock` int NOT NULL COMMENT '最高库存',
`status` tinyint NOT NULL DEFAULT '1' COMMENT '状态:0-停用 1-启用',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_code` (`code`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
2. 库存记录表(inventory)
CREATE TABLE `inventory` (
`id` bigint NOT NULL AUTO_INCREMENT,
`medicine_id` bigint NOT NULL COMMENT '药品ID',
`batch_no` varchar(32) NOT NULL COMMENT '批次号',
`quantity` int NOT NULL COMMENT '数量',
`production_date` date COMMENT '生产日期',
`expiry_date` date COMMENT '有效期',
`warehouse_id` bigint NOT NULL COMMENT '仓库ID',
`position` varchar(50) COMMENT '货架位置',
PRIMARY KEY (`id`),
KEY `idx_medicine_id` (`medicine_id`),
KEY `idx_batch_no` (`batch_no`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
3. 出入库记录表(stock_record)
CREATE TABLE `stock_record` (
`id` bigint NOT NULL AUTO_INCREMENT,
`record_no` varchar(32) NOT NULL COMMENT '单据编号',
`type` tinyint NOT NULL COMMENT '类型:1-入库 2-出库 3-盘点 4-报损',
`medicine_id` bigint NOT NULL COMMENT '药品ID',
`batch_no` varchar(32) NOT NULL COMMENT '批次号',
`quantity` int NOT NULL COMMENT '数量',
`operator_id` bigint NOT NULL COMMENT '操作人ID',
`operation_time` datetime NOT NULL COMMENT '操作时间',
`remark` varchar(200) COMMENT '备注',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_record_no` (`record_no`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
二、后端实现
1. 实体类
@Data
@TableName("medicine")
public class Medicine {
@TableId(type = IdType.AUTO)
private Long id;
private String code;
private String name;
private String specification;
private String unit;
private Long categoryId;
private String manufacturer;
private Integer shelfLife;
private String storageCondition;
private BigDecimal retailPrice;
private BigDecimal purchasePrice;
private Integer minStock;
private Integer maxStock;
private Integer status;
}
@Data
@TableName("inventory")
public class Inventory {
@TableId(type = IdType.AUTO)
private Long id;
private Long medicineId;
private String batchNo;
private Integer quantity;
private LocalDate productionDate;
private LocalDate expiryDate;
private Long warehouseId;
private String position;
}
2. Service层实现
@Service
@Slf4j
public class InventoryService {
@Autowired
private InventoryMapper inventoryMapper;
@Autowired
private StockRecordMapper stockRecordMapper;
@Transactional
public Result inStock(InStockDTO dto) {
// 1. 验证药品信息
Medicine medicine = validateMedicine(dto.getMedicineId());
// 2. 检查批次是否存在
Inventory inventory = inventoryMapper.selectByMedicineAndBatch(
dto.getMedicineId(), dto.getBatchNo());
if (inventory != null) {
// 更新现有库存
inventory.setQuantity(inventory.getQuantity() + dto.getQuantity());
inventoryMapper.updateById(inventory);
} else {
// 创建新库存记录
inventory = new Inventory();
BeanUtils.copyProperties(dto, inventory);
inventoryMapper.insert(inventory);
}
// 3. 创建入库记录
createStockRecord(StockRecordType.IN, dto);
// 4. 检查是否超过最大库存
checkMaxStock(medicine, inventory);
return Result.success();
}
@Transactional
public Result outStock(OutStockDTO dto) {
// 1. 检查库存是否充足
Inventory inventory = checkStock(dto.getMedicineId(), dto.getBatchNo(), dto.getQuantity());
// 2. 扣减库存
inventory.setQuantity(inventory.getQuantity() - dto.getQuantity());
inventoryMapper.updateById(inventory);
// 3. 创建出库记录
createStockRecord(StockRecordType.OUT, dto);
// 4. 检查是否达到最低库存警戒线
checkMinStock(inventory);
return Result.success();
}
@Transactional
public Result stockTaking(StockTakingDTO dto) {
// 盘点处理逻辑
Inventory inventory = inventoryMapper.selectById(dto.getInventoryId());
int difference = dto.getActualQuantity() - inventory.getQuantity();
// 更新库存
inventory.setQuantity(dto.getActualQuantity());
inventoryMapper.updateById(inventory);
// 创建盘点记录
createStockTakingRecord(inventory, difference, dto.getRemark());
return Result.success();
}
}
3. Controller层实现
@RestController
@RequestMapping("/api/inventory")
public class InventoryController {
@Autowired
private InventoryService inventoryService;
@PostMapping("/in")
public Result inStock(@RequestBody @Validated InStockDTO dto) {
return inventoryService.inStock(dto);
}
@PostMapping("/out")
public Result outStock(@RequestBody @Validated OutStockDTO dto) {
return inventoryService.outStock(dto);
}
@GetMapping("/list")
public Result list(InventoryQueryDTO query) {
return inventoryService.queryInventory(query);
}
@PostMapping("/stockTaking")
public Result stockTaking(@RequestBody @Validated StockTakingDTO dto) {
return inventoryService.stockTaking(dto);
}
}
三、前端实现
1. 库存管理主页面
<template>
<div class="inventory-management">
<!-- 搜索栏 -->
<el-form :inline="true" :model="queryForm" class="search-form">
<el-form-item label="药品名称">
<el-input v-model="queryForm.medicineName" placeholder="请输入药品名称" />
</el-form-item>
<el-form-item label="库存状态">
<el-select v-model="queryForm.stockStatus" placeholder="请选择">
<el-option label="正常" value="normal" />
<el-option label="低库存" value="low" />
<el-option label="超储" value="high" />
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="handleQuery">查询</el-button>
<el-button @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
<!-- 操作按钮 -->
<div class="operation-buttons">
<el-button type="primary" @click="handleInStock">入库</el-button>
<el-button type="warning" @click="handleOutStock">出库</el-button>
<el-button type="info" @click="handleStockTaking">盘点</el-button>
</div>
<!-- 库存列表 -->
<el-table :data="inventoryList" border style="width: 100%">
<el-table-column prop="medicineName" label="药品名称" />
<el-table-column prop="specification" label="规格" />
<el-table-column prop="batchNo" label="批次号" />
<el-table-column prop="quantity" label="库存数量" />
<el-table-column prop="expiryDate" label="有效期" />
<el-table-column label="库存状态">
<template #default="scope">
<el-tag :type="getStockStatusType(scope.row)">
{{ getStockStatusText(scope.row) }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="操作" width="200">
<template #default="scope">
<el-button size="small" @click="handleDetail(scope.row)">详情</el-button>
<el-button
size="small"
type="danger"
@click="handleWarn(scope.row)"
v-if="isNearExpiry(scope.row)"
>
临期警告
</el-button>
</template>
</el-table-column>
</el-table>
</div>
</template>
<script>
export default {
data() {
return {
queryForm: {
medicineName: '',
stockStatus: ''
},
inventoryList: []
}
},
methods: {
async handleQuery() {
try {
const res = await this.queryInventory(this.queryForm)
this.inventoryList = res.data
} catch (error) {
this.$message.error('查询失败')
}
},
getStockStatusType(row) {
if (row.quantity <= row.minStock) return 'danger'
if (row.quantity >= row.maxStock) return 'warning'
return 'success'
}
}
}
</script>
2. 入库表单组件
<template>
<el-dialog title="药品入库" v-model="visible">
<el-form :model="form" :rules="rules" ref="inStockForm" label-width="100px">
<el-form-item label="药品" prop="medicineId">
<el-select
v-model="form.medicineId"
filterable
remote
:remote-method="searchMedicine"
placeholder="请选择药品"
>
<el-option
v-for="item in medicineOptions"
:key="item.id"
:label="item.name"
:value="item.id"
/>
</el-select>
</el-form-item>
<el-form-item label="批次号" prop="batchNo">
<el-input v-model="form.batchNo" />
</el-form-item>
<el-form-item label="数量" prop="quantity">
<el-input-number v-model="form.quantity" :min="1" />
</el-form-item>
<el-form-item label="生产日期" prop="productionDate">
<el-date-picker
v-model="form.productionDate"
type="date"
placeholder="选择日期"
/>
</el-form-item>
<el-form-item label="有效期" prop="expiryDate">
<el-date-picker
v-model="form.expiryDate"
type="date"
placeholder="选择日期"
/>
</el-form-item>
</el-form>
<template #footer>
<el-button @click="visible = false">取消</el-button>
<el-button type="primary" @click="submitForm">确认入库</el-button>
</template>
</el-dialog>
</template>
四、业务流程
1. 入库流程
- 验证药品信息
- 检查批次号
- 更新或创建库存记录
- 生成入库单据
- 记录操作日志
2. 出库流程
- 检查库存数量
- 按批次先进先出出库
- 更新库存记录
- 生成出库单据
- 记录操作日志
3. 库存预警
@Component
public class StockWarningTask {
@Scheduled(cron = "0 0 9 * * ?") // 每天早上9点执行
public void checkStockWarning() {
// 检查低库存
List<Inventory> lowStockList = inventoryMapper.findLowStock();
sendLowStockWarning(lowStockList);
// 检查临期商品
List<Inventory> expiringList = inventoryMapper.findExpiring();
sendExpiryWarning(expiringList);
}
}
五、其他功能
1. 库存分析报表
@Service
public class InventoryAnalysisService {
public Map<String, Object> getAnalysisReport() {
Map<String, Object> report = new HashMap<>();
// 库存总值
report.put("totalValue", calculateTotalValue());
// 周转率分析
report.put("turnoverRate", calculateTurnoverRate());
// 库存结构分析
report.put("categoryDistribution", analyzeCategoryDistribution());
// 库存趋势
report.put("stockTrend", analyzeStockTrend());
return report;
}
}
2. 批次追踪
@Service
public class BatchTrackingService {
public List<BatchTrackingVO> trackBatch(String batchNo) {
// 获取入库记录
StockRecord inRecord = stockRecordMapper.findInRecordByBatch(batchNo);
// 获取出库记录
List<StockRecord> outRecords = stockRecordMapper.findOutRecordsByBatch(batchNo);
// 获取使用记录
List<UsageRecord> usageRecords = usageRecordMapper.findByBatch(batchNo);
return buildTrackingChain(inRecord, outRecords, usageRecords);
}
}
六、注意事项
-
数据一致性
- 使用事务确保库存操作的原子性
- 采用乐观锁防止并发更新问题
-
性能优化
- 合理使用索引
- 批量操作优化
- 缓存热点数据
-
安全控制
- 操作权限控制
- 关键操作审计日志
- 数据验证和消毒
-
业务规则
- 先进先出原则
- 库存预警机制
- 有效期管理
七、扩展功能
- 条码管理
- 供应商管理
- 采购计划
- 库存成本核算
- 多仓库管理
八、代码示例:库存变动锁
@Aspect
@Component
public class InventoryLockAspect {
@Autowired
private RedisTemplate redisTemplate;
@Around("@annotation(InventoryLock)")
public Object around(ProceedingJoinPoint point) throws Throwable {
String key = generateLockKey(point);
boolean locked = false;
try {
locked = redisTemplate.opsForValue().setIfAbsent(key, "1", 10, TimeUnit.SECONDS);
if (!locked) {
throw new BusinessException("操作太频繁,请稍后再试");
}
return point.proceed();
} finally {
if (locked) {
redisTemplate.delete(key);
}
}
}
}
这样的库存管理模块设计,既满足了基本的库存管理需求,又具备了良好的扩展性和可维护性。通过合理的数据结构设计和业务流程控制,可以有效保证库存数据的准确性和一致性。