|
|
@@ -26,6 +26,7 @@ import java.io.ByteArrayOutputStream;
|
|
|
import java.io.File;
|
|
|
import java.net.URL;
|
|
|
import java.text.SimpleDateFormat;
|
|
|
+import java.util.*;
|
|
|
import java.util.List;
|
|
|
import java.util.concurrent.TimeUnit;
|
|
|
|
|
|
@@ -245,22 +246,23 @@ public class PrescriptionImageServiceImpl implements PrescriptionImageService {
|
|
|
|
|
|
/**
|
|
|
* 绘制药品列表(每药两行:第一行药名规格数量,第二行用法用量)
|
|
|
- * 药品内部两行之间无空行,每个药品(包括最后一个)之后都加一个空白行
|
|
|
- * @return 返回最后一行的Y坐标(即最后一个空白行之后的位置)
|
|
|
*/
|
|
|
public int drawDrugList(Graphics2D pen, List<FsPrescribeDrug> drugs) {
|
|
|
if (drugs == null || drugs.isEmpty()) {
|
|
|
return 330;
|
|
|
}
|
|
|
-
|
|
|
+ // ====== 关键:预处理药品列表(清洗 + 合并) ======
|
|
|
+ List<FsPrescribeDrug> processedDrugs = preprocessDrugsForPrescription(drugs);
|
|
|
+ if (processedDrugs.isEmpty()) {
|
|
|
+ return 330;
|
|
|
+ }
|
|
|
+ // ====== 绘制逻辑 ======
|
|
|
int x = 133;
|
|
|
int y = 680;
|
|
|
+ int innerLineSpacing = 40;
|
|
|
+ int blankLine = 40;
|
|
|
|
|
|
- // 行高设置(可根据实际字体微调)
|
|
|
- int innerLineSpacing = 40; // 药品内部两行之间的间距(含呼吸感,避免粘连)
|
|
|
- int blankLine = 40; // 药品之间的空白行(比内部稍大,形成视觉分组)
|
|
|
-
|
|
|
- for (FsPrescribeDrug drug : drugs) {
|
|
|
+ for (FsPrescribeDrug drug : processedDrugs) {
|
|
|
// ========== 第一行:药名 + 规格 + 数量 ==========
|
|
|
StringBuilder line1 = new StringBuilder();
|
|
|
if (StringUtils.isNotBlank(drug.getDrugName())) {
|
|
|
@@ -280,7 +282,7 @@ public class PrescriptionImageServiceImpl implements PrescriptionImageService {
|
|
|
if (StringUtils.isNotBlank(firstLine)) {
|
|
|
y = drawDrugTextLine(pen, firstLine, x, y, 1100, innerLineSpacing);
|
|
|
}
|
|
|
- y += innerLineSpacing; // 第一行占位(即使无内容也占)
|
|
|
+ y += innerLineSpacing;
|
|
|
|
|
|
// ========== 第二行:用法用量 ==========
|
|
|
StringBuilder line2 = new StringBuilder();
|
|
|
@@ -301,15 +303,127 @@ public class PrescriptionImageServiceImpl implements PrescriptionImageService {
|
|
|
if (StringUtils.isNotBlank(secondLine)) {
|
|
|
y = drawDrugTextLine(pen, secondLine, x, y, 1000, innerLineSpacing);
|
|
|
}
|
|
|
- y += innerLineSpacing; // 第二行占位(即使无内容也占)
|
|
|
+ y += innerLineSpacing;
|
|
|
|
|
|
- // ========== 每个药品之后(包括最后一个)加一个空白行 ==========
|
|
|
+ // 每个药品之后(包括最后一个)加一个空白行
|
|
|
y += blankLine;
|
|
|
}
|
|
|
|
|
|
return y;
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * 预处理处方药品列表(满足三个需求):
|
|
|
+ * 1. 药名中“赠品”二字被移除;
|
|
|
+ * 2. 若药名或规格含“食品”、“保健”、“仪器”,整条剔除;
|
|
|
+ * 3. 按(药名+规格)合并重复项,数量累加。
|
|
|
+ */
|
|
|
+ private List<FsPrescribeDrug> preprocessDrugsForPrescription(List<FsPrescribeDrug> originalDrugs) {
|
|
|
+ if (originalDrugs == null || originalDrugs.isEmpty()) {
|
|
|
+ return new ArrayList<>();
|
|
|
+ }
|
|
|
+
|
|
|
+// Set<String> bannedKeywords = Collections.unmodifiableSet(
|
|
|
+// new HashSet<>(Arrays.asList("食品", "保健", "仪器","器械"))
|
|
|
+// );
|
|
|
+
|
|
|
+
|
|
|
+ // key: cleanedName + "|" + baseSpec(不含×N部分)
|
|
|
+ Map<String, FsPrescribeDrug> drugMap = new LinkedHashMap<>();
|
|
|
+
|
|
|
+ for (FsPrescribeDrug drug : originalDrugs) {
|
|
|
+ if (drug == null) continue;
|
|
|
+
|
|
|
+ String rawName = StringUtils.defaultString(drug.getDrugName()).trim();
|
|
|
+ String cleanedName = rawName.replace("赠品", "").trim();
|
|
|
+ if (cleanedName.isEmpty()) continue;
|
|
|
+
|
|
|
+ String spec = StringUtils.defaultString(drug.getDrugSpec()).trim();
|
|
|
+
|
|
|
+ // 黑名单检查(用原始 cleanedName 和 spec)
|
|
|
+// if (bannedKeywords.stream().anyMatch(kw -> cleanedName.contains(kw) || spec.contains(kw))) {
|
|
|
+// continue;
|
|
|
+// }
|
|
|
+
|
|
|
+ // 解析规格:分离基础规格和盒数
|
|
|
+ SpecParseResult parsed = parseSpec(spec);
|
|
|
+ String baseSpec = parsed.baseSpec;
|
|
|
+ long specBoxCount = parsed.boxCount;
|
|
|
+
|
|
|
+ // 获取 drugNum 字段的值(可能为 null)
|
|
|
+ Long drugNumObj = drug.getDrugNum();
|
|
|
+ long drugNum = (drugNumObj != null) ? drugNumObj : 0L;
|
|
|
+
|
|
|
+ long totalBoxes;
|
|
|
+ if (drugNum > 0) {
|
|
|
+ totalBoxes = drugNum;
|
|
|
+ } else {
|
|
|
+ totalBoxes = specBoxCount;
|
|
|
+ }
|
|
|
+
|
|
|
+ String key = cleanedName + "|" + baseSpec;
|
|
|
+
|
|
|
+ if (drugMap.containsKey(key)) {
|
|
|
+ FsPrescribeDrug existing = drugMap.get(key);
|
|
|
+ existing.setDrugNum(existing.getDrugNum() + totalBoxes);
|
|
|
+ } else {
|
|
|
+ FsPrescribeDrug newDrug = new FsPrescribeDrug();
|
|
|
+ newDrug.setDrugName(cleanedName);
|
|
|
+ newDrug.setDrugSpec(baseSpec);
|
|
|
+ newDrug.setDrugNum(totalBoxes);
|
|
|
+ newDrug.setDrugUnit(drug.getDrugUnit());
|
|
|
+ newDrug.setUsageMethod(drug.getUsageMethod());
|
|
|
+ newDrug.setUsagePerUseCount(drug.getUsagePerUseCount());
|
|
|
+ newDrug.setUsagePerUseUnit(drug.getUsagePerUseUnit());
|
|
|
+ newDrug.setUsageFrequencyUnit(drug.getUsageFrequencyUnit());
|
|
|
+ drugMap.put(key, newDrug);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return new ArrayList<>(drugMap.values());
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 从规格字符串中提取基础规格和盒数。
|
|
|
+ * 例如:"0.35g*24粒/盒×2" -> baseSpec="0.35g*24粒/盒", boxCount=2
|
|
|
+ * 如果没有 ×N,则 boxCount=1
|
|
|
+ */
|
|
|
+ private static class SpecParseResult {
|
|
|
+ String baseSpec;
|
|
|
+ long boxCount;
|
|
|
+
|
|
|
+ SpecParseResult(String baseSpec, long boxCount) {
|
|
|
+ this.baseSpec = baseSpec;
|
|
|
+ this.boxCount = boxCount;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private SpecParseResult parseSpec(String spec) {
|
|
|
+ if (spec == null || spec.isEmpty()) {
|
|
|
+ return new SpecParseResult(spec, 1L);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 匹配末尾的 "×数字" 或 "*数字"或 "x数字"或 "X数字"(兼容不同写法)
|
|
|
+ // 例如:×2、×10、*3、× 2(带空格)
|
|
|
+ java.util.regex.Pattern pattern = java.util.regex.Pattern.compile("([×**xXxX]\\s*\\d+)$");
|
|
|
+ java.util.regex.Matcher matcher = pattern.matcher(spec);
|
|
|
+
|
|
|
+ if (matcher.find()) {
|
|
|
+ String countPart = matcher.group(1);
|
|
|
+ String base = spec.substring(0, matcher.start()).trim();
|
|
|
+ String numStr = countPart.replaceAll("[×**xXxX\\s]", "").trim();
|
|
|
+ long count = 1L;
|
|
|
+ try {
|
|
|
+ count = Long.parseLong(numStr);
|
|
|
+ } catch (NumberFormatException ignored) {
|
|
|
+ count = 1L;
|
|
|
+ }
|
|
|
+ return new SpecParseResult(base.isEmpty() ? spec : base, count);
|
|
|
+ } else {
|
|
|
+ // 没有 ×N,默认 1 盒
|
|
|
+ return new SpecParseResult(spec, 1L);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
/**
|
|
|
* 专用于药品列表的文本绘制:支持自动换行,返回最后一行文本的基线 Y 坐标
|
|
|
* 不额外增加行高,由调用方控制换行(如 y += innerLineSpacing)
|