在传统中药材店铺经营中,手工记账、库存盘点、客户管理这些工作往往耗费大量人力。我见过不少老字号药铺还在用纸质账本记录药材进出,每次盘点库存都要停业半天。这个基于Java技术栈的中药材店铺管理系统,正是为了解决这些痛点而生。
系统采用SpringBoot+SSM框架组合开发,实现了药材进销存、客户管理、财务统计等核心功能模块。相比通用零售系统,特别针对中药材行业特性做了深度定制——比如药材的批次管理、效期预警、属性分类(如"根茎类""果实类")等专业功能。源码提供完整的前后端实现,包含详细的调试文档,二次开发时能少踩很多坑。
SpringBoot的自动配置特性让项目搭建变得极其简单,不用再被XML配置折磨。我实测从零搭建基础环境只需15分钟:
bash复制# 快速创建SpringBoot项目
mvn archetype:generate -DgroupId=com.herbstore -DartifactId=herb-mgr
-DarchetypeArtifactId=spring-boot-starter-parent -Dversion=1.0.0
SSM(Spring+SpringMVC+MyBatis)作为经典组合,在数据持久层处理上表现稳定。MyBatis的灵活SQL编写特别适合中药材这类需要复杂查询的业务场景。例如药材多条件搜索时:
xml复制<!-- 药材多条件查询示例 -->
<select id="selectHerbs" resultType="Herb">
SELECT * FROM t_herb
WHERE
<if test="category != null">category = #{category} AND</if>
<if test="priceMin != null">price >= #{priceMin} AND</if>
<if test="priceMax != null">price <= #{priceMax}</if>
status = 1
</select>
中药材管理有几个特殊字段需要特别注意:
核心表结构设计示例:
sql复制CREATE TABLE `t_herb` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`batch_no` varchar(32) NOT NULL COMMENT '批次号',
`name` varchar(50) NOT NULL COMMENT '药材名称',
`category` tinyint(4) NOT NULL COMMENT '1:根茎类 2:果实类...',
`properties` json DEFAULT NULL COMMENT '性味归经等JSON数据',
`expire_date` datetime NOT NULL COMMENT '失效日期',
PRIMARY KEY (`id`),
KEY `idx_category` (`category`),
KEY `idx_expire` (`expire_date`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
中药材行业对批次管理要求极高,我们实现了双重校验机制:
关键代码片段:
java复制// 批次号生成策略
public String generateBatchNo() {
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
String dateStr = sdf.format(new Date());
String randomStr = RandomStringUtils.randomAlphanumeric(4).toUpperCase();
return dateStr + "-" + randomStr;
}
// 出库批次校验
public void checkBatch(String batchNo) throws BizException {
Herb herb = herbMapper.selectByBatch(batchNo);
if(herb == null || herb.getStock() <= 0){
throw new BizException("批次不存在或库存不足");
}
}
通过Spring的Scheduled注解实现每日凌晨自动扫描:
java复制@Scheduled(cron = "0 0 3 * * ?") // 每天凌晨3点执行
public void checkExpiringHerbs() {
List<Herb> herbs = herbMapper.selectExpiringIn(7); // 7天内将过期
herbs.forEach(herb -> {
String msg = String.format("药材【%s】批次%s将在%s过期",
herb.getName(), herb.getBatchNo(),
DateFormat.getDateInstance().format(herb.getExpireDate()));
alertService.sendToManager(msg);
});
}
重要提示:效期检查一定要用服务器时间而非本地时间,避免时区问题导致预警不准
常见现象:
解决方案:
properties复制# application.properties
spring.servlet.multipart.max-file-size=10MB
spring.servlet.multipart.max-request-size=10MB
java复制public BufferedImage compressImage(BufferedImage src, float quality) {
// 实现图片质量压缩算法
// ...
}
多终端操作时可能出现库存数不准,我们最终采用两种方案:
sql复制UPDATE t_herb_stock
SET quantity = quantity - #{saleNum},
version = version + 1
WHERE herb_id = #{herbId} AND version = #{version}
java复制public boolean deductStock(Long herbId, int num) {
String lockKey = "stock_lock_" + herbId;
try {
// 尝试获取分布式锁
Boolean locked = redisTemplate.opsForValue()
.setIfAbsent(lockKey, "1", 30, TimeUnit.SECONDS);
if(locked != null && locked) {
// 执行库存扣减
return doDeductStock(herbId, num);
}
} finally {
redisTemplate.delete(lockKey);
}
return false;
}
推荐使用Docker Compose部署,下面是我的标准配置:
yaml复制version: '3'
services:
app:
image: openjdk:11-jre
ports:
- "8080:8080"
volumes:
- ./logs:/app/logs
environment:
- SPRING_PROFILES_ACTIVE=prod
mysql:
image: mysql:5.7
environment:
- MYSQL_ROOT_PASSWORD=yourstrongpassword
volumes:
- ./mysql-data:/var/lib/mysql
避坑指南:MySQL一定要挂载数据卷,否则容器重启后数据会丢失
针对药材搜索慢的问题,我们最终采用Elasticsearch实现全文检索:
java复制@Document(indexName = "herbs")
public class HerbIndex {
@Id
private Long id;
@Field(type = FieldType.Text, analyzer = "ik_max_word")
private String name;
// 其他字段...
}
java复制public Page<Herb> search(String keyword, Pageable pageable) {
NativeSearchQuery query = new NativeSearchQueryBuilder()
.withQuery(QueryBuilders.multiMatchQuery(keyword, "name","description"))
.withPageable(pageable)
.build();
return elasticsearchTemplate.queryForPage(query, Herb.class);
}
系统预留了几个重要的扩展点:
以微信支付为例,对接时注意:
java复制@RestController
@RequestMapping("/api/pay")
public class PayController {
@PostMapping("/wx/callback")
public String wxPayCallback(@RequestBody String notifyData) {
// 1. 验证签名
// 2. 处理支付结果
// 3. 更新订单状态
return "<xml><return_code>SUCCESS</return_code></xml>";
}
}
在项目实际落地过程中,最大的体会是一定要做好药材基础数据的初始化。我们花了整整两周时间整理2000多种中药材的标准名称、别名、分类信息,这部分工作看似枯燥,但后期带来的检索准确率提升非常明显。建议实施时优先完成数据标准化工作,可以事半功倍。