|
|
@@ -1,7 +1,12 @@
|
|
|
package com.etotem.cfc.service.impl;
|
|
|
|
|
|
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
|
|
import com.etotem.cfc.entity.FamilyFortune;
|
|
|
+import com.etotem.cfc.entity.FamilyMember;
|
|
|
+import com.etotem.cfc.entity.FamilyMemberAttributes;
|
|
|
import com.etotem.cfc.mapper.FamilyFortuneMapper;
|
|
|
+import com.etotem.cfc.mapper.FamilyMemberAttributesMapper;
|
|
|
+import com.etotem.cfc.mapper.FamilyMemberMapper;
|
|
|
import com.etotem.cfc.service.FortuneService;
|
|
|
import org.springframework.stereotype.Service;
|
|
|
import org.springframework.transaction.annotation.Transactional;
|
|
|
@@ -16,6 +21,16 @@ public class FortuneServiceImpl implements FortuneService {
|
|
|
|
|
|
@Resource
|
|
|
private FamilyFortuneMapper familyFortuneMapper;
|
|
|
+
|
|
|
+ @Resource
|
|
|
+ private FamilyMemberMapper familyMemberMapper;
|
|
|
+
|
|
|
+ @Resource
|
|
|
+ private FamilyMemberAttributesMapper familyMemberAttributesMapper;
|
|
|
+
|
|
|
+ // 天干地支常量(与 FamilyMemberAttributeService 保持一致)
|
|
|
+ private static final String[] HEAVENLY_STEMS = {"甲", "乙", "丙", "丁", "戊", "己", "庚", "辛", "壬", "癸"};
|
|
|
+ private static final String[] EARTHLY_BRANCHES = {"子", "丑", "寅", "卯", "辰", "巳", "午", "未", "申", "酉", "戌", "亥"};
|
|
|
|
|
|
@Override
|
|
|
public FamilyFortune getFamilyFortune(Long familyId, LocalDate date) {
|
|
|
@@ -75,36 +90,131 @@ public class FortuneServiceImpl implements FortuneService {
|
|
|
return result;
|
|
|
}
|
|
|
|
|
|
- /**
|
|
|
- * 获取日期的农历信息(简化实现)
|
|
|
+/**
|
|
|
+ * 获取日期的农历信息(与 FamilyMemberAttributeService 保持一致的干支算法)
|
|
|
*/
|
|
|
private ChineseDate getChineseDate(LocalDate date) {
|
|
|
- // TODO: 实际项目中可以集成农历库或数据库表
|
|
|
- // 这里简化实现:仅基于月份
|
|
|
ChineseDate cd = new ChineseDate();
|
|
|
cd.year = date.getYear();
|
|
|
cd.month = date.getMonthValue();
|
|
|
cd.day = date.getDayOfMonth();
|
|
|
cd.ganzhiYear = getGanzhiYear(date.getYear());
|
|
|
cd.ganzhiMonth = getGanzhiMonth(date.getMonthValue(), date.getYear());
|
|
|
- cd.ganzhiDay = "甲子"; // 简化
|
|
|
+
|
|
|
+ // 计算日柱(参考 FamilyMemberAttributeService.autoCalculate 的算法)
|
|
|
+ // 参考日:2000-01-01 视为甲子日(stemIdx=0, branchIdx=0)
|
|
|
+ long epochDay = date.toEpochDay();
|
|
|
+ long refEpochDay = LocalDate.of(2000, 1, 1).toEpochDay();
|
|
|
+ long diffDays = epochDay - refEpochDay;
|
|
|
+
|
|
|
+ int dayStemIdx = (int) ((diffDays) % 10);
|
|
|
+ if (dayStemIdx < 0) dayStemIdx += 10;
|
|
|
+ int dayBranchIdx = (int) ((diffDays) % 12);
|
|
|
+ if (dayBranchIdx < 0) dayBranchIdx += 12;
|
|
|
+
|
|
|
+ String dayStem = HEAVENLY_STEMS[dayStemIdx];
|
|
|
+ String dayBranch = EARTHLY_BRANCHES[dayBranchIdx];
|
|
|
+ cd.ganzhiDay = dayStem + dayBranch;
|
|
|
+
|
|
|
return cd;
|
|
|
}
|
|
|
|
|
|
- /**
|
|
|
- * 计算家庭成员的五行得分
|
|
|
+/**
|
|
|
+ * 计算家庭成员的五行得分(聚合所有成员的 wuxingElements)
|
|
|
*/
|
|
|
private Map<String, Integer> calculateFamilyElements(Long familyId) {
|
|
|
- // TODO: 实际项目中需要从数据库读取家庭成员的出生信息(八字)
|
|
|
- // 这里简化实现:均衡分配
|
|
|
Map<String, Integer> elements = new HashMap<>();
|
|
|
- elements.put("木", 20);
|
|
|
- elements.put("火", 20);
|
|
|
- elements.put("土", 20);
|
|
|
- elements.put("金", 20);
|
|
|
- elements.put("水", 20);
|
|
|
+ elements.put("木", 0);
|
|
|
+ elements.put("火", 0);
|
|
|
+ elements.put("土", 0);
|
|
|
+ elements.put("金", 0);
|
|
|
+ elements.put("水", 0);
|
|
|
+
|
|
|
+ // 查询家庭成员
|
|
|
+ List<FamilyMember> members = familyMemberMapper.selectList(
|
|
|
+ new LambdaQueryWrapper<FamilyMember>()
|
|
|
+ .eq(FamilyMember::getFamilyId, familyId)
|
|
|
+ .eq(FamilyMember::getStatus, "active"));
|
|
|
+ if (members == null || members.isEmpty()) {
|
|
|
+ return elements;
|
|
|
+ }
|
|
|
+
|
|
|
+ // key 映射:英文 -> 中文
|
|
|
+ Map<String, String> elementMap = new HashMap<>();
|
|
|
+ elementMap.put("wood", "木");
|
|
|
+ elementMap.put("fire", "火");
|
|
|
+ elementMap.put("earth", "土");
|
|
|
+ elementMap.put("metal", "金");
|
|
|
+ elementMap.put("water", "水");
|
|
|
+
|
|
|
+ int validCount = 0;
|
|
|
+ for (FamilyMember member : members) {
|
|
|
+ // memberType: 先查 child(不可登录成员),再查 parent(可登录成员)
|
|
|
+ FamilyMemberAttributes attrs = familyMemberAttributesMapper.selectOne(
|
|
|
+ new LambdaQueryWrapper<FamilyMemberAttributes>()
|
|
|
+ .eq(FamilyMemberAttributes::getMemberId, member.getId())
|
|
|
+ .eq(FamilyMemberAttributes::getMemberType, "child"));
|
|
|
+ if (attrs == null) {
|
|
|
+ attrs = familyMemberAttributesMapper.selectOne(
|
|
|
+ new LambdaQueryWrapper<FamilyMemberAttributes>()
|
|
|
+ .eq(FamilyMemberAttributes::getMemberId, member.getId())
|
|
|
+ .eq(FamilyMemberAttributes::getMemberType, "parent"));
|
|
|
+ }
|
|
|
+ if (attrs != null && attrs.getWuxingElements() != null) {
|
|
|
+ Map<String, Object> wuxingMap = fromJson(attrs.getWuxingElements());
|
|
|
+ for (Map.Entry<String, Object> entry : wuxingMap.entrySet()) {
|
|
|
+ String cnKey = elementMap.get(entry.getKey());
|
|
|
+ if (cnKey != null) {
|
|
|
+ Long val = (entry.getValue() instanceof Number)
|
|
|
+ ? ((Number) entry.getValue()).longValue()
|
|
|
+ : 0L;
|
|
|
+ elements.put(cnKey, elements.get(cnKey) + val.intValue());
|
|
|
+ }
|
|
|
+ }
|
|
|
+ validCount++;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 取平均值(保留整数)
|
|
|
+ // 取平均值(保留整数)
|
|
|
+ final int memberCount = validCount;
|
|
|
+ if (memberCount > 0) {
|
|
|
+ elements.replaceAll((k, v) -> v / memberCount);
|
|
|
+ }
|
|
|
return elements;
|
|
|
}
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Simple JSON parser for Map<String, Object> (reuse from FamilyMemberAttributeService)
|
|
|
+ */
|
|
|
+ @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 {
|
|
|
+ if (value.contains(".")) {
|
|
|
+ String intStr = value.replace(".", "").replaceAll("0+$", "");
|
|
|
+ map.put(key, intStr.isEmpty() ? 0L : Long.parseLong(intStr));
|
|
|
+ } else {
|
|
|
+ map.put(key, Long.parseLong(value));
|
|
|
+ }
|
|
|
+ } catch (NumberFormatException e) {
|
|
|
+ map.put(key, value);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return map;
|
|
|
+ }
|
|
|
|
|
|
/**
|
|
|
* 根据农历信息和家庭五行确定吉位
|
|
|
@@ -292,11 +402,9 @@ public class FortuneServiceImpl implements FortuneService {
|
|
|
fortune.setFortuneDate(date);
|
|
|
|
|
|
// 占位算法:五行三合
|
|
|
- String[] elements = {"木", "火", "土", "金", "水"};
|
|
|
- // 注意:原写法因运算符优先级问题(+ 高于 >>> 且 ^ 低于两者)会导致移位越界,可能造成 ArrayIndexOutOfBoundsException
|
|
|
+ String dominant = getStrongestElement(calculateFamilyElements(familyId));
|
|
|
+ fortune.setDominantElement(dominant);
|
|
|
int hash = (int)(familyId.longValue() ^ (familyId.longValue() >>> 32) ^ date.toEpochDay());
|
|
|
- int idx = Math.floorMod(Math.abs(hash), elements.length);
|
|
|
- fortune.setDominantElement(elements[idx]);
|
|
|
|
|
|
// 吉位
|
|
|
fortune.setLuckyDirection(calculateLuckyDirection(familyId, date));
|