feat(admin): 添加用户管理相关文件
添加用户管理视图、API和状态管理文件
This commit is contained in:
+612
@@ -0,0 +1,612 @@
|
||||
package io.destiny.biz.util;
|
||||
|
||||
import io.destiny.biz.domain.Almanac;
|
||||
import io.destiny.biz.domain.LunarDate;
|
||||
import io.destiny.biz.enums.*;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
public class AlmanacCalculator {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(AlmanacCalculator.class);
|
||||
|
||||
private static final int[] MONTH_BRANCHES = {
|
||||
3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 1, 2
|
||||
};
|
||||
|
||||
private static final String[][] SUITABLE_ACTIVITIES_BY_JIANCHU = {
|
||||
{"开业", "嫁娶", "出行", "求财", "祭祀", "祈福"},
|
||||
{"扫除", "治病", "破土", "安葬", "出行", "求医"},
|
||||
{"祈福", "祭祀", "安葬", "修造", "动土", "交易"},
|
||||
{"修造", "动土", "交易", "纳财", "签约", "安床"},
|
||||
{"纳财", "签约", "安床", "嫁娶", "开市", "交易"},
|
||||
{"上任", "出行", "求医", "祭祀", "祈福", "安葬"},
|
||||
{"破屋", "坏垣", "拆卸", "破土", "修坟", "安葬"},
|
||||
{"安葬", "破土", "修坟", "祭祀", "祈福", "出行"},
|
||||
{"嫁娶", "开市", "交易", "纳财", "签约", "安床"},
|
||||
{"纳财", "买田", "置业", "交易", "签约", "安床"},
|
||||
{"开业", "开工", "开张", "嫁娶", "出行", "求财"},
|
||||
{"塞穴", "断蚁", "筑堤", "修造", "动土", "交易"}
|
||||
};
|
||||
|
||||
private static final String[][] UNSUITABLE_ACTIVITIES_BY_JIANCHU = {
|
||||
{"安葬", "破土", "修坟", "祭祀", "祈福", "出行"},
|
||||
{"开业", "嫁娶", "出行", "求财", "祭祀", "祈福"},
|
||||
{"开业", "嫁娶", "出行", "求财", "祭祀", "祈福"},
|
||||
{"开业", "嫁娶", "出行", "求财", "祭祀", "祈福"},
|
||||
{"开业", "嫁娶", "出行", "求财", "祭祀", "祈福"},
|
||||
{"开业", "嫁娶", "出行", "求财", "祭祀", "祈福"},
|
||||
{"开业", "嫁娶", "出行", "求财", "祭祀", "祈福"},
|
||||
{"开业", "嫁娶", "出行", "求财", "祭祀", "祈福"},
|
||||
{"安葬", "破土", "修坟", "祭祀", "祈福", "出行"},
|
||||
{"安葬", "破土", "修坟", "祭祀", "祈福", "出行"},
|
||||
{"安葬", "破土", "修坟", "祭祀", "祈福", "出行"},
|
||||
{"开业", "嫁娶", "出行", "求财", "祭祀", "祈福"}
|
||||
};
|
||||
|
||||
private static final int[] CLASH_BRANCHES = {7, 8, 9, 10, 11, 12, 1, 2, 3, 4, 5, 6};
|
||||
|
||||
private static final String[] EVIL_DIRECTIONS = {"南", "东", "北", "西", "南", "东", "北", "西", "南", "东", "北", "西"};
|
||||
|
||||
private static final String[] STAR_GODS = {
|
||||
"青龙", "明堂", "天刑", "朱雀", "金匮", "天德", "白虎", "玉堂", "天牢", "玄武", "司命", "勾陈"
|
||||
};
|
||||
|
||||
private static final String[] NA_YIN_FIVE_ELEMENTS = {
|
||||
"海中金", "海中金", "炉中火", "炉中火", "大林木", "大林木", "路旁土", "路旁土", "剑锋金", "剑锋金",
|
||||
"山头火", "山头火", "涧下水", "涧下水", "城头土", "城头土", "白蜡金", "白蜡金", "杨柳木", "杨柳木",
|
||||
"泉中水", "泉中水", "屋上土", "屋上土", "霹雳火", "霹雳火", "松柏木", "松柏木", "长流水", "长流水",
|
||||
"沙中金", "沙中金", "山下火", "山下火", "平地木", "平地木", "壁上土", "壁上土", "金箔金", "金箔金",
|
||||
"覆灯火", "覆灯火", "天河水", "天河水", "大驿土", "大驿土", "钗钏金", "钗钏金", "桑柘木", "桑柘木",
|
||||
"大溪水", "大溪水", "沙中土", "沙中土", "天上火", "天上火", "石榴木", "石榴木", "大海水", "大海水"
|
||||
};
|
||||
|
||||
private static final String[] PENGZU_STEM_TABOOS = {
|
||||
"甲不开仓财物耗散",
|
||||
"乙不栽植千株不长",
|
||||
"丙不修灶必见灾殃",
|
||||
"丁不剃头头必生疮",
|
||||
"戊不受田田主不祥",
|
||||
"己不破券二比并亡",
|
||||
"庚不经络织机虚张",
|
||||
"辛不合酱主人不尝",
|
||||
"壬不汲水更难提防",
|
||||
"癸不词讼理弱敌强"
|
||||
};
|
||||
|
||||
private static final String[] PENGZU_BRANCH_TABOOS = {
|
||||
"子不问卜自惹祸殃",
|
||||
"丑不冠带主不还乡",
|
||||
"寅不祭祀神鬼不尝",
|
||||
"卯不穿井水泉不香",
|
||||
"辰不哭泣必主重丧",
|
||||
"巳不远行财物伏藏",
|
||||
"午不苫盖屋主更张",
|
||||
"未不服药毒气入肠",
|
||||
"申不安床鬼祟入房",
|
||||
"酉不会客醉坐颠狂",
|
||||
"戌不吃犬作怪上床",
|
||||
"亥不嫁娶不利新郎"
|
||||
};
|
||||
|
||||
private AlmanacCalculator() {
|
||||
throw new UnsupportedOperationException("Utility class");
|
||||
}
|
||||
|
||||
public static Almanac calculateAlmanac(LocalDateTime solarDateTime, LunarDate lunarDate) {
|
||||
log.debug("开始计算黄历 - 阳历:{},农历:{}", solarDateTime, lunarDate);
|
||||
|
||||
Almanac.Builder builder = Almanac.builder()
|
||||
.solarDate(solarDateTime)
|
||||
.lunarDate(lunarDate);
|
||||
|
||||
String jianChu = calculateJianChu(lunarDate);
|
||||
builder.jianChu(jianChu);
|
||||
log.debug("建除十二神:{}", jianChu);
|
||||
|
||||
List<String> suitable = calculateSuitable(jianChu);
|
||||
builder.suitable(suitable);
|
||||
log.debug("宜事项:{}", suitable);
|
||||
|
||||
List<String> unsuitable = calculateUnsuitable(jianChu);
|
||||
builder.unsuitable(unsuitable);
|
||||
log.debug("忌事项:{}", unsuitable);
|
||||
|
||||
String godDirection = calculateGodDirection(solarDateTime);
|
||||
builder.godDirection(godDirection);
|
||||
log.debug("财神方位:{}", godDirection);
|
||||
|
||||
String joyDirection = calculateJoyDirection(solarDateTime);
|
||||
builder.joyDirection(joyDirection);
|
||||
log.debug("喜神方位:{}", joyDirection);
|
||||
|
||||
String fortuneDirection = calculateFortuneDirection(solarDateTime);
|
||||
builder.fortuneDirection(fortuneDirection);
|
||||
log.debug("福神方位:{}", fortuneDirection);
|
||||
|
||||
String nobleDirection = calculateNobleDirection(solarDateTime);
|
||||
builder.nobleDirection(nobleDirection);
|
||||
log.debug("贵人方位:{}", nobleDirection);
|
||||
|
||||
String clash = calculateClash(lunarDate);
|
||||
builder.clash(clash);
|
||||
log.debug("冲:{}", clash);
|
||||
|
||||
String evil = calculateEvil(lunarDate);
|
||||
builder.evil(evil);
|
||||
log.debug("煞:{}", evil);
|
||||
|
||||
String starGod = calculateStarGod(solarDateTime);
|
||||
builder.starGod(starGod);
|
||||
log.debug("值日星神:{}", starGod);
|
||||
|
||||
String naYin = calculateNaYin(solarDateTime);
|
||||
builder.naYin(naYin);
|
||||
log.debug("纳音五行:{}", naYin);
|
||||
|
||||
String fetusGod = calculateFetusGod(lunarDate);
|
||||
builder.fetusGod(fetusGod);
|
||||
log.debug("胎神方位:{}", fetusGod);
|
||||
|
||||
String pengzuTaboo = calculatePengzuTaboo(solarDateTime);
|
||||
builder.pengzuTaboo(pengzuTaboo);
|
||||
log.debug("彭祖百忌:{}", pengzuTaboo);
|
||||
|
||||
Almanac almanac = builder.build();
|
||||
log.debug("黄历计算完成:{}", almanac);
|
||||
return almanac;
|
||||
}
|
||||
|
||||
public static String calculateJianChu(LunarDate lunarDate) {
|
||||
log.debug("计算建除十二神 - 农历:{}", lunarDate);
|
||||
|
||||
if (lunarDate == null) {
|
||||
log.error("农历日期不能为空");
|
||||
throw new IllegalArgumentException("农历日期不能为空");
|
||||
}
|
||||
|
||||
int monthBranch = parseLunarMonthBranch(lunarDate.getLunarMonth());
|
||||
int dayNumber = parseLunarDayNumber(lunarDate.getLunarDay());
|
||||
|
||||
int jianChuIndex = (monthBranch + dayNumber - 1) % 12;
|
||||
if (jianChuIndex == 0) {
|
||||
jianChuIndex = 12;
|
||||
}
|
||||
|
||||
JianChuDeity jianChuDeity = JianChuDeity.fromIndex(jianChuIndex);
|
||||
String result = jianChuDeity.getName();
|
||||
|
||||
log.debug("建除十二神计算结果:{}", result);
|
||||
return result;
|
||||
}
|
||||
|
||||
private static int parseLunarMonthBranch(String lunarMonth) {
|
||||
try {
|
||||
int monthNum = Integer.parseInt(lunarMonth);
|
||||
if (monthNum < 1 || monthNum > 12) {
|
||||
log.error("农历月份超出范围:{}", lunarMonth);
|
||||
throw new IllegalArgumentException("农历月份超出范围:" + lunarMonth);
|
||||
}
|
||||
return MONTH_BRANCHES[monthNum - 1];
|
||||
} catch (NumberFormatException e) {
|
||||
log.error("无法解析农历月份:{}", lunarMonth);
|
||||
throw new IllegalArgumentException("无法解析农历月份:" + lunarMonth);
|
||||
}
|
||||
}
|
||||
|
||||
private static int parseLunarDayNumber(String lunarDay) {
|
||||
try {
|
||||
int dayNum = Integer.parseInt(lunarDay);
|
||||
if (dayNum < 1 || dayNum > 30) {
|
||||
log.error("农历日期超出范围:{}", lunarDay);
|
||||
throw new IllegalArgumentException("农历日期超出范围:" + lunarDay);
|
||||
}
|
||||
return dayNum;
|
||||
} catch (NumberFormatException e) {
|
||||
log.error("无法解析农历日期:{}", lunarDay);
|
||||
throw new IllegalArgumentException("无法解析农历日期:" + lunarDay);
|
||||
}
|
||||
}
|
||||
|
||||
public static List<String> calculateSuitable(String jianChu) {
|
||||
log.debug("计算宜事项 - 建除十二神:{}", jianChu);
|
||||
|
||||
if (jianChu == null || jianChu.isEmpty()) {
|
||||
log.error("建除十二神不能为空");
|
||||
throw new IllegalArgumentException("建除十二神不能为空");
|
||||
}
|
||||
|
||||
JianChuDeity jianChuDeity = JianChuDeity.fromName(jianChu);
|
||||
int index = jianChuDeity.getIndex() - 1;
|
||||
|
||||
String[] suitableActivities = SUITABLE_ACTIVITIES_BY_JIANCHU[index];
|
||||
List<String> result = new ArrayList<>(Arrays.asList(suitableActivities));
|
||||
|
||||
log.debug("宜事项计算结果:{}", result);
|
||||
return result;
|
||||
}
|
||||
|
||||
public static List<String> calculateUnsuitable(String jianChu) {
|
||||
log.debug("计算忌事项 - 建除十二神:{}", jianChu);
|
||||
|
||||
if (jianChu == null || jianChu.isEmpty()) {
|
||||
log.error("建除十二神不能为空");
|
||||
throw new IllegalArgumentException("建除十二神不能为空");
|
||||
}
|
||||
|
||||
JianChuDeity jianChuDeity = JianChuDeity.fromName(jianChu);
|
||||
int index = jianChuDeity.getIndex() - 1;
|
||||
|
||||
String[] unsuitableActivities = UNSUITABLE_ACTIVITIES_BY_JIANCHU[index];
|
||||
List<String> result = new ArrayList<>(Arrays.asList(unsuitableActivities));
|
||||
|
||||
log.debug("忌事项计算结果:{}", result);
|
||||
return result;
|
||||
}
|
||||
|
||||
public static String calculateGodDirection(LocalDateTime solarDateTime) {
|
||||
log.debug("计算财神方位 - 阳历:{}", solarDateTime);
|
||||
|
||||
if (solarDateTime == null) {
|
||||
log.error("阳历日期不能为空");
|
||||
throw new IllegalArgumentException("阳历日期不能为空");
|
||||
}
|
||||
|
||||
int dayStemIndex = getDayStemIndex(solarDateTime);
|
||||
String result;
|
||||
|
||||
switch (dayStemIndex) {
|
||||
case 1:
|
||||
case 2:
|
||||
result = "东北";
|
||||
break;
|
||||
case 3:
|
||||
case 4:
|
||||
result = "正西";
|
||||
break;
|
||||
case 5:
|
||||
case 6:
|
||||
result = "正北";
|
||||
break;
|
||||
case 7:
|
||||
case 8:
|
||||
result = "正东";
|
||||
break;
|
||||
case 9:
|
||||
case 10:
|
||||
result = "正南";
|
||||
break;
|
||||
default:
|
||||
result = "";
|
||||
}
|
||||
|
||||
log.debug("财神方位计算结果:{}", result);
|
||||
return result;
|
||||
}
|
||||
|
||||
public static String calculateJoyDirection(LocalDateTime solarDateTime) {
|
||||
log.debug("计算喜神方位 - 阳历:{}", solarDateTime);
|
||||
|
||||
if (solarDateTime == null) {
|
||||
log.error("阳历日期不能为空");
|
||||
throw new IllegalArgumentException("阳历日期不能为空");
|
||||
}
|
||||
|
||||
int dayStemIndex = getDayStemIndex(solarDateTime);
|
||||
String result;
|
||||
|
||||
switch (dayStemIndex) {
|
||||
case 1:
|
||||
case 3:
|
||||
case 5:
|
||||
case 7:
|
||||
case 9:
|
||||
result = "正北";
|
||||
break;
|
||||
case 2:
|
||||
case 4:
|
||||
case 6:
|
||||
case 8:
|
||||
case 10:
|
||||
result = "东北";
|
||||
break;
|
||||
default:
|
||||
result = "";
|
||||
}
|
||||
|
||||
log.debug("喜神方位计算结果:{}", result);
|
||||
return result;
|
||||
}
|
||||
|
||||
public static String calculateFortuneDirection(LocalDateTime solarDateTime) {
|
||||
log.debug("计算福神方位 - 阳历:{}", solarDateTime);
|
||||
|
||||
if (solarDateTime == null) {
|
||||
log.error("阳历日期不能为空");
|
||||
throw new IllegalArgumentException("阳历日期不能为空");
|
||||
}
|
||||
|
||||
int dayStemIndex = getDayStemIndex(solarDateTime);
|
||||
String result;
|
||||
|
||||
switch (dayStemIndex) {
|
||||
case 1:
|
||||
case 2:
|
||||
result = "正北";
|
||||
break;
|
||||
case 3:
|
||||
case 4:
|
||||
result = "西北";
|
||||
break;
|
||||
case 5:
|
||||
case 7:
|
||||
case 8:
|
||||
result = "西南";
|
||||
break;
|
||||
case 6:
|
||||
result = "正南";
|
||||
break;
|
||||
case 9:
|
||||
case 10:
|
||||
result = "东南";
|
||||
break;
|
||||
default:
|
||||
result = "";
|
||||
}
|
||||
|
||||
log.debug("福神方位计算结果:{}", result);
|
||||
return result;
|
||||
}
|
||||
|
||||
public static String calculateNobleDirection(LocalDateTime solarDateTime) {
|
||||
log.debug("计算贵人方位 - 阳历:{}", solarDateTime);
|
||||
|
||||
if (solarDateTime == null) {
|
||||
log.error("阳历日期不能为空");
|
||||
throw new IllegalArgumentException("阳历日期不能为空");
|
||||
}
|
||||
|
||||
int dayStemIndex = getDayStemIndex(solarDateTime);
|
||||
String result;
|
||||
|
||||
switch (dayStemIndex) {
|
||||
case 1:
|
||||
result = "丑、未";
|
||||
break;
|
||||
case 2:
|
||||
result = "子、申";
|
||||
break;
|
||||
case 3:
|
||||
case 4:
|
||||
result = "亥、酉";
|
||||
break;
|
||||
case 5:
|
||||
case 6:
|
||||
result = "丑、未";
|
||||
break;
|
||||
case 7:
|
||||
case 8:
|
||||
result = "午、寅";
|
||||
break;
|
||||
case 9:
|
||||
case 10:
|
||||
result = "卯、巳";
|
||||
break;
|
||||
default:
|
||||
result = "";
|
||||
}
|
||||
|
||||
log.debug("贵人方位计算结果:{}", result);
|
||||
return result;
|
||||
}
|
||||
|
||||
private static int getDayStemIndex(LocalDateTime solarDateTime) {
|
||||
int year = solarDateTime.getYear();
|
||||
int month = solarDateTime.getMonthValue();
|
||||
int day = solarDateTime.getDayOfMonth();
|
||||
|
||||
int totalDays = calculateTotalDays(year, month, day);
|
||||
int dayStemIndex = (totalDays + 3) % 10;
|
||||
if (dayStemIndex == 0) {
|
||||
dayStemIndex = 10;
|
||||
}
|
||||
|
||||
return dayStemIndex;
|
||||
}
|
||||
|
||||
private static int calculateTotalDays(int year, int month, int day) {
|
||||
int totalDays = 0;
|
||||
|
||||
for (int y = 1900; y < year; y++) {
|
||||
totalDays += isLeapYear(y) ? 366 : 365;
|
||||
}
|
||||
|
||||
int[] daysInMonth = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
|
||||
if (isLeapYear(year)) {
|
||||
daysInMonth[1] = 29;
|
||||
}
|
||||
|
||||
for (int m = 1; m < month; m++) {
|
||||
totalDays += daysInMonth[m - 1];
|
||||
}
|
||||
|
||||
totalDays += day;
|
||||
|
||||
return totalDays;
|
||||
}
|
||||
|
||||
private static boolean isLeapYear(int year) {
|
||||
return (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0);
|
||||
}
|
||||
|
||||
public static String calculateClash(LunarDate lunarDate) {
|
||||
log.debug("计算冲 - 农历:{}", lunarDate);
|
||||
|
||||
if (lunarDate == null) {
|
||||
log.error("农历日期不能为空");
|
||||
throw new IllegalArgumentException("农历日期不能为空");
|
||||
}
|
||||
|
||||
int dayBranchIndex = getDayBranchIndex(lunarDate.getLunarDay());
|
||||
int clashBranchIndex = CLASH_BRANCHES[dayBranchIndex - 1];
|
||||
|
||||
EarthlyBranch clashBranch = EarthlyBranch.fromIndex(clashBranchIndex);
|
||||
String result = clashBranch.getName();
|
||||
|
||||
log.debug("冲计算结果:{}", result);
|
||||
return result;
|
||||
}
|
||||
|
||||
public static String calculateEvil(LunarDate lunarDate) {
|
||||
log.debug("计算煞 - 农历:{}", lunarDate);
|
||||
|
||||
if (lunarDate == null) {
|
||||
log.error("农历日期不能为空");
|
||||
throw new IllegalArgumentException("农历日期不能为空");
|
||||
}
|
||||
|
||||
int dayBranchIndex = getDayBranchIndex(lunarDate.getLunarDay());
|
||||
String result = EVIL_DIRECTIONS[dayBranchIndex - 1];
|
||||
|
||||
log.debug("煞计算结果:{}", result);
|
||||
return result;
|
||||
}
|
||||
|
||||
private static int getDayBranchIndex(String lunarDay) {
|
||||
int dayNumber = parseLunarDayNumber(lunarDay);
|
||||
int dayBranchIndex = (dayNumber + 2) % 12;
|
||||
if (dayBranchIndex == 0) {
|
||||
dayBranchIndex = 12;
|
||||
}
|
||||
return dayBranchIndex;
|
||||
}
|
||||
|
||||
public static String calculateStarGod(LocalDateTime solarDateTime) {
|
||||
log.debug("计算值日星神 - 阳历:{}", solarDateTime);
|
||||
|
||||
if (solarDateTime == null) {
|
||||
log.error("阳历日期不能为空");
|
||||
throw new IllegalArgumentException("阳历日期不能为空");
|
||||
}
|
||||
|
||||
int dayBranchIndex = getDayBranchIndexFromSolar(solarDateTime);
|
||||
String result = STAR_GODS[dayBranchIndex - 1];
|
||||
|
||||
log.debug("值日星神计算结果:{}", result);
|
||||
return result;
|
||||
}
|
||||
|
||||
private static int getDayBranchIndexFromSolar(LocalDateTime solarDateTime) {
|
||||
int year = solarDateTime.getYear();
|
||||
int month = solarDateTime.getMonthValue();
|
||||
int day = solarDateTime.getDayOfMonth();
|
||||
|
||||
int totalDays = calculateTotalDays(year, month, day);
|
||||
int dayBranchIndex = (totalDays + 1) % 12;
|
||||
if (dayBranchIndex == 0) {
|
||||
dayBranchIndex = 12;
|
||||
}
|
||||
|
||||
return dayBranchIndex;
|
||||
}
|
||||
|
||||
public static String calculateNaYin(LocalDateTime solarDateTime) {
|
||||
log.debug("计算纳音五行 - 阳历:{}", solarDateTime);
|
||||
|
||||
if (solarDateTime == null) {
|
||||
log.error("阳历日期不能为空");
|
||||
throw new IllegalArgumentException("阳历日期不能为空");
|
||||
}
|
||||
|
||||
int dayStemIndex = getDayStemIndex(solarDateTime);
|
||||
int dayBranchIndex = getDayBranchIndexFromSolar(solarDateTime);
|
||||
|
||||
int naYinIndex = (dayStemIndex - 1) * 6 + (dayBranchIndex - 1) / 2;
|
||||
|
||||
String result = NA_YIN_FIVE_ELEMENTS[naYinIndex];
|
||||
|
||||
log.debug("纳音五行计算结果:{}", result);
|
||||
return result;
|
||||
}
|
||||
|
||||
public static String calculateFetusGod(LunarDate lunarDate) {
|
||||
log.debug("计算胎神方位 - 农历:{}", lunarDate);
|
||||
|
||||
if (lunarDate == null) {
|
||||
log.error("农历日期不能为空");
|
||||
throw new IllegalArgumentException("农历日期不能为空");
|
||||
}
|
||||
|
||||
int dayBranchIndex = getDayBranchIndex(lunarDate.getLunarDay());
|
||||
String result;
|
||||
|
||||
switch (dayBranchIndex) {
|
||||
case 1:
|
||||
result = "房床北";
|
||||
break;
|
||||
case 2:
|
||||
result = "厕厕东北";
|
||||
break;
|
||||
case 3:
|
||||
result = "炉灶东北";
|
||||
break;
|
||||
case 4:
|
||||
result = "大门东";
|
||||
break;
|
||||
case 5:
|
||||
result = "栖床东南";
|
||||
break;
|
||||
case 6:
|
||||
result = "房床东南";
|
||||
break;
|
||||
case 7:
|
||||
result = "房床南";
|
||||
break;
|
||||
case 8:
|
||||
result = "厕厕西南";
|
||||
break;
|
||||
case 9:
|
||||
result = "炉灶西南";
|
||||
break;
|
||||
case 10:
|
||||
result = "大门西";
|
||||
break;
|
||||
case 11:
|
||||
result = "栖床西北";
|
||||
break;
|
||||
case 12:
|
||||
result = "房床西北";
|
||||
break;
|
||||
default:
|
||||
result = "";
|
||||
}
|
||||
|
||||
log.debug("胎神方位计算结果:{}", result);
|
||||
return result;
|
||||
}
|
||||
|
||||
public static String calculatePengzuTaboo(LocalDateTime solarDateTime) {
|
||||
log.debug("计算彭祖百忌 - 阳历:{}", solarDateTime);
|
||||
|
||||
if (solarDateTime == null) {
|
||||
log.error("阳历日期不能为空");
|
||||
throw new IllegalArgumentException("阳历日期不能为空");
|
||||
}
|
||||
|
||||
int dayStemIndex = getDayStemIndex(solarDateTime);
|
||||
int dayBranchIndex = getDayBranchIndexFromSolar(solarDateTime);
|
||||
|
||||
String stemTaboo = PENGZU_STEM_TABOOS[dayStemIndex - 1];
|
||||
String branchTaboo = PENGZU_BRANCH_TABOOS[dayBranchIndex - 1];
|
||||
|
||||
String result = stemTaboo + "," + branchTaboo;
|
||||
|
||||
log.debug("彭祖百忌计算结果:{}", result);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
+278
@@ -0,0 +1,278 @@
|
||||
package io.destiny.biz.util;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZoneId;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.time.format.DateTimeParseException;
|
||||
import java.time.format.TextStyle;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
public class DateTimeFormatUtil {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(DateTimeFormatUtil.class);
|
||||
|
||||
private static final String DEFAULT_TIMEZONE = "Asia/Shanghai";
|
||||
private static final String DEFAULT_DATE_FORMAT = "yyyy-MM-dd";
|
||||
private static final String DEFAULT_DATETIME_FORMAT = "yyyy-MM-dd HH:mm:ss";
|
||||
|
||||
private static final Map<String, DateTimeFormatter> FORMATTER_CACHE = new ConcurrentHashMap<>();
|
||||
|
||||
static {
|
||||
log.debug("初始化日期时间格式工具类");
|
||||
initializeFormatters();
|
||||
log.debug("日期时间格式工具类初始化完成");
|
||||
}
|
||||
|
||||
private static void initializeFormatters() {
|
||||
log.debug("初始化日期时间格式化器缓存");
|
||||
FORMATTER_CACHE.put("yyyy-MM-dd", DateTimeFormatter.ofPattern("yyyy-MM-dd"));
|
||||
FORMATTER_CACHE.put("yyyy/MM/dd", DateTimeFormatter.ofPattern("yyyy/MM/dd"));
|
||||
FORMATTER_CACHE.put("yyyy年MM月dd日", DateTimeFormatter.ofPattern("yyyy年MM月dd日"));
|
||||
FORMATTER_CACHE.put("yyyyMMdd", DateTimeFormatter.ofPattern("yyyyMMdd"));
|
||||
FORMATTER_CACHE.put("yyyy-MM-dd HH:mm:ss", DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
|
||||
FORMATTER_CACHE.put("yyyy/MM/dd HH:mm:ss", DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss"));
|
||||
FORMATTER_CACHE.put("yyyy年MM月dd日 HH时mm分ss秒", DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH时mm分ss秒"));
|
||||
FORMATTER_CACHE.put("yyyyMMddHHmmss", DateTimeFormatter.ofPattern("yyyyMMddHHmmss"));
|
||||
FORMATTER_CACHE.put("MM-dd", DateTimeFormatter.ofPattern("MM-dd"));
|
||||
FORMATTER_CACHE.put("MM月dd日", DateTimeFormatter.ofPattern("MM月dd日"));
|
||||
log.debug("日期时间格式化器缓存初始化完成,共{}个格式", FORMATTER_CACHE.size());
|
||||
}
|
||||
|
||||
public static String formatDate(LocalDate date) {
|
||||
return formatDate(date, DEFAULT_DATE_FORMAT);
|
||||
}
|
||||
|
||||
public static String formatDate(LocalDate date, String pattern) {
|
||||
log.debug("格式化日期:{},格式:{}", date, pattern);
|
||||
if (date == null) {
|
||||
log.debug("日期为空,返回null");
|
||||
return null;
|
||||
}
|
||||
|
||||
DateTimeFormatter formatter = getFormatter(pattern);
|
||||
String result = date.format(formatter);
|
||||
log.debug("日期格式化完成:{}", result);
|
||||
return result;
|
||||
}
|
||||
|
||||
public static String formatDateTime(LocalDateTime dateTime) {
|
||||
return formatDateTime(dateTime, DEFAULT_DATETIME_FORMAT);
|
||||
}
|
||||
|
||||
public static String formatDateTime(LocalDateTime dateTime, String pattern) {
|
||||
log.debug("格式化日期时间:{},格式:{}", dateTime, pattern);
|
||||
if (dateTime == null) {
|
||||
log.debug("日期时间为空,返回null");
|
||||
return null;
|
||||
}
|
||||
|
||||
DateTimeFormatter formatter = getFormatter(pattern);
|
||||
String result = dateTime.format(formatter);
|
||||
log.debug("日期时间格式化完成:{}", result);
|
||||
return result;
|
||||
}
|
||||
|
||||
public static String formatDateWithTimezone(LocalDate date, String timeZone) {
|
||||
log.debug("格式化日期(带时区):{},时区:{}", date, timeZone);
|
||||
if (date == null) {
|
||||
log.debug("日期为空,返回null");
|
||||
return null;
|
||||
}
|
||||
|
||||
ZoneId zoneId = parseZoneId(timeZone);
|
||||
String result = formatDate(date) + " (" + zoneId.getId() + ")";
|
||||
log.debug("日期格式化完成(带时区):{}", result);
|
||||
return result;
|
||||
}
|
||||
|
||||
public static String formatDateTimeWithTimezone(LocalDateTime dateTime, String timeZone) {
|
||||
log.debug("格式化日期时间(带时区):{},时区:{}", dateTime, timeZone);
|
||||
if (dateTime == null) {
|
||||
log.debug("日期时间为空,返回null");
|
||||
return null;
|
||||
}
|
||||
|
||||
ZoneId zoneId = parseZoneId(timeZone);
|
||||
ZonedDateTime zonedDateTime = dateTime.atZone(zoneId);
|
||||
String result = zonedDateTime.format(getFormatter(DEFAULT_DATETIME_FORMAT)) + " (" + zoneId.getId() + ")";
|
||||
log.debug("日期时间格式化完成(带时区):{}", result);
|
||||
return result;
|
||||
}
|
||||
|
||||
public static LocalDate parseDate(String dateStr) {
|
||||
return parseDate(dateStr, DEFAULT_DATE_FORMAT);
|
||||
}
|
||||
|
||||
public static LocalDate parseDate(String dateStr, String pattern) {
|
||||
log.debug("解析日期字符串:{},格式:{}", dateStr, pattern);
|
||||
if (dateStr == null || dateStr.trim().isEmpty()) {
|
||||
log.debug("日期字符串为空,返回null");
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
DateTimeFormatter formatter = getFormatter(pattern);
|
||||
LocalDate result = LocalDate.parse(dateStr, formatter);
|
||||
log.debug("日期解析完成:{}", result);
|
||||
return result;
|
||||
} catch (DateTimeParseException e) {
|
||||
log.error("日期解析失败:{},格式:{},错误:{}", dateStr, pattern, e.getMessage());
|
||||
throw new IllegalArgumentException("日期格式错误:" + dateStr + ",期望格式:" + pattern, e);
|
||||
}
|
||||
}
|
||||
|
||||
public static LocalDateTime parseDateTime(String dateTimeStr) {
|
||||
return parseDateTime(dateTimeStr, DEFAULT_DATETIME_FORMAT);
|
||||
}
|
||||
|
||||
public static LocalDateTime parseDateTime(String dateTimeStr, String pattern) {
|
||||
log.debug("解析日期时间字符串:{},格式:{}", dateTimeStr, pattern);
|
||||
if (dateTimeStr == null || dateTimeStr.trim().isEmpty()) {
|
||||
log.debug("日期时间字符串为空,返回null");
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
DateTimeFormatter formatter = getFormatter(pattern);
|
||||
LocalDateTime result = LocalDateTime.parse(dateTimeStr, formatter);
|
||||
log.debug("日期时间解析完成:{}", result);
|
||||
return result;
|
||||
} catch (DateTimeParseException e) {
|
||||
log.error("日期时间解析失败:{},格式:{},错误:{}", dateTimeStr, pattern, e.getMessage());
|
||||
throw new IllegalArgumentException("日期时间格式错误:" + dateTimeStr + ",期望格式:" + pattern, e);
|
||||
}
|
||||
}
|
||||
|
||||
public static LocalDateTime convertTimezone(LocalDateTime dateTime, String fromTimeZone, String toTimeZone) {
|
||||
log.debug("转换时区:{},从{}到{}", dateTime, fromTimeZone, toTimeZone);
|
||||
if (dateTime == null) {
|
||||
log.debug("日期时间为空,返回null");
|
||||
return null;
|
||||
}
|
||||
|
||||
ZoneId fromZoneId = parseZoneId(fromTimeZone);
|
||||
ZoneId toZoneId = parseZoneId(toTimeZone);
|
||||
|
||||
ZonedDateTime fromZoned = dateTime.atZone(fromZoneId);
|
||||
ZonedDateTime toZoned = fromZoned.withZoneSameInstant(toZoneId);
|
||||
LocalDateTime result = toZoned.toLocalDateTime();
|
||||
|
||||
log.debug("时区转换完成:{} -> {}", dateTime, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
public static String formatDateLocalized(LocalDate date, String timeZone, Locale locale) {
|
||||
log.debug("格式化本地化日期:{},时区:{},语言环境:{}", date, timeZone, locale);
|
||||
if (date == null) {
|
||||
log.debug("日期为空,返回null");
|
||||
return null;
|
||||
}
|
||||
|
||||
String year = String.valueOf(date.getYear());
|
||||
String month = date.getMonth().getDisplayName(TextStyle.FULL, locale);
|
||||
String day = String.valueOf(date.getDayOfMonth());
|
||||
String weekday = date.getDayOfWeek().getDisplayName(TextStyle.FULL, locale);
|
||||
|
||||
String result = String.format("%s年%s月%s日 %s", year, month, day, weekday);
|
||||
log.debug("本地化日期格式化完成:{}", result);
|
||||
return result;
|
||||
}
|
||||
|
||||
public static boolean isValidDateRange(LocalDate startDate, LocalDate endDate) {
|
||||
log.debug("验证日期范围:{} - {}", startDate, endDate);
|
||||
if (startDate == null || endDate == null) {
|
||||
log.debug("开始日期或结束日期为空,返回false");
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean result = !startDate.isAfter(endDate);
|
||||
log.debug("日期范围验证结果:{}", result);
|
||||
return result;
|
||||
}
|
||||
|
||||
public static int daysBetween(LocalDate startDate, LocalDate endDate) {
|
||||
log.debug("计算日期间隔:{} - {}", startDate, endDate);
|
||||
if (startDate == null || endDate == null) {
|
||||
log.debug("开始日期或结束日期为空,返回0");
|
||||
return 0;
|
||||
}
|
||||
|
||||
long days = java.time.temporal.ChronoUnit.DAYS.between(startDate, endDate);
|
||||
int result = (int) Math.abs(days);
|
||||
log.debug("日期间隔计算完成:{}天", result);
|
||||
return result;
|
||||
}
|
||||
|
||||
public static boolean isSameDay(LocalDate date1, LocalDate date2) {
|
||||
log.debug("判断是否为同一天:{} 和 {}", date1, date2);
|
||||
if (date1 == null || date2 == null) {
|
||||
log.debug("任一日期为空,返回false");
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean result = date1.isEqual(date2);
|
||||
log.debug("同一天判断结果:{}", result);
|
||||
return result;
|
||||
}
|
||||
|
||||
public static boolean isToday(LocalDate date) {
|
||||
log.debug("判断是否为今天:{}", date);
|
||||
if (date == null) {
|
||||
log.debug("日期为空,返回false");
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean result = date.isEqual(LocalDate.now());
|
||||
log.debug("今天判断结果:{}", result);
|
||||
return result;
|
||||
}
|
||||
|
||||
public static boolean isFutureDate(LocalDate date) {
|
||||
log.debug("判断是否为未来日期:{}", date);
|
||||
if (date == null) {
|
||||
log.debug("日期为空,返回false");
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean result = date.isAfter(LocalDate.now());
|
||||
log.debug("未来日期判断结果:{}", result);
|
||||
return result;
|
||||
}
|
||||
|
||||
public static boolean isPastDate(LocalDate date) {
|
||||
log.debug("判断是否为过去日期:{}", date);
|
||||
if (date == null) {
|
||||
log.debug("日期为空,返回false");
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean result = date.isBefore(LocalDate.now());
|
||||
log.debug("过去日期判断结果:{}", result);
|
||||
return result;
|
||||
}
|
||||
|
||||
private static DateTimeFormatter getFormatter(String pattern) {
|
||||
return FORMATTER_CACHE.computeIfAbsent(pattern, DateTimeFormatter::ofPattern);
|
||||
}
|
||||
|
||||
private static ZoneId parseZoneId(String timeZone) {
|
||||
if (timeZone == null || timeZone.trim().isEmpty()) {
|
||||
log.debug("时区为空,使用默认时区:{}", DEFAULT_TIMEZONE);
|
||||
return ZoneId.of(DEFAULT_TIMEZONE);
|
||||
}
|
||||
|
||||
try {
|
||||
return ZoneId.of(timeZone);
|
||||
} catch (Exception e) {
|
||||
log.error("时区解析失败:{},使用默认时区:{}", timeZone, DEFAULT_TIMEZONE);
|
||||
return ZoneId.of(DEFAULT_TIMEZONE);
|
||||
}
|
||||
}
|
||||
}
|
||||
+1300
File diff suppressed because it is too large
Load Diff
+490
@@ -0,0 +1,490 @@
|
||||
package io.destiny.biz.util;
|
||||
|
||||
import io.destiny.biz.domain.Palace;
|
||||
import io.destiny.biz.domain.StarInfo;
|
||||
import io.destiny.biz.domain.ZiweiChart;
|
||||
import io.destiny.biz.enums.MajorStar;
|
||||
import io.destiny.biz.enums.PalaceType;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 格局识别工具类
|
||||
* 用于识别紫微斗数中的各种格局
|
||||
* 通过分析命盘中的星曜组合,识别出特殊的格局并给出评分
|
||||
*
|
||||
* @author 张翔
|
||||
* @date 2025-12-29
|
||||
*/
|
||||
public class PatternRecognitionUtil {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(PatternRecognitionUtil.class);
|
||||
|
||||
/**
|
||||
* 格局内部类,用于表示识别到的格局信息
|
||||
*/
|
||||
public static class Pattern {
|
||||
/** 格局名称 */
|
||||
private String name;
|
||||
/** 格局描述 */
|
||||
private String description;
|
||||
/** 宫位类型 */
|
||||
private PalaceType palaceType;
|
||||
/** 涉及的主星列表 */
|
||||
private List<MajorStar> stars;
|
||||
/** 格局评分 */
|
||||
private int score;
|
||||
|
||||
/**
|
||||
* 构造函数
|
||||
*
|
||||
* @param name 格局名称
|
||||
* @param description 格局描述
|
||||
* @param palaceType 宫位类型
|
||||
* @param stars 涉及的主星列表
|
||||
* @param score 格局评分
|
||||
*/
|
||||
public Pattern(String name, String description, PalaceType palaceType, List<MajorStar> stars, int score) {
|
||||
this.name = name;
|
||||
this.description = description;
|
||||
this.palaceType = palaceType;
|
||||
this.stars = stars;
|
||||
this.score = score;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取格局名称
|
||||
*
|
||||
* @return 格局名称
|
||||
*/
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取格局描述
|
||||
*
|
||||
* @return 格局描述
|
||||
*/
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取宫位类型
|
||||
*
|
||||
* @return 宫位类型
|
||||
*/
|
||||
public PalaceType getPalaceType() {
|
||||
return palaceType;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取涉及的主星列表
|
||||
*
|
||||
* @return 主星列表
|
||||
*/
|
||||
public List<MajorStar> getStars() {
|
||||
return stars;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取格局评分
|
||||
*
|
||||
* @return 格局评分
|
||||
*/
|
||||
public int getScore() {
|
||||
return score;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 识别命盘中的格局
|
||||
*
|
||||
* @param chart 紫微盘对象
|
||||
* @return 识别到的格局列表
|
||||
*/
|
||||
public static List<Pattern> recognizePatterns(ZiweiChart chart) {
|
||||
log.info("【格局识别】开始识别命盘格局");
|
||||
if (chart == null) {
|
||||
log.error("【格局识别错误】命盘对象为空,无法识别格局");
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
log.debug("【格局识别】开始分析命盘中的格局");
|
||||
List<Pattern> patterns = new ArrayList<>();
|
||||
|
||||
Palace mingGong = chart.getPalaceByType(PalaceType.MING.getName());
|
||||
if (mingGong != null) {
|
||||
List<MajorStar> mingGongStars = getMajorStars(mingGong);
|
||||
log.debug("【格局识别】命宫主星:{}", mingGongStars);
|
||||
|
||||
if (mingGongStars.contains(MajorStar.ZIWEI) && mingGongStars.contains(MajorStar.TIANFU)) {
|
||||
int score = calculatePatternScore(mingGongStars, PalaceType.MING, chart);
|
||||
patterns.add(new Pattern("紫府同宫格", "紫微星与天府星同宫,主富贵双全,格局极佳", PalaceType.MING, mingGongStars, score));
|
||||
log.info("识别到格局:紫府同宫格(评分:{})", score);
|
||||
} else if (mingGongStars.contains(MajorStar.TAIYANG) && mingGongStars.contains(MajorStar.TAIYIN)) {
|
||||
int score = calculatePatternScore(mingGongStars, PalaceType.MING, chart);
|
||||
patterns.add(new Pattern("日月同宫格", "太阳星与太阴星同宫,主光明磊落,名声远扬", PalaceType.MING, mingGongStars, score));
|
||||
log.info("识别到格局:日月同宫格(评分:{})", score);
|
||||
} else if (mingGongStars.contains(MajorStar.TIANJI) && mingGongStars.contains(MajorStar.TAIYIN) && mingGongStars.contains(MajorStar.TIANLIANG)) {
|
||||
int score = calculatePatternScore(mingGongStars, PalaceType.MING, chart);
|
||||
patterns.add(new Pattern("机月同梁格", "天机、太阴、天梁三星同宫,主智慧谋略,稳重踏实", PalaceType.MING, mingGongStars, score));
|
||||
log.info("识别到格局:机月同梁格(评分:{})", score);
|
||||
} else if (mingGongStars.contains(MajorStar.QISHA) && mingGongStars.contains(MajorStar.POJUN) && mingGongStars.contains(MajorStar.TANLANG)) {
|
||||
int score = calculatePatternScore(mingGongStars, PalaceType.MING, chart);
|
||||
patterns.add(new Pattern("杀破狼格", "七杀、破军、贪狼三星同宫,主变动开创,消耗较大", PalaceType.MING, mingGongStars, score));
|
||||
log.info("识别到格局:杀破狼格(评分:{})", score);
|
||||
} else if (mingGongStars.contains(MajorStar.ZIWEI) && mingGongStars.size() == 1) {
|
||||
int score = calculatePatternScore(mingGongStars, PalaceType.MING, chart);
|
||||
patterns.add(new Pattern("紫微独坐格", "紫微星独坐命宫,主权威尊贵,领导才能强", PalaceType.MING, mingGongStars, score));
|
||||
log.info("识别到格局:紫微独坐格(评分:{})", score);
|
||||
} else if (mingGongStars.contains(MajorStar.TIANFU) && mingGongStars.size() == 1) {
|
||||
int score = calculatePatternScore(mingGongStars, PalaceType.MING, chart);
|
||||
patterns.add(new Pattern("天府独坐格", "天府星独坐命宫,主稳重保守,财富丰厚", PalaceType.MING, mingGongStars, score));
|
||||
log.info("识别到格局:天府独坐格(评分:{})", score);
|
||||
} else if (mingGongStars.contains(MajorStar.TAIYANG) && mingGongStars.size() == 1) {
|
||||
int score = calculatePatternScore(mingGongStars, PalaceType.MING, chart);
|
||||
patterns.add(new Pattern("太阳独坐格", "太阳星独坐命宫,主光明磊落,热情大方", PalaceType.MING, mingGongStars, score));
|
||||
log.info("识别到格局:太阳独坐格(评分:{})", score);
|
||||
} else if (mingGongStars.contains(MajorStar.TAIYIN) && mingGongStars.size() == 1) {
|
||||
int score = calculatePatternScore(mingGongStars, PalaceType.MING, chart);
|
||||
patterns.add(new Pattern("太阴独坐格", "太阴星独坐命宫,主温柔内敛,母性光辉", PalaceType.MING, mingGongStars, score));
|
||||
log.info("识别到格局:太阴独坐格(评分:{})", score);
|
||||
} else if (mingGongStars.contains(MajorStar.WUQU) && mingGongStars.size() == 1) {
|
||||
int score = calculatePatternScore(mingGongStars, PalaceType.MING, chart);
|
||||
patterns.add(new Pattern("武曲独坐格", "武曲星独坐命宫,主刚毅务实,财帛丰盈", PalaceType.MING, mingGongStars, score));
|
||||
log.info("识别到格局:武曲独坐格(评分:{})", score);
|
||||
} else if (mingGongStars.contains(MajorStar.TIANTONG) && mingGongStars.size() == 1) {
|
||||
int score = calculatePatternScore(mingGongStars, PalaceType.MING, chart);
|
||||
patterns.add(new Pattern("天同独坐格", "天同星独坐命宫,主温和福气,享受人生", PalaceType.MING, mingGongStars, score));
|
||||
log.info("识别到格局:天同独坐格(评分:{})", score);
|
||||
} else if (mingGongStars.contains(MajorStar.TANLANG) && mingGongStars.size() == 1) {
|
||||
int score = calculatePatternScore(mingGongStars, PalaceType.MING, chart);
|
||||
patterns.add(new Pattern("贪狼独坐格", "贪狼星独坐命宫,主欲望强烈,魅力非凡", PalaceType.MING, mingGongStars, score));
|
||||
log.info("识别到格局:贪狼独坐格(评分:{})", score);
|
||||
} else if (mingGongStars.contains(MajorStar.JUMEN) && mingGongStars.size() == 1) {
|
||||
int score = calculatePatternScore(mingGongStars, PalaceType.MING, chart);
|
||||
patterns.add(new Pattern("巨门独坐格", "巨门星独坐命宫,主口才出众,是非较多", PalaceType.MING, mingGongStars, score));
|
||||
log.info("识别到格局:巨门独坐格(评分:{})", score);
|
||||
} else if (mingGongStars.contains(MajorStar.TIANXIANG) && mingGongStars.size() == 1) {
|
||||
int score = calculatePatternScore(mingGongStars, PalaceType.MING, chart);
|
||||
patterns.add(new Pattern("天相独坐格", "天相星独坐命宫,主辅佐贵人,稳重可靠", PalaceType.MING, mingGongStars, score));
|
||||
log.info("识别到格局:天相独坐格(评分:{})", score);
|
||||
} else if (mingGongStars.contains(MajorStar.TIANLIANG) && mingGongStars.size() == 1) {
|
||||
int score = calculatePatternScore(mingGongStars, PalaceType.MING, chart);
|
||||
patterns.add(new Pattern("天梁独坐格", "天梁星独坐命宫,主恩泽长辈,稳重踏实", PalaceType.MING, mingGongStars, score));
|
||||
log.info("识别到格局:天梁独坐格(评分:{})", score);
|
||||
} else if (mingGongStars.contains(MajorStar.QISHA) && mingGongStars.size() == 1) {
|
||||
int score = calculatePatternScore(mingGongStars, PalaceType.MING, chart);
|
||||
patterns.add(new Pattern("七杀独坐格", "七杀星独坐命宫,主威猛果断,开创能力强", PalaceType.MING, mingGongStars, score));
|
||||
log.info("识别到格局:七杀独坐格(评分:{})", score);
|
||||
} else if (mingGongStars.contains(MajorStar.POJUN) && mingGongStars.size() == 1) {
|
||||
int score = calculatePatternScore(mingGongStars, PalaceType.MING, chart);
|
||||
patterns.add(new Pattern("破军独坐格", "破军星独坐命宫,主破旧立新,变动较多", PalaceType.MING, mingGongStars, score));
|
||||
log.info("识别到格局:破军独坐格(评分:{})", score);
|
||||
} else if (mingGongStars.contains(MajorStar.TIANJI) && mingGongStars.size() == 1) {
|
||||
int score = calculatePatternScore(mingGongStars, PalaceType.MING, chart);
|
||||
patterns.add(new Pattern("天机独坐格", "天机星独坐命宫,主智慧谋略,变通能力强", PalaceType.MING, mingGongStars, score));
|
||||
log.info("识别到格局:天机独坐格(评分:{})", score);
|
||||
} else if (mingGongStars.contains(MajorStar.LIANCHEN) && mingGongStars.size() == 1) {
|
||||
int score = calculatePatternScore(mingGongStars, PalaceType.MING, chart);
|
||||
patterns.add(new Pattern("廉贞独坐格", "廉贞星独坐命宫,主感情丰富,才华出众", PalaceType.MING, mingGongStars, score));
|
||||
log.info("识别到格局:廉贞独坐格(评分:{})", score);
|
||||
}
|
||||
} else {
|
||||
log.warn("【格局识别警告】未找到命宫,无法分析命宫格局");
|
||||
}
|
||||
|
||||
Palace caiBoGong = chart.getPalaceByType(PalaceType.CAI.getName());
|
||||
if (caiBoGong != null) {
|
||||
List<MajorStar> caiBoStars = getMajorStars(caiBoGong);
|
||||
log.debug("【格局识别】财帛宫主星:{}", caiBoStars);
|
||||
|
||||
if (caiBoStars.contains(MajorStar.WUQU)) {
|
||||
int score = calculatePatternScore(caiBoStars, PalaceType.CAI, chart);
|
||||
patterns.add(new Pattern("武曲守财格", "武曲星守财帛宫,主财帛丰盈,财运亨通", PalaceType.CAI, caiBoStars, score));
|
||||
log.info("识别到格局:武曲守财格(评分:{})", score);
|
||||
} else if (caiBoStars.contains(MajorStar.TANLANG)) {
|
||||
int score = calculatePatternScore(caiBoStars, PalaceType.CAI, chart);
|
||||
patterns.add(new Pattern("贪狼守财格", "贪狼星守财帛宫,主欲望强烈,追求财富", PalaceType.CAI, caiBoStars, score));
|
||||
log.info("识别到格局:贪狼守财格(评分:{})", score);
|
||||
} else if (caiBoStars.contains(MajorStar.TIANFU)) {
|
||||
int score = calculatePatternScore(caiBoStars, PalaceType.CAI, chart);
|
||||
patterns.add(new Pattern("天府守财格", "天府星守财帛宫,主财富丰厚,财运稳健", PalaceType.CAI, caiBoStars, score));
|
||||
log.info("识别到格局:天府守财格(评分:{})", score);
|
||||
}
|
||||
} else {
|
||||
log.debug("【格局识别】未找到财帛宫");
|
||||
}
|
||||
|
||||
Palace guanLuGong = chart.getPalaceByType(PalaceType.GUANLU.getName());
|
||||
if (guanLuGong != null) {
|
||||
List<MajorStar> guanLuStars = getMajorStars(guanLuGong);
|
||||
log.debug("【格局识别】官禄宫主星:{}", guanLuStars);
|
||||
|
||||
if (guanLuStars.contains(MajorStar.TIANXIANG)) {
|
||||
int score = calculatePatternScore(guanLuStars, PalaceType.GUANLU, chart);
|
||||
patterns.add(new Pattern("天相守官格", "天相星守官禄宫,主辅佐贵人,官运亨通", PalaceType.GUANLU, guanLuStars, score));
|
||||
log.info("识别到格局:天相守官格(评分:{})", score);
|
||||
} else if (guanLuStars.contains(MajorStar.QISHA)) {
|
||||
int score = calculatePatternScore(guanLuStars, PalaceType.GUANLU, chart);
|
||||
patterns.add(new Pattern("七杀守官格", "七杀星守官禄宫,主威猛果断,开创事业", PalaceType.GUANLU, guanLuStars, score));
|
||||
log.info("识别到格局:七杀守官格(评分:{})", score);
|
||||
} else if (guanLuStars.contains(MajorStar.TIANJI)) {
|
||||
int score = calculatePatternScore(guanLuStars, PalaceType.GUANLU, chart);
|
||||
patterns.add(new Pattern("天机守官格", "天机星守官禄宫,主智慧谋略,善于规划", PalaceType.GUANLU, guanLuStars, score));
|
||||
log.info("识别到格局:天机守官格(评分:{})", score);
|
||||
}
|
||||
} else {
|
||||
log.debug("【格局识别】未找到官禄宫");
|
||||
}
|
||||
|
||||
Palace qianYiGong = chart.getPalaceByType(PalaceType.QIAN.getName());
|
||||
if (qianYiGong != null) {
|
||||
List<MajorStar> qianYiStars = getMajorStars(qianYiGong);
|
||||
log.debug("【格局识别】迁移宫主星:{}", qianYiStars);
|
||||
|
||||
if (qianYiStars.contains(MajorStar.TAIYANG)) {
|
||||
int score = calculatePatternScore(qianYiStars, PalaceType.QIAN, chart);
|
||||
patterns.add(new Pattern("太阳守迁格", "太阳星守迁移宫,主光明磊落,外出顺利", PalaceType.QIAN, qianYiStars, score));
|
||||
log.info("识别到格局:太阳守迁格(评分:{})", score);
|
||||
} else if (qianYiStars.contains(MajorStar.TAIYIN)) {
|
||||
int score = calculatePatternScore(qianYiStars, PalaceType.QIAN, chart);
|
||||
patterns.add(new Pattern("太阴守迁格", "太阴星守迁移宫,主温柔内敛,外出平安", PalaceType.QIAN, qianYiStars, score));
|
||||
log.info("识别到格局:太阴守迁格(评分:{})", score);
|
||||
} else if (qianYiStars.contains(MajorStar.TANLANG)) {
|
||||
int score = calculatePatternScore(qianYiStars, PalaceType.QIAN, chart);
|
||||
patterns.add(new Pattern("贪狼守迁格", "贪狼星守迁移宫,主魅力非凡,外出受欢迎", PalaceType.QIAN, qianYiStars, score));
|
||||
log.info("识别到格局:贪狼守迁格(评分:{})", score);
|
||||
}
|
||||
} else {
|
||||
log.debug("未找到迁移宫");
|
||||
}
|
||||
|
||||
log.info("格局识别完成,共识别到{}个格局", patterns.size());
|
||||
log.debug("识别到的格局:{}", patterns);
|
||||
return patterns;
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算格局评分
|
||||
* 综合考虑星曜性质、宫位重要性、三方四正支持等因素
|
||||
*
|
||||
* @param stars 涉及的主星列表
|
||||
* @param palaceType 宫位类型
|
||||
* @param chart 紫微盘对象
|
||||
* @return 格局评分(0-100)
|
||||
*/
|
||||
private static int calculatePatternScore(List<MajorStar> stars, PalaceType palaceType, ZiweiChart chart) {
|
||||
int baseScore = calculateBaseScore(stars, palaceType);
|
||||
int sanFangSiZhengBonus = calculateSanFangSiZhengBonus(palaceType, chart);
|
||||
int starSynergyBonus = calculateStarSynergyBonus(stars);
|
||||
|
||||
int totalScore = baseScore + sanFangSiZhengBonus + starSynergyBonus;
|
||||
|
||||
log.debug("格局评分计算 - 基础分:{},三方四正加分:{},星曜协同加分:{},总分:{}",
|
||||
baseScore, sanFangSiZhengBonus, starSynergyBonus, totalScore);
|
||||
|
||||
return Math.min(100, Math.max(0, totalScore));
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算基础评分
|
||||
* 根据星曜性质和宫位类型计算基础分
|
||||
*
|
||||
* @param stars 主星列表
|
||||
* @param palaceType 宫位类型
|
||||
* @return 基础评分
|
||||
*/
|
||||
private static int calculateBaseScore(List<MajorStar> stars, PalaceType palaceType) {
|
||||
int baseScore = 0;
|
||||
|
||||
for (MajorStar star : stars) {
|
||||
int starScore = getStarBaseScore(star, palaceType);
|
||||
baseScore += starScore;
|
||||
}
|
||||
|
||||
if (stars.size() > 0) {
|
||||
baseScore = baseScore / stars.size();
|
||||
}
|
||||
|
||||
return baseScore;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取星曜基础评分
|
||||
* 根据星曜性质和宫位类型计算单星基础分
|
||||
*
|
||||
* @param star 主星
|
||||
* @param palaceType 宫位类型
|
||||
* @return 星曜基础评分
|
||||
*/
|
||||
private static int getStarBaseScore(MajorStar star, PalaceType palaceType) {
|
||||
int score = 60;
|
||||
|
||||
switch (star) {
|
||||
case ZIWEI:
|
||||
score = (palaceType == PalaceType.MING) ? 85 : 75;
|
||||
break;
|
||||
case TIANFU:
|
||||
score = (palaceType == PalaceType.CAI) ? 78 : 72;
|
||||
break;
|
||||
case TAIYANG:
|
||||
score = (palaceType == PalaceType.QIAN) ? 75 : 70;
|
||||
break;
|
||||
case TAIYIN:
|
||||
score = (palaceType == PalaceType.QIAN) ? 70 : 65;
|
||||
break;
|
||||
case WUQU:
|
||||
score = (palaceType == PalaceType.CAI) ? 80 : 70;
|
||||
break;
|
||||
case TIANXIANG:
|
||||
score = (palaceType == PalaceType.GUANLU) ? 80 : 70;
|
||||
break;
|
||||
case QISHA:
|
||||
score = (palaceType == PalaceType.GUANLU) ? 75 : 65;
|
||||
break;
|
||||
case TIANJI:
|
||||
score = (palaceType == PalaceType.GUANLU) ? 72 : 68;
|
||||
break;
|
||||
case TIANTONG:
|
||||
score = 70;
|
||||
break;
|
||||
case TIANLIANG:
|
||||
score = 70;
|
||||
break;
|
||||
case TANLANG:
|
||||
score = (palaceType == PalaceType.CAI || palaceType == PalaceType.QIAN) ? 68 : 62;
|
||||
break;
|
||||
case JUMEN:
|
||||
score = 60;
|
||||
break;
|
||||
case LIANCHEN:
|
||||
score = 60;
|
||||
break;
|
||||
case POJUN:
|
||||
score = 65;
|
||||
break;
|
||||
default:
|
||||
score = 60;
|
||||
break;
|
||||
}
|
||||
|
||||
return score;
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算三方四正加分
|
||||
* 根据三方四正宫位的星曜情况计算加分
|
||||
*
|
||||
* @param palaceType 宫位类型
|
||||
* @param chart 紫微盘对象
|
||||
* @return 三方四正加分
|
||||
*/
|
||||
private static int calculateSanFangSiZhengBonus(PalaceType palaceType, ZiweiChart chart) {
|
||||
SanFangSiZhengUtil.SanFangSiZheng sanFangSiZheng = chart.getSanFangSiZheng();
|
||||
if (sanFangSiZheng == null) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int bonus = 0;
|
||||
int sanFangScore = sanFangSiZheng.calculateSanFangScore();
|
||||
int siZhengScore = sanFangSiZheng.calculateSiZhengScore();
|
||||
|
||||
if (sanFangScore >= 75) {
|
||||
bonus += 5;
|
||||
} else if (sanFangScore >= 70) {
|
||||
bonus += 3;
|
||||
}
|
||||
|
||||
if (siZhengScore >= 75) {
|
||||
bonus += 3;
|
||||
} else if (siZhengScore >= 70) {
|
||||
bonus += 2;
|
||||
}
|
||||
|
||||
return bonus;
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算星曜协同加分
|
||||
* 根据星曜之间的协同效应计算加分
|
||||
*
|
||||
* @param stars 主星列表
|
||||
* @return 星曜协同加分
|
||||
*/
|
||||
private static int calculateStarSynergyBonus(List<MajorStar> stars) {
|
||||
if (stars.size() < 2) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int bonus = 0;
|
||||
|
||||
if (stars.contains(MajorStar.ZIWEI) && stars.contains(MajorStar.TIANFU)) {
|
||||
bonus += 8;
|
||||
}
|
||||
|
||||
if (stars.contains(MajorStar.TAIYANG) && stars.contains(MajorStar.TAIYIN)) {
|
||||
bonus += 6;
|
||||
}
|
||||
|
||||
if (stars.contains(MajorStar.TIANJI) && stars.contains(MajorStar.TAIYIN) && stars.contains(MajorStar.TIANLIANG)) {
|
||||
bonus += 7;
|
||||
}
|
||||
|
||||
if (stars.contains(MajorStar.QISHA) && stars.contains(MajorStar.POJUN) && stars.contains(MajorStar.TANLANG)) {
|
||||
bonus += 5;
|
||||
}
|
||||
|
||||
if (stars.contains(MajorStar.TIANJI) && stars.contains(MajorStar.TIANLIANG)) {
|
||||
bonus += 4;
|
||||
}
|
||||
|
||||
if (stars.contains(MajorStar.TIANXIANG) && stars.contains(MajorStar.TIANLIANG)) {
|
||||
bonus += 4;
|
||||
}
|
||||
|
||||
return bonus;
|
||||
}
|
||||
|
||||
private static List<MajorStar> getMajorStars(Palace palace) {
|
||||
List<MajorStar> stars = new ArrayList<>();
|
||||
for (StarInfo starInfo : palace.getMajorStars()) {
|
||||
stars.add(starInfo.getStar());
|
||||
}
|
||||
return stars;
|
||||
}
|
||||
|
||||
public static String analyzePatterns(List<Pattern> patterns) {
|
||||
log.info("开始分析格局,格局数量:{}", patterns != null ? patterns.size() : 0);
|
||||
if (patterns == null || patterns.isEmpty()) {
|
||||
log.debug("未识别到特殊格局");
|
||||
return "未识别到特殊格局";
|
||||
}
|
||||
|
||||
StringBuilder analysis = new StringBuilder();
|
||||
analysis.append("【格局分析】\n");
|
||||
analysis.append("共识别到").append(patterns.size()).append("个格局:\n\n");
|
||||
|
||||
for (Pattern pattern : patterns) {
|
||||
analysis.append("格局名称:").append(pattern.getName()).append("\n");
|
||||
analysis.append("格局描述:").append(pattern.getDescription()).append("\n");
|
||||
analysis.append("所在宫位:").append(pattern.getPalaceType().getName()).append("\n");
|
||||
analysis.append("涉及星曜:");
|
||||
for (MajorStar star : pattern.getStars()) {
|
||||
analysis.append(star.getName()).append(" ");
|
||||
}
|
||||
analysis.append("\n");
|
||||
analysis.append("格局评分:").append(pattern.getScore()).append("\n\n");
|
||||
log.debug("分析格局:{}(评分:{})", pattern.getName(), pattern.getScore());
|
||||
}
|
||||
|
||||
log.info("格局分析完成,共分析{}个格局", patterns.size());
|
||||
return analysis.toString();
|
||||
}
|
||||
}
|
||||
+249
@@ -0,0 +1,249 @@
|
||||
package io.destiny.biz.util;
|
||||
|
||||
import io.destiny.biz.domain.Palace;
|
||||
import io.destiny.biz.domain.ZiweiChart;
|
||||
import io.destiny.biz.enums.EarthlyBranch;
|
||||
import io.destiny.biz.enums.PalaceType;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 三方四正工具类
|
||||
* 用于计算和分析紫微斗数中的三方四正
|
||||
* 三方:命宫、财帛宫、官禄宫
|
||||
* 四正:命宫、财帛宫、官禄宫、迁移宫
|
||||
*
|
||||
* @author 张翔
|
||||
* @date 2025-12-29
|
||||
*/
|
||||
public class SanFangSiZhengUtil {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(SanFangSiZhengUtil.class);
|
||||
|
||||
/**
|
||||
* 三方四正内部类
|
||||
* 封装三方四正的四个宫位信息
|
||||
*/
|
||||
public static class SanFangSiZheng {
|
||||
/** 命宫 */
|
||||
private Palace mingGong;
|
||||
/** 财帛宫 */
|
||||
private Palace caiBoGong;
|
||||
/** 官禄宫 */
|
||||
private Palace guanLuGong;
|
||||
/** 迁移宫 */
|
||||
private Palace qianYiGong;
|
||||
|
||||
/**
|
||||
* 构造函数
|
||||
* @param mingGong 命宫
|
||||
* @param caiBoGong 财帛宫
|
||||
* @param guanLuGong 官禄宫
|
||||
* @param qianYiGong 迁移宫
|
||||
*/
|
||||
public SanFangSiZheng(Palace mingGong, Palace caiBoGong, Palace guanLuGong, Palace qianYiGong) {
|
||||
this.mingGong = mingGong;
|
||||
this.caiBoGong = caiBoGong;
|
||||
this.guanLuGong = guanLuGong;
|
||||
this.qianYiGong = qianYiGong;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取命宫
|
||||
* @return 命宫对象
|
||||
*/
|
||||
public Palace getMingGong() {
|
||||
return mingGong;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取财帛宫
|
||||
* @return 财帛宫对象
|
||||
*/
|
||||
public Palace getCaiBoGong() {
|
||||
return caiBoGong;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取官禄宫
|
||||
* @return 官禄宫对象
|
||||
*/
|
||||
public Palace getGuanLuGong() {
|
||||
return guanLuGong;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取迁移宫
|
||||
* @return 迁移宫对象
|
||||
*/
|
||||
public Palace getQianYiGong() {
|
||||
return qianYiGong;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取三方宫位列表
|
||||
* 三方包括:命宫、财帛宫、官禄宫
|
||||
* @return 三方宫位列表
|
||||
*/
|
||||
public List<Palace> getSanFang() {
|
||||
List<Palace> sanFang = new ArrayList<>();
|
||||
sanFang.add(mingGong);
|
||||
sanFang.add(caiBoGong);
|
||||
sanFang.add(guanLuGong);
|
||||
return sanFang;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取四正宫位列表
|
||||
* 四正包括:命宫、财帛宫、官禄宫、迁移宫
|
||||
* @return 四正宫位列表
|
||||
*/
|
||||
public List<Palace> getSiZheng() {
|
||||
List<Palace> siZheng = new ArrayList<>();
|
||||
siZheng.add(mingGong);
|
||||
siZheng.add(caiBoGong);
|
||||
siZheng.add(guanLuGong);
|
||||
siZheng.add(qianYiGong);
|
||||
return siZheng;
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算三方平均分
|
||||
* @return 三方平均分值
|
||||
*/
|
||||
public int calculateSanFangScore() {
|
||||
int totalScore = 0;
|
||||
for (Palace palace : getSanFang()) {
|
||||
totalScore += palace.getScore();
|
||||
}
|
||||
return totalScore / 3;
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算四正平均分
|
||||
* @return 四正平均分值
|
||||
*/
|
||||
public int calculateSiZhengScore() {
|
||||
int totalScore = 0;
|
||||
for (Palace palace : getSiZheng()) {
|
||||
totalScore += palace.getScore();
|
||||
}
|
||||
return totalScore / 4;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算三方四正
|
||||
* 根据命宫地支计算财帛宫、官禄宫、迁移宫的位置
|
||||
* @param chart 紫微盘对象
|
||||
* @return 三方四正对象
|
||||
*/
|
||||
public static SanFangSiZheng calculateSanFangSiZheng(ZiweiChart chart) {
|
||||
log.info("【三方四正计算】开始计算三方四正");
|
||||
Palace mingGong = chart.getPalaceByType(PalaceType.MING.getName());
|
||||
if (mingGong == null) {
|
||||
log.error("【三方四正计算错误】未找到命宫,无法计算三方四正");
|
||||
return null;
|
||||
}
|
||||
|
||||
EarthlyBranch mingGongBranch = mingGong.getEarthlyBranch();
|
||||
log.debug("【三方四正计算】命宫地支:{}", mingGongBranch.getName());
|
||||
|
||||
EarthlyBranch caiBoGongBranch = calculateCaiBoGong(mingGongBranch);
|
||||
EarthlyBranch guanLuGongBranch = calculateGuanLuGong(mingGongBranch);
|
||||
EarthlyBranch qianYiGongBranch = calculateQianYiGong(mingGongBranch);
|
||||
|
||||
Palace caiBoGong = chart.getPalaceByBranch(caiBoGongBranch);
|
||||
Palace guanLuGong = chart.getPalaceByBranch(guanLuGongBranch);
|
||||
Palace qianYiGong = chart.getPalaceByBranch(qianYiGongBranch);
|
||||
|
||||
log.info("【三方四正计算完成】三方四正计算完成 - 命宫:{},财帛宫:{},官禄宫:{},迁移宫:{}",
|
||||
mingGongBranch.getName(), caiBoGongBranch.getName(),
|
||||
guanLuGongBranch.getName(), qianYiGongBranch.getName());
|
||||
log.debug("【三方四正计算详情】财帛宫地支:{}", caiBoGongBranch.getName());
|
||||
log.debug("【三方四正计算详情】官禄宫地支:{}", guanLuGongBranch.getName());
|
||||
log.debug("【三方四正计算详情】迁移宫地支:{}", qianYiGongBranch.getName());
|
||||
|
||||
return new SanFangSiZheng(mingGong, caiBoGong, guanLuGong, qianYiGong);
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算财帛宫地支
|
||||
* 财帛宫位于命宫对冲位置(命宫+5)
|
||||
* @param mingGongBranch 命宫地支
|
||||
* @return 财帛宫地支
|
||||
*/
|
||||
private static EarthlyBranch calculateCaiBoGong(EarthlyBranch mingGongBranch) {
|
||||
int mingGongIndex = mingGongBranch.getIndex();
|
||||
int caiBoGongIndex = mingGongIndex + 5;
|
||||
if (caiBoGongIndex > 12) {
|
||||
caiBoGongIndex -= 12;
|
||||
}
|
||||
return EarthlyBranch.fromIndex(caiBoGongIndex);
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算官禄宫地支
|
||||
* 官禄宫位于命宫对冲位置(命宫+10)
|
||||
* @param mingGongBranch 命宫地支
|
||||
* @return 官禄宫地支
|
||||
*/
|
||||
private static EarthlyBranch calculateGuanLuGong(EarthlyBranch mingGongBranch) {
|
||||
int mingGongIndex = mingGongBranch.getIndex();
|
||||
int guanLuGongIndex = mingGongIndex + 10;
|
||||
if (guanLuGongIndex > 12) {
|
||||
guanLuGongIndex -= 12;
|
||||
}
|
||||
return EarthlyBranch.fromIndex(guanLuGongIndex);
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算迁移宫地支
|
||||
* 迁移宫位于命宫对冲位置(命宫+6)
|
||||
* @param mingGongBranch 命宫地支
|
||||
* @return 迁移宫地支
|
||||
*/
|
||||
private static EarthlyBranch calculateQianYiGong(EarthlyBranch mingGongBranch) {
|
||||
int mingGongIndex = mingGongBranch.getIndex();
|
||||
int qianYiGongIndex = mingGongIndex + 6;
|
||||
if (qianYiGongIndex > 12) {
|
||||
qianYiGongIndex -= 12;
|
||||
}
|
||||
return EarthlyBranch.fromIndex(qianYiGongIndex);
|
||||
}
|
||||
|
||||
/**
|
||||
* 分析三方四正
|
||||
* 生成三方四正的详细分析报告
|
||||
* @param sanFangSiZheng 三方四正对象
|
||||
* @return 分析报告字符串
|
||||
*/
|
||||
public static String analyzeSanFangSiZheng(SanFangSiZheng sanFangSiZheng) {
|
||||
StringBuilder analysis = new StringBuilder();
|
||||
analysis.append("【三方四正分析】\n");
|
||||
|
||||
analysis.append("\n【三方分析】\n");
|
||||
analysis.append("命宫:").append(sanFangSiZheng.getMingGong().getPalaceType().getName())
|
||||
.append("(").append(sanFangSiZheng.getMingGong().getEarthlyBranch().getName()).append(")")
|
||||
.append(",得分:").append(sanFangSiZheng.getMingGong().getScore()).append("\n");
|
||||
analysis.append("财帛宫:").append(sanFangSiZheng.getCaiBoGong().getPalaceType().getName())
|
||||
.append("(").append(sanFangSiZheng.getCaiBoGong().getEarthlyBranch().getName()).append(")")
|
||||
.append(",得分:").append(sanFangSiZheng.getCaiBoGong().getScore()).append("\n");
|
||||
analysis.append("官禄宫:").append(sanFangSiZheng.getGuanLuGong().getPalaceType().getName())
|
||||
.append("(").append(sanFangSiZheng.getGuanLuGong().getEarthlyBranch().getName()).append(")")
|
||||
.append(",得分:").append(sanFangSiZheng.getGuanLuGong().getScore()).append("\n");
|
||||
analysis.append("三方平均分:").append(sanFangSiZheng.calculateSanFangScore()).append("\n");
|
||||
|
||||
analysis.append("\n【四正分析】\n");
|
||||
analysis.append("迁移宫:").append(sanFangSiZheng.getQianYiGong().getPalaceType().getName())
|
||||
.append("(").append(sanFangSiZheng.getQianYiGong().getEarthlyBranch().getName()).append(")")
|
||||
.append(",得分:").append(sanFangSiZheng.getQianYiGong().getScore()).append("\n");
|
||||
analysis.append("四正平均分:").append(sanFangSiZheng.calculateSiZhengScore()).append("\n");
|
||||
|
||||
return analysis.toString();
|
||||
}
|
||||
}
|
||||
+152
@@ -0,0 +1,152 @@
|
||||
package io.destiny.biz.util;
|
||||
|
||||
import io.destiny.biz.domain.Almanac;
|
||||
import io.destiny.biz.dto.SearchCondition;
|
||||
import io.destiny.biz.dto.MatchedItems;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class SearchConditionOptimizer {
|
||||
|
||||
private static final int CACHE_SIZE = 1000;
|
||||
private static final Map<String, Boolean> matchCache = new ConcurrentHashMap<>(CACHE_SIZE);
|
||||
|
||||
public static void clearCache() {
|
||||
matchCache.clear();
|
||||
}
|
||||
|
||||
public static boolean matchConditionsOptimized(Almanac almanac, List<SearchCondition> conditions) {
|
||||
if (conditions == null || conditions.isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
String cacheKey = generateCacheKey(almanac, conditions);
|
||||
Boolean cachedResult = matchCache.get(cacheKey);
|
||||
if (cachedResult != null) {
|
||||
return cachedResult;
|
||||
}
|
||||
|
||||
boolean result = conditions.stream().allMatch(condition -> {
|
||||
List<String> items = "suitable".equals(condition.getType())
|
||||
? almanac.getSuitable()
|
||||
: almanac.getUnsuitable();
|
||||
|
||||
Set<String> itemSet = new HashSet<>(items);
|
||||
Set<String> conditionItems = new HashSet<>(condition.getItems());
|
||||
|
||||
if (condition.getExclude()) {
|
||||
return Collections.disjoint(itemSet, conditionItems);
|
||||
}
|
||||
|
||||
if ("and".equals(condition.getOperator())) {
|
||||
return itemSet.containsAll(conditionItems);
|
||||
} else {
|
||||
return !Collections.disjoint(itemSet, conditionItems);
|
||||
}
|
||||
});
|
||||
|
||||
if (matchCache.size() >= CACHE_SIZE) {
|
||||
matchCache.clear();
|
||||
}
|
||||
matchCache.put(cacheKey, result);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static List<io.destiny.biz.dto.SearchResult> createSearchResultsOptimized(
|
||||
List<Almanac> almanacs,
|
||||
List<SearchCondition> conditions,
|
||||
io.destiny.biz.service.ICalendarService calendarService,
|
||||
java.time.format.DateTimeFormatter dateFormatter) {
|
||||
|
||||
return almanacs.parallelStream()
|
||||
.filter(almanac -> matchConditionsOptimized(almanac, conditions))
|
||||
.map(almanac -> createSearchResult(almanac, conditions, calendarService, dateFormatter))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private static io.destiny.biz.dto.SearchResult createSearchResult(
|
||||
Almanac almanac,
|
||||
List<SearchCondition> conditions,
|
||||
io.destiny.biz.service.ICalendarService calendarService,
|
||||
java.time.format.DateTimeFormatter dateFormatter) {
|
||||
|
||||
io.destiny.biz.dto.SearchResult result = new io.destiny.biz.dto.SearchResult();
|
||||
result.setDate(almanac.getSolarDate().toLocalDate().format(dateFormatter));
|
||||
|
||||
io.destiny.biz.dto.AlmanacResponse almanacResponse = io.destiny.biz.dto.AlmanacResponse.fromAlmanac(almanac,
|
||||
calendarService);
|
||||
result.setLunarDate(formatLunarDate(almanacResponse));
|
||||
result.setWeekday(getWeekday(almanac.getSolarDate().toLocalDate()));
|
||||
|
||||
List<String> matchedSuitable = new ArrayList<>();
|
||||
List<String> matchedUnsuitable = new ArrayList<>();
|
||||
int matchCount = 0;
|
||||
|
||||
Set<String> suitableSet = new HashSet<>(almanac.getSuitable());
|
||||
Set<String> unsuitableSet = new HashSet<>(almanac.getUnsuitable());
|
||||
|
||||
for (SearchCondition condition : conditions) {
|
||||
Set<String> itemSet = "suitable".equals(condition.getType())
|
||||
? suitableSet
|
||||
: unsuitableSet;
|
||||
|
||||
Set<String> conditionItems = new HashSet<>(condition.getItems());
|
||||
|
||||
List<String> matched = conditionItems.stream()
|
||||
.filter(itemSet::contains)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
if ("suitable".equals(condition.getType())) {
|
||||
matchedSuitable.addAll(matched);
|
||||
} else {
|
||||
matchedUnsuitable.addAll(matched);
|
||||
}
|
||||
|
||||
matchCount += matched.size();
|
||||
}
|
||||
|
||||
result.setMatchedItems(new MatchedItems(matchedSuitable, matchedUnsuitable));
|
||||
result.setMatchCount(matchCount);
|
||||
result.setAlmanacData(almanacResponse);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static String generateCacheKey(Almanac almanac, List<SearchCondition> conditions) {
|
||||
StringBuilder keyBuilder = new StringBuilder();
|
||||
keyBuilder.append(almanac.getSolarDate().toString());
|
||||
keyBuilder.append("|");
|
||||
keyBuilder.append(almanac.getSuitable().hashCode());
|
||||
keyBuilder.append("|");
|
||||
keyBuilder.append(almanac.getUnsuitable().hashCode());
|
||||
keyBuilder.append("|");
|
||||
|
||||
for (SearchCondition condition : conditions) {
|
||||
keyBuilder.append(condition.getType());
|
||||
keyBuilder.append(":");
|
||||
keyBuilder.append(condition.getOperator());
|
||||
keyBuilder.append(":");
|
||||
keyBuilder.append(condition.getExclude());
|
||||
keyBuilder.append(":");
|
||||
keyBuilder.append(condition.getItems().hashCode());
|
||||
keyBuilder.append("|");
|
||||
}
|
||||
|
||||
return keyBuilder.toString();
|
||||
}
|
||||
|
||||
private static String formatLunarDate(io.destiny.biz.dto.AlmanacResponse almanacResponse) {
|
||||
if (almanacResponse == null || almanacResponse.getLunarDate() == null) {
|
||||
return "";
|
||||
}
|
||||
return "农历" + almanacResponse.getLunarDate();
|
||||
}
|
||||
|
||||
private static String getWeekday(java.time.LocalDate date) {
|
||||
String[] weekdays = { "星期日", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六" };
|
||||
return weekdays[date.getDayOfWeek().getValue() % 7];
|
||||
}
|
||||
}
|
||||
+316
@@ -0,0 +1,316 @@
|
||||
package io.destiny.biz.util;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZoneId;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.time.format.TextStyle;
|
||||
import java.time.zone.ZoneRules;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class TimeZoneUtil {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(TimeZoneUtil.class);
|
||||
|
||||
private static final String DEFAULT_TIMEZONE = "Asia/Shanghai";
|
||||
|
||||
private static final Map<String, String> COMMON_TIMEZONE_NAMES = createCommonTimezoneNames();
|
||||
|
||||
private static Map<String, String> createCommonTimezoneNames() {
|
||||
log.debug("初始化常用时区名称映射");
|
||||
Map<String, String> names = new HashMap<>();
|
||||
|
||||
names.put("Asia/Shanghai", "中国标准时间 (北京)");
|
||||
names.put("Asia/Hong_Kong", "香港时间");
|
||||
names.put("Asia/Taipei", "台北时间");
|
||||
names.put("Asia/Tokyo", "日本标准时间");
|
||||
names.put("Asia/Seoul", "韩国标准时间");
|
||||
names.put("Asia/Singapore", "新加坡时间");
|
||||
names.put("Asia/Kolkata", "印度标准时间");
|
||||
names.put("Asia/Dubai", "海湾标准时间");
|
||||
names.put("Europe/London", "格林威治标准时间");
|
||||
names.put("Europe/Paris", "中欧时间");
|
||||
names.put("Europe/Berlin", "中欧时间");
|
||||
names.put("Europe/Moscow", "莫斯科时间");
|
||||
names.put("America/New_York", "美国东部时间");
|
||||
names.put("America/Chicago", "美国中部时间");
|
||||
names.put("America/Denver", "美国山地时间");
|
||||
names.put("America/Los_Angeles", "美国太平洋时间");
|
||||
names.put("America/Toronto", "加拿大东部时间");
|
||||
names.put("Australia/Sydney", "澳大利亚东部时间");
|
||||
names.put("Pacific/Auckland", "新西兰标准时间");
|
||||
|
||||
log.debug("常用时区名称映射初始化完成,共{}个时区", names.size());
|
||||
return Collections.unmodifiableMap(names);
|
||||
}
|
||||
|
||||
static {
|
||||
log.debug("初始化时区工具类");
|
||||
log.debug("时区工具类初始化完成");
|
||||
}
|
||||
|
||||
public static ZoneId getDefaultZoneId() {
|
||||
return ZoneId.of(DEFAULT_TIMEZONE);
|
||||
}
|
||||
|
||||
public static ZoneId parseZoneId(String timeZone) {
|
||||
log.debug("解析时区:{}", timeZone);
|
||||
if (timeZone == null || timeZone.trim().isEmpty()) {
|
||||
log.debug("时区为空,使用默认时区:{}", DEFAULT_TIMEZONE);
|
||||
return ZoneId.of(DEFAULT_TIMEZONE);
|
||||
}
|
||||
|
||||
try {
|
||||
ZoneId zoneId = ZoneId.of(timeZone);
|
||||
log.debug("时区解析成功:{}", zoneId);
|
||||
return zoneId;
|
||||
} catch (Exception e) {
|
||||
log.error("时区解析失败:{},使用默认时区:{}", timeZone, DEFAULT_TIMEZONE);
|
||||
return ZoneId.of(DEFAULT_TIMEZONE);
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isValidTimeZone(String timeZone) {
|
||||
log.debug("验证时区有效性:{}", timeZone);
|
||||
if (timeZone == null || timeZone.trim().isEmpty()) {
|
||||
log.debug("时区为空,返回false");
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
ZoneId.of(timeZone);
|
||||
log.debug("时区验证通过:{}", timeZone);
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
log.debug("时区验证失败:{}", timeZone);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static ZonedDateTime convertTimeZone(LocalDateTime dateTime, String fromTimeZone, String toTimeZone) {
|
||||
log.debug("转换时区:{},从{}到{}", dateTime, fromTimeZone, toTimeZone);
|
||||
if (dateTime == null) {
|
||||
log.debug("日期时间为空,返回null");
|
||||
return null;
|
||||
}
|
||||
|
||||
ZoneId fromZoneId = parseZoneId(fromTimeZone);
|
||||
ZoneId toZoneId = parseZoneId(toTimeZone);
|
||||
|
||||
ZonedDateTime fromZoned = dateTime.atZone(fromZoneId);
|
||||
ZonedDateTime toZoned = fromZoned.withZoneSameInstant(toZoneId);
|
||||
|
||||
log.debug("时区转换完成:{} -> {}", fromZoned, toZoned);
|
||||
return toZoned;
|
||||
}
|
||||
|
||||
public static ZonedDateTime convertTimeZone(ZonedDateTime zonedDateTime, String toTimeZone) {
|
||||
log.debug("转换时区:{},到{}", zonedDateTime, toTimeZone);
|
||||
if (zonedDateTime == null) {
|
||||
log.debug("日期时间为空,返回null");
|
||||
return null;
|
||||
}
|
||||
|
||||
ZoneId toZoneId = parseZoneId(toTimeZone);
|
||||
ZonedDateTime result = zonedDateTime.withZoneSameInstant(toZoneId);
|
||||
|
||||
log.debug("时区转换完成:{} -> {}", zonedDateTime, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
public static String getTimeZoneDisplayName(String timeZone, Locale locale) {
|
||||
log.debug("获取时区显示名称:{},语言环境:{}", timeZone, locale);
|
||||
ZoneId zoneId = parseZoneId(timeZone);
|
||||
|
||||
String displayName = COMMON_TIMEZONE_NAMES.getOrDefault(
|
||||
zoneId.getId(),
|
||||
zoneId.getDisplayName(TextStyle.FULL, locale != null ? locale : Locale.CHINESE)
|
||||
);
|
||||
|
||||
log.debug("时区显示名称:{}", displayName);
|
||||
return displayName;
|
||||
}
|
||||
|
||||
public static String getTimeZoneOffset(String timeZone) {
|
||||
log.debug("获取时区偏移量:{}", timeZone);
|
||||
ZoneId zoneId = parseZoneId(timeZone);
|
||||
|
||||
ZonedDateTime now = ZonedDateTime.now(zoneId);
|
||||
ZoneRules rules = zoneId.getRules();
|
||||
int offsetSeconds = rules.getOffset(now.toInstant()).getTotalSeconds();
|
||||
|
||||
int hours = offsetSeconds / 3600;
|
||||
int minutes = Math.abs((offsetSeconds % 3600) / 60);
|
||||
|
||||
String offset = String.format("UTC%+d:%02d", hours, minutes);
|
||||
log.debug("时区偏移量:{}", offset);
|
||||
return offset;
|
||||
}
|
||||
|
||||
public static boolean isDaylightSavingTime(String timeZone) {
|
||||
log.debug("判断是否为夏令时:{}", timeZone);
|
||||
ZoneId zoneId = parseZoneId(timeZone);
|
||||
|
||||
ZonedDateTime now = ZonedDateTime.now(zoneId);
|
||||
ZoneRules rules = zoneId.getRules();
|
||||
boolean result = rules.isDaylightSavings(now.toInstant());
|
||||
|
||||
log.debug("夏令时判断结果:{}", result);
|
||||
return result;
|
||||
}
|
||||
|
||||
public static List<TimeZoneInfo> getCommonTimeZones() {
|
||||
log.debug("获取常用时区列表");
|
||||
List<TimeZoneInfo> timeZones = COMMON_TIMEZONE_NAMES.entrySet().stream()
|
||||
.map(entry -> {
|
||||
ZoneId zoneId = ZoneId.of(entry.getKey());
|
||||
ZonedDateTime now = ZonedDateTime.now(zoneId);
|
||||
ZoneRules rules = zoneId.getRules();
|
||||
int offsetSeconds = rules.getOffset(now.toInstant()).getTotalSeconds();
|
||||
int hours = offsetSeconds / 3600;
|
||||
int minutes = Math.abs((offsetSeconds % 3600) / 60);
|
||||
String offset = String.format("UTC%+d:%02d", hours, minutes);
|
||||
|
||||
return new TimeZoneInfo(
|
||||
entry.getKey(),
|
||||
entry.getValue(),
|
||||
offset,
|
||||
rules.isDaylightSavings(now.toInstant())
|
||||
);
|
||||
})
|
||||
.sorted(Comparator.comparingInt(TimeZoneInfo::getOffsetHours))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
log.debug("常用时区列表获取完成,共{}个时区", timeZones.size());
|
||||
return timeZones;
|
||||
}
|
||||
|
||||
public static List<String> getAllAvailableTimeZones() {
|
||||
log.debug("获取所有可用时区列表");
|
||||
List<String> timeZones = ZoneId.getAvailableZoneIds().stream()
|
||||
.sorted()
|
||||
.collect(Collectors.toList());
|
||||
|
||||
log.debug("所有可用时区列表获取完成,共{}个时区", timeZones.size());
|
||||
return timeZones;
|
||||
}
|
||||
|
||||
public static String getCurrentTimeInTimeZone(String timeZone) {
|
||||
log.debug("获取时区当前时间:{}", timeZone);
|
||||
ZoneId zoneId = parseZoneId(timeZone);
|
||||
ZonedDateTime now = ZonedDateTime.now(zoneId);
|
||||
|
||||
String result = DateTimeFormatUtil.formatDateTime(now.toLocalDateTime());
|
||||
log.debug("时区当前时间:{}", result);
|
||||
return result;
|
||||
}
|
||||
|
||||
public static int getTimeDifference(String timeZone1, String timeZone2) {
|
||||
log.debug("计算时区时间差:{} 和 {}", timeZone1, timeZone2);
|
||||
ZoneId zoneId1 = parseZoneId(timeZone1);
|
||||
ZoneId zoneId2 = parseZoneId(timeZone2);
|
||||
|
||||
ZonedDateTime now = ZonedDateTime.now(zoneId1);
|
||||
ZoneRules rules1 = zoneId1.getRules();
|
||||
ZoneRules rules2 = zoneId2.getRules();
|
||||
|
||||
int offset1 = rules1.getOffset(now.toInstant()).getTotalSeconds();
|
||||
int offset2 = rules2.getOffset(now.toInstant()).getTotalSeconds();
|
||||
|
||||
int diffHours = (offset2 - offset1) / 3600;
|
||||
log.debug("时区时间差:{}小时", diffHours);
|
||||
return diffHours;
|
||||
}
|
||||
|
||||
public static String getTimeDifferenceDescription(String timeZone1, String timeZone2) {
|
||||
log.debug("生成时区时间差描述:{} 和 {}", timeZone1, timeZone2);
|
||||
int diffHours = getTimeDifference(timeZone1, timeZone2);
|
||||
|
||||
String description;
|
||||
if (diffHours == 0) {
|
||||
description = "两个时区时间相同";
|
||||
} else if (diffHours > 0) {
|
||||
description = String.format("%s比%s快%d小时", timeZone2, timeZone1, diffHours);
|
||||
} else {
|
||||
description = String.format("%s比%s慢%d小时", timeZone2, timeZone1, Math.abs(diffHours));
|
||||
}
|
||||
|
||||
log.debug("时区时间差描述:{}", description);
|
||||
return description;
|
||||
}
|
||||
|
||||
public static TimeZoneInfo getTimeZoneInfo(String timeZone) {
|
||||
log.debug("获取时区信息:{}", timeZone);
|
||||
ZoneId zoneId = parseZoneId(timeZone);
|
||||
|
||||
ZonedDateTime now = ZonedDateTime.now(zoneId);
|
||||
ZoneRules rules = zoneId.getRules();
|
||||
int offsetSeconds = rules.getOffset(now.toInstant()).getTotalSeconds();
|
||||
int hours = offsetSeconds / 3600;
|
||||
int minutes = Math.abs((offsetSeconds % 3600) / 60);
|
||||
String offset = String.format("UTC%+d:%02d", hours, minutes);
|
||||
|
||||
String displayName = COMMON_TIMEZONE_NAMES.getOrDefault(
|
||||
zoneId.getId(),
|
||||
zoneId.getDisplayName(TextStyle.FULL, Locale.CHINESE)
|
||||
);
|
||||
|
||||
TimeZoneInfo info = new TimeZoneInfo(
|
||||
zoneId.getId(),
|
||||
displayName,
|
||||
offset,
|
||||
rules.isDaylightSavings(now.toInstant())
|
||||
);
|
||||
|
||||
log.debug("时区信息获取完成:{}", info);
|
||||
return info;
|
||||
}
|
||||
|
||||
public static class TimeZoneInfo {
|
||||
private final String id;
|
||||
private final String displayName;
|
||||
private final String offset;
|
||||
private final boolean isDaylightSavingTime;
|
||||
|
||||
public TimeZoneInfo(String id, String displayName, String offset, boolean isDaylightSavingTime) {
|
||||
this.id = id;
|
||||
this.displayName = displayName;
|
||||
this.offset = offset;
|
||||
this.isDaylightSavingTime = isDaylightSavingTime;
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getDisplayName() {
|
||||
return displayName;
|
||||
}
|
||||
|
||||
public String getOffset() {
|
||||
return offset;
|
||||
}
|
||||
|
||||
public boolean isDaylightSavingTime() {
|
||||
return isDaylightSavingTime;
|
||||
}
|
||||
|
||||
public int getOffsetHours() {
|
||||
String offsetStr = offset.replace("UTC", "");
|
||||
int hours = Integer.parseInt(offsetStr.split(":")[0]);
|
||||
return hours;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("%s (%s) %s%s",
|
||||
displayName,
|
||||
id,
|
||||
offset,
|
||||
isDaylightSavingTime ? " [夏令时]" : "");
|
||||
}
|
||||
}
|
||||
}
|
||||
+107
@@ -0,0 +1,107 @@
|
||||
package io.destiny.biz.util;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.LocalTime;
|
||||
|
||||
public class TrueSolarTimeUtil {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(TrueSolarTimeUtil.class);
|
||||
private static final double BEIJING_LONGITUDE = 120.0;
|
||||
private static final int MINUTES_PER_DEGREE = 4;
|
||||
|
||||
public static LocalTime calculateTrueSolarTime(LocalTime beijingTime, double longitude) {
|
||||
log.debug("计算真太阳时 - 北京时间:{},经度:{}", beijingTime, longitude);
|
||||
double longitudeDiff = longitude - BEIJING_LONGITUDE;
|
||||
int timeDiffMinutes = (int) Math.round(longitudeDiff * MINUTES_PER_DEGREE);
|
||||
LocalTime trueSolarTime = beijingTime.plusMinutes(timeDiffMinutes);
|
||||
log.debug("真太阳时计算完成:{},时间差:{}分钟", trueSolarTime, timeDiffMinutes);
|
||||
return trueSolarTime;
|
||||
}
|
||||
|
||||
public static LocalDateTime calculateTrueSolarDateTime(LocalDateTime beijingDateTime, double longitude) {
|
||||
log.debug("计算真太阳日期时间 - 北京日期时间:{},经度:{}", beijingDateTime, longitude);
|
||||
LocalTime beijingTime = beijingDateTime.toLocalTime();
|
||||
LocalTime trueSolarTime = calculateTrueSolarTime(beijingTime, longitude);
|
||||
LocalDate date = beijingDateTime.toLocalDate();
|
||||
|
||||
int timeDiffMinutes = calculateTimeDifferenceMinutes(longitude);
|
||||
|
||||
if (timeDiffMinutes > 0 && trueSolarTime.isBefore(beijingTime)) {
|
||||
date = date.plusDays(1);
|
||||
log.debug("因时间差原因,日期向前调整1天");
|
||||
} else if (timeDiffMinutes < 0 && trueSolarTime.isAfter(beijingTime)) {
|
||||
date = date.minusDays(1);
|
||||
log.debug("因时间差原因,日期向后调整1天");
|
||||
}
|
||||
|
||||
LocalDateTime result = LocalDateTime.of(date, trueSolarTime);
|
||||
log.debug("真太阳日期时间计算完成:{}", result);
|
||||
return result;
|
||||
}
|
||||
|
||||
public static int calculateTimeDifferenceMinutes(double longitude) {
|
||||
log.debug("计算经度{}的时间差(分钟)", longitude);
|
||||
double longitudeDiff = longitude - BEIJING_LONGITUDE;
|
||||
int diffMinutes = (int) Math.round(longitudeDiff * MINUTES_PER_DEGREE);
|
||||
log.debug("时间差计算完成:{}分钟", diffMinutes);
|
||||
return diffMinutes;
|
||||
}
|
||||
|
||||
public static String getTimeDifferenceDescription(double longitude) {
|
||||
log.debug("生成经度{}的时间差描述", longitude);
|
||||
int diffMinutes = calculateTimeDifferenceMinutes(longitude);
|
||||
int hours = diffMinutes / 60;
|
||||
int minutes = Math.abs(diffMinutes % 60);
|
||||
|
||||
String description;
|
||||
if (diffMinutes == 0) {
|
||||
description = "与北京时间一致";
|
||||
} else if (diffMinutes > 0) {
|
||||
description = String.format("比北京时间快%d小时%d分钟", hours, minutes);
|
||||
} else {
|
||||
description = String.format("比北京时间慢%d小时%d分钟", Math.abs(hours), minutes);
|
||||
}
|
||||
|
||||
log.debug("时间差描述:{}", description);
|
||||
return description;
|
||||
}
|
||||
|
||||
public static boolean isEarlyMorning(LocalTime time) {
|
||||
boolean result = time.isAfter(LocalTime.of(23, 0)) || time.isBefore(LocalTime.of(1, 0));
|
||||
log.debug("检查时间{}是否为凌晨:{}", time, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
public static boolean isLateNight(LocalTime time) {
|
||||
boolean result = time.isAfter(LocalTime.of(1, 0)) && time.isBefore(LocalTime.of(5, 0));
|
||||
log.debug("检查时间{}是否为深夜:{}", time, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
public static String getZiweiHourBranch(LocalTime trueSolarTime) {
|
||||
log.debug("计算时间{}的紫微时支", trueSolarTime);
|
||||
int hour = trueSolarTime.getHour();
|
||||
|
||||
String hourBranch;
|
||||
if (hour >= 23 || hour < 1) hourBranch = "子时";
|
||||
else if (hour >= 1 && hour < 3) hourBranch = "丑时";
|
||||
else if (hour >= 3 && hour < 5) hourBranch = "寅时";
|
||||
else if (hour >= 5 && hour < 7) hourBranch = "卯时";
|
||||
else if (hour >= 7 && hour < 9) hourBranch = "辰时";
|
||||
else if (hour >= 9 && hour < 11) hourBranch = "巳时";
|
||||
else if (hour >= 11 && hour < 13) hourBranch = "午时";
|
||||
else if (hour >= 13 && hour < 15) hourBranch = "未时";
|
||||
else if (hour >= 15 && hour < 17) hourBranch = "申时";
|
||||
else if (hour >= 17 && hour < 19) hourBranch = "酉时";
|
||||
else if (hour >= 19 && hour < 21) hourBranch = "戌时";
|
||||
else if (hour >= 21 && hour < 23) hourBranch = "亥时";
|
||||
else hourBranch = "未知时辰";
|
||||
|
||||
log.debug("紫微时支计算完成:{}", hourBranch);
|
||||
return hourBranch;
|
||||
}
|
||||
}
|
||||
+1025
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user