1. 项目概述:经方药食两用服务平台的技术架构
这个基于Java SpringBoot+Vue3+MyBatis的经方药食两用服务平台,是一个典型的现代前后端分离架构的中医药健康类应用。平台核心功能围绕"药食同源"理念展开,将传统经方与现代食疗方案数字化,为用户提供个性化推荐、配伍禁忌检查、功效查询等服务。
技术栈选择上,后端采用SpringBoot 2.7.x+MyBatis-Plus 3.5.x组合,前端使用Vue3+TypeScript+Element Plus构建,数据库选用MySQL 8.0。这种技术组合在当前企业级应用中非常普遍,既能保证开发效率,又能满足高并发场景下的性能需求。
提示:项目源码中通常包含完整的Maven多模块结构和Vue CLI工程,建议使用IDEA 2022+和VS Code作为开发工具链
2. 核心技术栈解析
2.1 SpringBoot后端设计要点
后端采用经典的三层架构设计:
- Controller层:处理HTTP请求,使用
@Validated进行参数校验 - Service层:业务逻辑实现,包含经方配伍算法核心实现
- DAO层:MyBatis-Plus实现数据库操作
特殊配置项包括:
java复制// 中药剂量换算拦截器
public class DosageConverter implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
// 实现两->克等单位自动转换
}
}
// MyBatis-Plus分页插件配置
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
return interceptor;
}
2.2 Vue3前端工程化实践
前端架构采用Composition API+Pinia状态管理:
javascript复制// 经方搜索组件示例
const searchForm = reactive({
name: '',
efficacy: '',
ingredients: []
})
const searchResults = ref([])
const search = async () => {
results.value = await api.post('/formula/search', searchForm)
}
关键优化点:
- 使用Vite代替Webpack构建,HMR速度提升3倍
- 采用动态导入实现路由懒加载
- 自定义指令实现中药名称的拼音搜索支持
2.3 MySQL数据库设计规范
主要表结构设计原则:
- 中药表(herb):包含四气五味、归经等字段
- 经方表(formula):存储组成、用法等
- 食材表(food):记录性味归经
- 配伍规则表(compatibility):存储十八反十九畏等规则
sql复制CREATE TABLE `herb` (
`id` int NOT NULL AUTO_INCREMENT,
`name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL,
`pinyin` varchar(100) DEFAULT NULL COMMENT '拼音检索字段',
`category` varchar(20) DEFAULT NULL COMMENT '解表/清热...',
`flavor` varchar(50) DEFAULT NULL COMMENT '辛甘酸苦咸',
`nature` varchar(10) DEFAULT NULL COMMENT '寒热温凉平',
`meridian` varchar(100) DEFAULT NULL COMMENT '归经',
`dosage_min` decimal(5,2) DEFAULT NULL COMMENT '最小用量(克)',
`dosage_max` decimal(5,2) DEFAULT NULL COMMENT '最大用量(克)',
`contraindication` text COMMENT '禁忌说明',
PRIMARY KEY (`id`),
FULLTEXT KEY `idx_search` (`name`,`pinyin`,`category`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
3. 核心业务逻辑实现
3.1 药食配伍算法实现
配伍检查核心逻辑:
java复制public class CompatibilityChecker {
private static final Set<String> EIGHTEEN_INCOMPATIBLE = Set.of(
"乌头-半夏", "乌头-瓜蒌", "甘草-甘遂" // 十八反规则
);
public static CheckResult check(List<Herb> herbs, List<Food> foods) {
// 检查十八反
for (Herb h1 : herbs) {
for (Herb h2 : herbs) {
if (EIGHTEEN_INCOMPATIBLE.contains(h1.getName()+"-"+h2.getName())) {
return CheckResult.fail("存在配伍禁忌:"+h1.getName()+"反"+h2.getName());
}
}
}
// 检查食物相克
// ...
return CheckResult.success();
}
}
3.2 智能推荐系统设计
基于用户体质的推荐算法流程:
- 用户完成中医体质问卷(九种体质分类)
- 系统根据体质得分向量计算相似经方
- 结合季节因素调整推荐权重
- 过滤存在配伍禁忌的方案
java复制public List<Formula> recommend(User user) {
// 1. 获取用户体质向量
double[] constitution = user.getConstitutionVector();
// 2. 计算余弦相似度
List<FormulaCandidate> candidates = formulaRepository.findAll()
.stream()
.map(f -> new FormulaCandidate(f, cosineSimilarity(constitution, f.getConstitutionVector())))
.sorted(Comparator.comparingDouble(FormulaCandidate::getScore).reversed())
.limit(100)
.collect(Collectors.toList());
// 3. 季节因素调整
Season season = SeasonDetector.getCurrentSeason();
candidates.forEach(c -> c.setScore(c.getScore() * season.getWeight(c.getFormula())));
// 4. 过滤禁忌
return candidates.stream()
.filter(c -> CompatibilityChecker.check(c.getFormula().getHerbs()).isSuccess())
.map(FormulaCandidate::getFormula)
.limit(10)
.collect(Collectors.toList());
}
4. 前后端交互关键实现
4.1 安全认证方案
采用JWT+RefreshToken双令牌机制:
- 登录接口返回accessToken(30分钟过期)和refreshToken(7天过期)
- 前端请求头携带Authorization: Bearer
- 后端使用Spring Security过滤器链验证
安全配置示例:
java复制@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.csrf().disable()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers("/api/auth/**").permitAll()
.antMatchers("/api/**").authenticated()
.anyRequest().permitAll()
.and()
.addFilterBefore(jwtFilter(), UsernamePasswordAuthenticationFilter.class);
return http.build();
}
}
4.2 文件上传与OSS集成
经方图片上传处理流程:
- 前端将文件转为Base64或FormData格式
- 后端接收后生成唯一文件名(MD5+时间戳)
- 调用OSS SDK上传到阿里云OSS
- 返回CDN访问地址
javascript复制// Vue3上传组件
const uploadImage = async (file) => {
const formData = new FormData()
formData.append('file', file)
const { url } = await api.post('/upload', formData, {
headers: { 'Content-Type': 'multipart/form-data' }
})
return url
}
5. 性能优化实践
5.1 缓存策略设计
采用多级缓存架构:
- 本地Caffeine缓存高频访问的经方数据
- Redis缓存用户个性化推荐结果
- MySQL查询结果缓存
缓存配置示例:
java复制@Configuration
@EnableCaching
public class CacheConfig {
@Bean
public CacheManager cacheManager() {
CaffeineCacheManager manager = new CaffeineCacheManager();
manager.setCaffeine(Caffeine.newBuilder()
.initialCapacity(100)
.maximumSize(1000)
.expireAfterWrite(10, TimeUnit.MINUTES));
return manager;
}
}
@Service
@CacheConfig(cacheNames = "formula")
public class FormulaService {
@Cacheable(key = "#id")
public Formula getById(Long id) {
return formulaMapper.selectById(id);
}
}
5.2 SQL优化案例
经方复杂查询优化前:
sql复制SELECT * FROM formula f
JOIN formula_herb fh ON f.id = fh.formula_id
JOIN herb h ON fh.herb_id = h.id
WHERE h.nature = '温' AND f.efficacy LIKE '%感冒%'
优化后方案:
- 添加复合索引:
ALTER TABLE formula ADD INDEX idx_efficacy (efficacy(20)) - 使用覆盖索引查询:
sql复制SELECT f.id, f.name FROM formula f
WHERE EXISTS (
SELECT 1 FROM formula_herb fh
JOIN herb h ON fh.herb_id = h.id
WHERE fh.formula_id = f.id AND h.nature = '温'
) AND f.efficacy LIKE '感冒%'
6. 部署与监控方案
6.1 容器化部署
Docker Compose编排示例:
yaml复制version: '3'
services:
app:
image: openjdk:17-jdk
ports:
- "8080:8080"
volumes:
- ./app.jar:/app.jar
command: java -jar /app.jar
depends_on:
- mysql
- redis
mysql:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: root
MYSQL_DATABASE: tcm
ports:
- "3306:3306"
volumes:
- ./mysql-data:/var/lib/mysql
redis:
image: redis:6
ports:
- "6379:6379"
6.2 健康监控配置
SpringBoot Actuator配置:
properties复制# application.properties
management.endpoints.web.exposure.include=health,info,metrics
management.endpoint.health.show-details=always
management.metrics.tags.application=tcm-platform
Prometheus监控指标采集:
java复制@Bean
MeterRegistryCustomizer<MeterRegistry> metricsCommonTags() {
return registry -> registry.config().commonTags(
"region", System.getenv("REGION"),
"service", "tcm-service"
);
}
7. 开发中的典型问题解决
7.1 中药剂量单位统一问题
问题现象:
- 古籍记载的"两"、"钱"等单位需要转换为现代"克"
- 不同朝代度量衡标准不同
解决方案:
- 创建单位换算服务:
java复制public interface DosageConverter {
BigDecimal convert(BigDecimal value, String fromUnit, String toUnit);
}
@Service
public class AncientDosageConverter implements DosageConverter {
private static final Map<String, BigDecimal> HAN_DYNASTY = Map.of(
"两", new BigDecimal("15.625"),
"钱", new BigDecimal("3.90625")
);
@Override
public BigDecimal convert(BigDecimal value, String fromUnit, String toUnit) {
if ("克".equals(toUnit)) {
return value.multiply(HAN_DYNASTY.get(fromUnit));
}
throw new UnsupportedOperationException();
}
}
7.2 Vue3响应式数据丢失问题
典型场景:
- 从接口获取的经方数据在组件中失去响应性
- 使用reactive包裹的对象属性变更不触发更新
解决方案:
- 使用ref处理基本类型
- 对象数组使用
reactive+toRefs组合:
javascript复制const formula = reactive({
herbs: [] as Herb[]
})
const loadFormula = async (id) => {
const res = await api.get(`/formula/${id}`)
formula.herbs = res.data.herbs // 保持响应性
}
8. 项目扩展方向建议
8.1 中医药知识图谱构建
基于现有数据的扩展:
- 使用Neo4j构建药材-功效-病症关系网
- 实现智能问答功能:
cypher复制// 查询具有清热功效且适合夏季使用的中药
MATCH (h:Herb)-[:HAS_EFFICACY]->(e:Efficacy {name:'清热'})
MATCH (h)-[:SEASON_SUITABILITY]->(s:Season {name:'夏季'})
RETURN h.name, h.nature, h.flavor
8.2 微信小程序端开发
适配方案:
- 复用现有API接口
- 使用Uni-app跨平台框架:
javascript复制// pages/formula/detail.vue
export default {
data() {
return {
formula: {}
}
},
onLoad(options) {
uni.request({
url: 'https://api.example.com/formula/' + options.id,
success: (res) => {
this.formula = res.data
}
})
}
}
在实际开发这类中医药信息系统时,需要特别注意传统医学理论与现代计算机技术的结合方式。比如我们在处理药材配伍规则时,不仅需要准确实现十八反十九畏等经典规则,还要考虑各地医师的临床经验差异。建议在系统设计时保留足够的灵活性,通过规则引擎等方式支持后续的知识库扩充。
