|
|
@@ -0,0 +1,431 @@
|
|
|
+package com.etotem.cfc.service;
|
|
|
+
|
|
|
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
|
|
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
|
|
+import com.etotem.cfc.entity.*;
|
|
|
+import com.etotem.cfc.mapper.FamilyMemberAttributesMapper;
|
|
|
+import com.etotem.cfc.mapper.ZodiacConfigMapper;
|
|
|
+import com.etotem.cfc.mapper.BaziConfigMapper;
|
|
|
+import com.etotem.cfc.mapper.BloodTypeConfigMapper;
|
|
|
+import org.springframework.stereotype.Service;
|
|
|
+
|
|
|
+import javax.annotation.Resource;
|
|
|
+import java.math.BigDecimal;
|
|
|
+import java.math.RoundingMode;
|
|
|
+import java.util.*;
|
|
|
+import java.util.Calendar;
|
|
|
+
|
|
|
+@Service
|
|
|
+public class FamilyMemberAttributeService extends ServiceImpl<FamilyMemberAttributesMapper, FamilyMemberAttributes> {
|
|
|
+
|
|
|
+ @Resource
|
|
|
+ private ZodiacConfigMapper zodiacConfigMapper;
|
|
|
+
|
|
|
+ @Resource
|
|
|
+ private BaziConfigMapper baziConfigMapper;
|
|
|
+
|
|
|
+ @Resource
|
|
|
+ private BloodTypeConfigMapper bloodTypeConfigMapper;
|
|
|
+
|
|
|
+ // ========== Chinese Zodiac constants ==========
|
|
|
+ private static final String[] ZODIAC_CODES = {"rat", "ox", "tiger", "rabbit", "dragon", "snake",
|
|
|
+ "horse", "sheep", "monkey", "rooster", "dog", "pig"};
|
|
|
+
|
|
|
+ // ========== Heavenly Stems & Earthly Branches ==========
|
|
|
+ private static final String[] HEAVENLY_STEMS = {"甲", "乙", "丙", "丁", "戊", "己", "庚", "辛", "壬", "癸"};
|
|
|
+ private static final String[] EARTHLY_BRANCHES = {"子", "丑", "寅", "卯", "辰", "巳", "午", "未", "申", "酉", "戌", "亥"};
|
|
|
+
|
|
|
+ // ========== Month pillar lookup: month earthly branches (1=寅, 2=卯, ..., 12=丑) ==========
|
|
|
+ private static final int[] MONTH_BRANCH_INDEX = {2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 0, 1}; // month 1-12 -> branch index
|
|
|
+
|
|
|
+ // ========== Hour pillar: earthly branch by two-hour period ==========
|
|
|
+ private static final int[] HOUR_BRANCH_MAP = new int[24];
|
|
|
+ static {
|
|
|
+ // 23-0: 子(0), 1-2: 丑(1), 3-4: 寅(2), ... 21-22: 亥(11)
|
|
|
+ HOUR_BRANCH_MAP[23] = 0; HOUR_BRANCH_MAP[0] = 0;
|
|
|
+ HOUR_BRANCH_MAP[1] = 1; HOUR_BRANCH_MAP[2] = 1;
|
|
|
+ HOUR_BRANCH_MAP[3] = 2; HOUR_BRANCH_MAP[4] = 2;
|
|
|
+ HOUR_BRANCH_MAP[5] = 3; HOUR_BRANCH_MAP[6] = 3;
|
|
|
+ HOUR_BRANCH_MAP[7] = 4; HOUR_BRANCH_MAP[8] = 4;
|
|
|
+ HOUR_BRANCH_MAP[9] = 5; HOUR_BRANCH_MAP[10] = 5;
|
|
|
+ HOUR_BRANCH_MAP[11] = 6; HOUR_BRANCH_MAP[12] = 6;
|
|
|
+ HOUR_BRANCH_MAP[13] = 7; HOUR_BRANCH_MAP[14] = 7;
|
|
|
+ HOUR_BRANCH_MAP[15] = 8; HOUR_BRANCH_MAP[16] = 8;
|
|
|
+ HOUR_BRANCH_MAP[17] = 9; HOUR_BRANCH_MAP[18] = 9;
|
|
|
+ HOUR_BRANCH_MAP[19] = 10; HOUR_BRANCH_MAP[20] = 10;
|
|
|
+ HOUR_BRANCH_MAP[21] = 11; HOUR_BRANCH_MAP[22] = 11;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Get attributes by memberId + memberType
|
|
|
+ */
|
|
|
+ public FamilyMemberAttributes getByMember(Long memberId, String memberType) {
|
|
|
+ LambdaQueryWrapper<FamilyMemberAttributes> wrapper = new LambdaQueryWrapper<>();
|
|
|
+ wrapper.eq(FamilyMemberAttributes::getMemberId, memberId);
|
|
|
+ wrapper.eq(FamilyMemberAttributes::getMemberType, memberType);
|
|
|
+ return this.getOne(wrapper);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Save or update attributes (upsert by memberId + memberType)
|
|
|
+ */
|
|
|
+ public FamilyMemberAttributes saveOrUpdateAttribute(FamilyMemberAttributes attribute) {
|
|
|
+ if (attribute.getId() != null) {
|
|
|
+ attribute.setUpdatedAt(new Date());
|
|
|
+ this.updateById(attribute);
|
|
|
+ return attribute;
|
|
|
+ }
|
|
|
+ if (attribute.getMemberId() != null && attribute.getMemberType() != null) {
|
|
|
+ FamilyMemberAttributes existing = getByMember(attribute.getMemberId(), attribute.getMemberType());
|
|
|
+ if (existing != null) {
|
|
|
+ attribute.setId(existing.getId());
|
|
|
+ attribute.setCreatedAt(existing.getCreatedAt());
|
|
|
+ attribute.setUpdatedAt(new Date());
|
|
|
+ this.updateById(attribute);
|
|
|
+ return attribute;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ attribute.setCreatedAt(new Date());
|
|
|
+ attribute.setUpdatedAt(new Date());
|
|
|
+ this.save(attribute);
|
|
|
+ return attribute;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Auto-calculate zodiac, eightCharacters (BaZi), and life number from birthDatetime.
|
|
|
+ * Also computes wuxingElements from the four pillars.
|
|
|
+ */
|
|
|
+ public void autoCalculate(FamilyMemberAttributes attrs) {
|
|
|
+ if (attrs.getBirthDatetime() == null) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ Calendar cal = Calendar.getInstance();
|
|
|
+ cal.setTime(attrs.getBirthDatetime());
|
|
|
+ int year = cal.get(Calendar.YEAR);
|
|
|
+ int month = cal.get(Calendar.MONTH) + 1; // 1-based
|
|
|
+ int day = cal.get(Calendar.DAY_OF_MONTH);
|
|
|
+ int hour = cal.get(Calendar.HOUR_OF_DAY);
|
|
|
+
|
|
|
+ // --- Zodiac ---
|
|
|
+ String zodiac = ZODIAC_CODES[(year - 4) % 12];
|
|
|
+ attrs.setZodiac(zodiac);
|
|
|
+
|
|
|
+ // --- Year Pillar ---
|
|
|
+ int yearStemIdx = (year - 4) % 10;
|
|
|
+ int yearBranchIdx = (year - 4) % 12;
|
|
|
+ String yearPillar = HEAVENLY_STEMS[yearStemIdx] + EARTHLY_BRANCHES[yearBranchIdx];
|
|
|
+
|
|
|
+ // --- Month Pillar ---
|
|
|
+ // Month stem: based on year stem, using "五虎遁" rule
|
|
|
+ // 甲己之年丙作首 (stem 0,5 -> start 丙=2), 乙庚之年戊为头 (1,6 -> 戊=4),
|
|
|
+ // 丙辛之岁寻庚上 (2,7 -> 庚=6), 丁壬壬寅顺水流 (3,8 -> 壬=8),
|
|
|
+ // 戊癸甲寅好追求 (4,9 -> 甲=0)
|
|
|
+ int monthStemStart = new int[]{2, 4, 6, 8, 0}[(yearStemIdx % 5)];
|
|
|
+ int monthStemIdx = (monthStemStart + month - 1) % 10;
|
|
|
+ int monthBranchIdx = MONTH_BRANCH_INDEX[month - 1];
|
|
|
+ String monthPillar = HEAVENLY_STEMS[monthStemIdx] + EARTHLY_BRANCHES[monthBranchIdx];
|
|
|
+
|
|
|
+ // --- Day Pillar ---
|
|
|
+ // Simplified day pillar calculation using a known reference:
|
|
|
+ // Reference: 2000-01-01 = 甲子日 (stem=0, branch=0)
|
|
|
+ // Days from reference to target
|
|
|
+ Calendar ref = Calendar.getInstance();
|
|
|
+ ref.set(2000, Calendar.JANUARY, 1, 0, 0, 0);
|
|
|
+ ref.set(Calendar.MILLISECOND, 0);
|
|
|
+ long diffMs = cal.getTimeInMillis() - ref.getTimeInMillis();
|
|
|
+ long diffDays = diffMs / (24 * 60 * 60 * 1000);
|
|
|
+ int dayStemIdx = (int) ((0 + diffDays) % 10);
|
|
|
+ if (dayStemIdx < 0) dayStemIdx += 10;
|
|
|
+ int dayBranchIdx = (int) ((0 + diffDays) % 12);
|
|
|
+ if (dayBranchIdx < 0) dayBranchIdx += 12;
|
|
|
+ String dayPillar = HEAVENLY_STEMS[dayStemIdx] + EARTHLY_BRANCHES[dayBranchIdx];
|
|
|
+
|
|
|
+ // --- Hour Pillar ---
|
|
|
+ // Hour stem: based on day stem, using "五鼠遁" rule
|
|
|
+ // 甲己日起甲子 (0,5 -> start 甲=0), 乙庚日起丙子 (1,6 -> 丙=2),
|
|
|
+ // 丙辛日起戊子 (2,7 -> 戊=4), 丁壬日起庚子 (3,8 -> 庚=6),
|
|
|
+ // 戊癸日起壬子 (4,9 -> 壬=8)
|
|
|
+ int hourBranchIdx = HOUR_BRANCH_MAP[hour];
|
|
|
+ int hourStemStart = new int[]{0, 2, 4, 6, 8}[(dayStemIdx % 5)];
|
|
|
+ int hourStemIdx = (hourStemStart + hourBranchIdx) % 10;
|
|
|
+ String hourPillar = HEAVENLY_STEMS[hourStemIdx] + EARTHLY_BRANCHES[hourBranchIdx];
|
|
|
+
|
|
|
+ // --- Eight Characters JSON ---
|
|
|
+ Map<String, String> eightCharMap = new LinkedHashMap<>();
|
|
|
+ eightCharMap.put("year", yearPillar);
|
|
|
+ eightCharMap.put("month", monthPillar);
|
|
|
+ eightCharMap.put("day", dayPillar);
|
|
|
+ eightCharMap.put("hour", hourPillar);
|
|
|
+ attrs.setEightCharacters(toJson(eightCharMap));
|
|
|
+
|
|
|
+ // --- Wuxing Elements ---
|
|
|
+ // Count element occurrences from all 8 characters (4 stems + 4 branches)
|
|
|
+ Map<String, Integer> wuxingCount = new LinkedHashMap<>();
|
|
|
+ wuxingCount.put("wood", 0);
|
|
|
+ wuxingCount.put("fire", 0);
|
|
|
+ wuxingCount.put("earth", 0);
|
|
|
+ wuxingCount.put("metal", 0);
|
|
|
+ wuxingCount.put("water", 0);
|
|
|
+
|
|
|
+ String[] allStems = {HEAVENLY_STEMS[yearStemIdx], HEAVENLY_STEMS[monthStemIdx],
|
|
|
+ HEAVENLY_STEMS[dayStemIdx], HEAVENLY_STEMS[hourStemIdx]};
|
|
|
+ int[] allBranchIndices = {yearBranchIdx, monthBranchIdx, dayBranchIdx, hourBranchIdx};
|
|
|
+
|
|
|
+ for (String stem : allStems) {
|
|
|
+ String element = stemToElement(stem);
|
|
|
+ wuxingCount.put(element, wuxingCount.get(element) + 1);
|
|
|
+ }
|
|
|
+ for (int bIdx : allBranchIndices) {
|
|
|
+ String element = branchToElement(bIdx);
|
|
|
+ wuxingCount.put(element, wuxingCount.get(element) + 1);
|
|
|
+ }
|
|
|
+
|
|
|
+ // Convert counts to percentages (total = 8)
|
|
|
+ Map<String, Integer> wuxingPct = new LinkedHashMap<>();
|
|
|
+ for (Map.Entry<String, Integer> entry : wuxingCount.entrySet()) {
|
|
|
+ wuxingPct.put(entry.getKey(), (int) Math.round(entry.getValue() * 100.0 / 8));
|
|
|
+ }
|
|
|
+ attrs.setWuxingElements(toJson(wuxingPct));
|
|
|
+
|
|
|
+ // --- Life Number ---
|
|
|
+ int lifeNumber = calcLifeNumber(year, month, day);
|
|
|
+ // Store in behaviorModifier as a field (no dedicated column)
|
|
|
+ Map<String, Object> modifierMap = new LinkedHashMap<>();
|
|
|
+ if (attrs.getBehaviorModifier() != null && !attrs.getBehaviorModifier().isEmpty()) {
|
|
|
+ modifierMap = fromJson(attrs.getBehaviorModifier());
|
|
|
+ }
|
|
|
+ modifierMap.put("lifeNumber", lifeNumber);
|
|
|
+ attrs.setBehaviorModifier(toJson(modifierMap));
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Calculate innate base score from zodiac_config + bazi_config + blood_type_config.
|
|
|
+ * Formula: zodiac × 50% + bazi (average of stems) × 30% + blood_type × 20%
|
|
|
+ *
|
|
|
+ * @param memberId member ID
|
|
|
+ * @param memberType child/parent
|
|
|
+ * @param dimensionCode "mind" or "wisdom"
|
|
|
+ * @return computed base score (0-100)
|
|
|
+ */
|
|
|
+ public BigDecimal calcInnateScore(Long memberId, String memberType, String dimensionCode) {
|
|
|
+ FamilyMemberAttributes attrs = getByMember(memberId, memberType);
|
|
|
+ if (attrs == null) {
|
|
|
+ return BigDecimal.ZERO;
|
|
|
+ }
|
|
|
+
|
|
|
+ // If already computed, return cached value
|
|
|
+ if ("mind".equals(dimensionCode) && attrs.getMindBaseScore() != null
|
|
|
+ && attrs.getMindBaseScore().compareTo(BigDecimal.ZERO) > 0) {
|
|
|
+ return attrs.getMindBaseScore();
|
|
|
+ }
|
|
|
+ if ("wisdom".equals(dimensionCode) && attrs.getWisdomBaseScore() != null
|
|
|
+ && attrs.getWisdomBaseScore().compareTo(BigDecimal.ZERO) > 0) {
|
|
|
+ return attrs.getWisdomBaseScore();
|
|
|
+ }
|
|
|
+
|
|
|
+ // --- Zodiac contribution (50%) ---
|
|
|
+ BigDecimal zodiacScore = BigDecimal.ZERO;
|
|
|
+ if (attrs.getZodiac() != null) {
|
|
|
+ LambdaQueryWrapper<ZodiacConfig> zodiacWrapper = new LambdaQueryWrapper<>();
|
|
|
+ zodiacWrapper.eq(ZodiacConfig::getZodiacCode, attrs.getZodiac());
|
|
|
+ ZodiacConfig zodiacConfig = zodiacConfigMapper.selectOne(zodiacWrapper);
|
|
|
+ if (zodiacConfig != null) {
|
|
|
+ zodiacScore = "mind".equals(dimensionCode) ? zodiacConfig.getMindBase() : zodiacConfig.getWisdomBase();
|
|
|
+ if (zodiacScore == null) zodiacScore = BigDecimal.ZERO;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // --- BaZi contribution (30%) - average of all stems/branches in eightCharacters ---
|
|
|
+ BigDecimal baziScore = BigDecimal.ZERO;
|
|
|
+ if (attrs.getEightCharacters() != null) {
|
|
|
+ Map<String, Object> eightCharMap = fromJson(attrs.getEightCharacters());
|
|
|
+ List<BigDecimal> scores = new ArrayList<>();
|
|
|
+ for (Map.Entry<String, Object> entry : eightCharMap.entrySet()) {
|
|
|
+ String pillar = String.valueOf(entry.getValue());
|
|
|
+ if (pillar != null && pillar.length() >= 1) {
|
|
|
+ String stem = pillar.substring(0, 1);
|
|
|
+ LambdaQueryWrapper<BaziConfig> baziWrapper = new LambdaQueryWrapper<>();
|
|
|
+ baziWrapper.eq(BaziConfig::getStemOrBranch, stem);
|
|
|
+ baziWrapper.eq(BaziConfig::getType, "stem");
|
|
|
+ BaziConfig baziConfig = baziConfigMapper.selectOne(baziWrapper);
|
|
|
+ if (baziConfig != null) {
|
|
|
+ BigDecimal val = "mind".equals(dimensionCode) ? baziConfig.getMindBase() : baziConfig.getWisdomBase();
|
|
|
+ if (val != null) scores.add(val);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (pillar != null && pillar.length() >= 2) {
|
|
|
+ String branch = pillar.substring(1, 2);
|
|
|
+ LambdaQueryWrapper<BaziConfig> baziWrapper = new LambdaQueryWrapper<>();
|
|
|
+ baziWrapper.eq(BaziConfig::getStemOrBranch, branch);
|
|
|
+ baziWrapper.eq(BaziConfig::getType, "branch");
|
|
|
+ BaziConfig baziConfig = baziConfigMapper.selectOne(baziWrapper);
|
|
|
+ if (baziConfig != null) {
|
|
|
+ BigDecimal val = "mind".equals(dimensionCode) ? baziConfig.getMindBase() : baziConfig.getWisdomBase();
|
|
|
+ if (val != null) scores.add(val);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (!scores.isEmpty()) {
|
|
|
+ BigDecimal sum = BigDecimal.ZERO;
|
|
|
+ for (BigDecimal s : scores) sum = sum.add(s);
|
|
|
+ baziScore = sum.divide(BigDecimal.valueOf(scores.size()), 4, RoundingMode.HALF_UP);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // --- Blood type contribution (20%) ---
|
|
|
+ BigDecimal bloodScore = BigDecimal.ZERO;
|
|
|
+ if (attrs.getBloodType() != null) {
|
|
|
+ LambdaQueryWrapper<BloodTypeConfig> bloodWrapper = new LambdaQueryWrapper<>();
|
|
|
+ bloodWrapper.eq(BloodTypeConfig::getBloodType, attrs.getBloodType());
|
|
|
+ BloodTypeConfig bloodConfig = bloodTypeConfigMapper.selectOne(bloodWrapper);
|
|
|
+ if (bloodConfig != null) {
|
|
|
+ bloodScore = "mind".equals(dimensionCode) ? bloodConfig.getMindBase() : bloodConfig.getWisdomBase();
|
|
|
+ if (bloodScore == null) bloodScore = BigDecimal.ZERO;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // --- Weighted sum ---
|
|
|
+ BigDecimal result = zodiacScore.multiply(BigDecimal.valueOf(0.5))
|
|
|
+ .add(baziScore.multiply(BigDecimal.valueOf(0.3)))
|
|
|
+ .add(bloodScore.multiply(BigDecimal.valueOf(0.2)))
|
|
|
+ .setScale(2, RoundingMode.HALF_UP);
|
|
|
+
|
|
|
+ // Cache the result
|
|
|
+ if ("mind".equals(dimensionCode)) {
|
|
|
+ attrs.setMindBaseScore(result);
|
|
|
+ } else {
|
|
|
+ attrs.setWisdomBaseScore(result);
|
|
|
+ }
|
|
|
+ this.updateById(attrs);
|
|
|
+
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ // ========== Helper methods ==========
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Calculate life number: sum digits of YYYYMMDD until single digit
|
|
|
+ */
|
|
|
+ private int calcLifeNumber(int year, int month, int day) {
|
|
|
+ String dateStr = String.format("%04d%02d%02d", year, month, day);
|
|
|
+ int sum = 0;
|
|
|
+ for (char c : dateStr.toCharArray()) {
|
|
|
+ sum += (c - '0');
|
|
|
+ }
|
|
|
+ while (sum >= 10) {
|
|
|
+ int newSum = 0;
|
|
|
+ int n = sum;
|
|
|
+ while (n > 0) {
|
|
|
+ newSum += n % 10;
|
|
|
+ n /= 10;
|
|
|
+ }
|
|
|
+ sum = newSum;
|
|
|
+ }
|
|
|
+ return sum;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Map heavenly stem to wuxing element
|
|
|
+ */
|
|
|
+ private String stemToElement(String stem) {
|
|
|
+ switch (stem) {
|
|
|
+ case "甲": case "乙": return "wood";
|
|
|
+ case "丙": case "丁": return "fire";
|
|
|
+ case "戊": case "己": return "earth";
|
|
|
+ case "庚": case "辛": return "metal";
|
|
|
+ case "壬": case "癸": return "water";
|
|
|
+ default: return "earth";
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Map earthly branch index to wuxing element
|
|
|
+ */
|
|
|
+ private String branchToElement(int branchIdx) {
|
|
|
+ // 子=水, 丑=土, 寅=木, 卯=木, 辰=土, 巳=火, 午=火, 未=土, 申=金, 酉=金, 戌=土, 亥=水
|
|
|
+ switch (branchIdx) {
|
|
|
+ case 0: case 11: return "water"; // 子, 亥
|
|
|
+ case 1: case 4: case 7: case 10: return "earth"; // 丑, 辰, 未, 戌
|
|
|
+ case 2: case 3: return "wood"; // 寅, 卯
|
|
|
+ case 5: case 6: return "fire"; // 巳, 午
|
|
|
+ case 8: case 9: return "metal"; // 申, 酉
|
|
|
+ default: return "earth";
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Simple JSON serializer for Map
|
|
|
+ */
|
|
|
+ private String toJson(Map<String, ?> map) {
|
|
|
+ StringBuilder sb = new StringBuilder("{");
|
|
|
+ boolean first = true;
|
|
|
+ for (Map.Entry<String, ?> entry : map.entrySet()) {
|
|
|
+ if (!first) sb.append(",");
|
|
|
+ first = false;
|
|
|
+ sb.append("\"").append(entry.getKey()).append("\":");
|
|
|
+ Object val = entry.getValue();
|
|
|
+ if (val instanceof Number) {
|
|
|
+ sb.append(val);
|
|
|
+ } else {
|
|
|
+ sb.append("\"").append(val).append("\"");
|
|
|
+ }
|
|
|
+ }
|
|
|
+ sb.append("}");
|
|
|
+ return sb.toString();
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Simple JSON parser for Map<String, Object>
|
|
|
+ */
|
|
|
+ @SuppressWarnings("unchecked")
|
|
|
+ private Map<String, Object> fromJson(String json) {
|
|
|
+ Map<String, Object> map = new LinkedHashMap<>();
|
|
|
+ if (json == null || json.isEmpty() || !json.startsWith("{")) {
|
|
|
+ return map;
|
|
|
+ }
|
|
|
+ String content = json.substring(1, json.length() - 1).trim();
|
|
|
+ if (content.isEmpty()) return map;
|
|
|
+ String[] pairs = content.split(",");
|
|
|
+ for (String pair : pairs) {
|
|
|
+ String[] kv = pair.split(":", 2);
|
|
|
+ if (kv.length == 2) {
|
|
|
+ String key = kv[0].trim().replace("\"", "");
|
|
|
+ String value = kv[1].trim().replace("\"", "");
|
|
|
+ // Try to parse as number
|
|
|
+ try {
|
|
|
+ if (value.contains(".")) {
|
|
|
+ map.put(key, Double.parseDouble(value));
|
|
|
+ } else {
|
|
|
+ map.put(key, Integer.parseInt(value));
|
|
|
+ }
|
|
|
+ } catch (NumberFormatException e) {
|
|
|
+ map.put(key, value);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return map;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Simple JSON parser for Map<String, String>
|
|
|
+ */
|
|
|
+ private Map<String, String> fromJson(String json, @SuppressWarnings("unused") boolean stringValues) {
|
|
|
+ Map<String, String> map = new LinkedHashMap<>();
|
|
|
+ if (json == null || json.isEmpty() || !json.startsWith("{")) {
|
|
|
+ return map;
|
|
|
+ }
|
|
|
+ String content = json.substring(1, json.length() - 1).trim();
|
|
|
+ if (content.isEmpty()) return map;
|
|
|
+ String[] pairs = content.split(",");
|
|
|
+ for (String pair : pairs) {
|
|
|
+ String[] kv = pair.split(":", 2);
|
|
|
+ if (kv.length == 2) {
|
|
|
+ String key = kv[0].trim().replace("\"", "");
|
|
|
+ String value = kv[1].trim().replace("\"", "");
|
|
|
+ map.put(key, value);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return map;
|
|
|
+ }
|
|
|
+}
|