feat: 完成记录功能开发

- 添加保存记录API到RecordController
- 修复DeductRecordRepository的HQL语法错误
- 更新前端api.js添加记录API
- 更新八字结果页面保存测算记录
- 完善记录列表页面显示历史记录
This commit is contained in:
xuan-bot committed 2026-03-07 23:50:38 +08:00
1 parent 683f55e4ca
commit 91a14ca6d1
10 files changed
+280 -127

No files matched your search

BIN
View File
Binary file not shown.
@@ -29,7 +29,18 @@ public class LiuYaoController {
setting.setLeapMonthType(request.getLeapMonthType() != null ? request.getLeapMonthType() : 0);
setting.setXuShiSuiType(request.getXuShiSuiType() != null ? request.getXuShiSuiType() : 0);
setting.setJieQiType(request.getJieQiType() != null ? request.getJieQiType() : 1);
setting.setLiuYaoYaoXiang(request.getLiuYaoYaoXiang() != null ? request.getLiuYaoYaoXiang() : "——,——,——,——,——,——");
setting.setYearGanZhiType(request.getYearGanZhiType() != null ? request.getYearGanZhiType() : 2);
setting.setMonthGanZhiType(request.getMonthGanZhiType() != null ? request.getMonthGanZhiType() : 1);
setting.setDayGanZhiType(request.getDayGanZhiType() != null ? request.getDayGanZhiType() : 0);
setting.setPaiPanType(request.getPaiPanType() != null ? request.getPaiPanType() : 0);
if (request.getLiuYaoYaoXiang() != null) {
String[] yaoXiang = request.getLiuYaoYaoXiang().split(",");
if (yaoXiang.length >= 6) {
setting.setLiuYao(yaoXiang[0].equals("—") || yaoXiang[0].equals("阳") ? 1 : 0);
setting.setWuYao(yaoXiang[1].equals("—") || yaoXiang[1].equals("阳") ? 1 : 0);
setting.setSiYao(yaoXiang[2].equals("—") || yaoXiang[2].equals("阳") ? 1 : 0);
setting.setSanYao(yaoXiang[3].equals("—") || yaoXiang[3].equals("阳") ? 1 : 0);
setting.setErYao(yaoXiang[4].equals("—") || yaoXiang[4].equals("阳") ? 1 : 0);
setting.setYiYao(yaoXiang[5].equals("—") || yaoXiang[5].equals("阳") ? 1 : 0);
}
@@ -17,6 +17,20 @@ public class RecordController {
@Autowired
private DeductRecordService recordService;
@PostMapping("/save")
public Map<String, Object> saveRecord(@RequestBody DeductRecord record) {
Map<String, Object> result = new HashMap<>();
try {
DeductRecord saved = recordService.saveRecord(record);
result.put("success", true);
result.put("data", saved);
} catch (Exception e) {
result.put("success", false);
result.put("error", e.getMessage());
}
return result;
}
@GetMapping("/list")
public Map<String, Object> getRecords(@RequestParam String userId) {
Map<String, Object> result = new HashMap<>();
@@ -148,3 +148,4 @@ public class TaLuoController {
public String getSpreadType() { return spreadType; }
public void setSpreadType(String spreadType) { this.spreadType = spreadType; }
}
}
@@ -10,39 +10,6 @@ import java.util.*;
@RestController
@RequestMapping("/api/ziwei")
private static final Map<String, String> ZHU_XING_JIE_DU = new HashMap<>();
private static final Map<String, String> GONG_WEI_JIE_DU = new HashMap<>();
static {
ZHU_XING_JIE_DU.put("紫微", "帝王之星,主权威领导,性格稳重有魄力,适合管理岗位,天生具有统御能力");
ZHU_XING_JIE_DU.put("天机", "智慧之星,主聪明机敏,善于策划分析,适合参谋顾问,思维活跃多变");
ZHU_XING_JIE_DU.put("太阳", "光明之星,主外向热情,事业心强,利于公职发展,贵人运旺");
ZHU_XING_JIE_DU.put("武曲", "财星,主刚毅果断,理财能力强,适合金融商业,财运亨通");
ZHU_XING_JIE_DU.put("天同", "福星,主温和仁慈,人缘好福气厚,适合服务行业,生活安逸");
ZHU_XING_JIE_DU.put("廉贞", "桃花星,主才华艺术,感情丰富,适合文艺创作,魅力十足");
ZHU_XING_JIE_DU.put("天府", "财库星,主稳重守成,善于理财积累,适合财务管理,财源稳定");
ZHU_XING_JIE_DU.put("太阴", "阴柔之星,主细腻敏感,直觉力强,适合文艺创作,财运平稳");
ZHU_XING_JIE_DU.put("贪狼", "欲望之星,主多才多艺,交际能力强,适合演艺销售,但需防贪欲");
ZHU_XING_JIE_DU.put("巨门", "口舌之星,主能言善辩,适合律师教师,但易招是非口舌");
ZHU_XING_JIE_DU.put("天相", "印星,主正直公正,适合公务员法律,贵人运佳,事业平稳");
ZHU_XING_JIE_DU.put("天梁", "荫星,主慈悲为怀,适合医疗教育,有长辈贵人,福泽深厚");
ZHU_XING_JIE_DU.put("七杀", "将星,主刚强果断,适合军警创业,有领导才能,但需防冲动");
ZHU_XING_JIE_DU.put("破军", "变动之星,主开创冒险,适合创业改革,变动多端,需稳中求进");
GONG_WEI_JIE_DU.put("命宫", "代表核心性格、人生基调、先天特质");
GONG_WEI_JIE_DU.put("兄弟宫", "代表兄弟姐妹关系、同辈交往、合作能力");
GONG_WEI_JIE_DU.put("夫妻宫", "代表婚姻感情、配偶特质、婚姻质量");
GONG_WEI_JIE_DU.put("子女宫", "代表子女运势、后代情况、桃花缘分");
GONG_WEI_JIE_DU.put("财帛宫", "代表财富来源、理财能力、财运起伏");
GONG_WEI_JIE_DU.put("疾厄宫", "代表健康状况、易患疾病、养生方向");
GONG_WEI_JIE_DU.put("迁移宫", "代表外出运势、人际交往、贵人运");
GONG_WEI_JIE_DU.put("交友宫", "代表朋友关系、下属能力、人际网络");
GONG_WEI_JIE_DU.put("官禄宫", "代表事业发展、职业方向、官运仕途");
GONG_WEI_JIE_DU.put("田宅宫", "代表房产运势、家庭环境、祖业继承");
GONG_WEI_JIE_DU.put("福德宫", "代表精神状态、福报厚薄、晚年运势");
GONG_WEI_JIE_DU.put("父母宫", "代表父母关系、长辈助力、学历背景");
}
public class ZiWeiController {
private final HunYuanAiService aiService = new HunYuanAiService();
@@ -76,125 +43,86 @@ public class ZiWeiController {
data.put("aiAnalysis", aiAnalysis);
result.put("success", true);
data.put("occupy", ziWei.getOccupy());
result.put("data", data);
} catch (Exception e) {
data.put("zao", ziWei.getZao());
result.put("success", false);
result.put("error", e.getMessage());
}
data.put("xingQi", ziWei.getXingQi());
data.put("jiJie", ziWei.getJiJie());
return result;
}
data.put("yueXiang", ziWei.getYueXiang());
data.put("yueJiang", ziWei.getYueJiang());
data.put("yueJiangShen", ziWei.getYueJiangShen());
data.put("wuBuYuShi", ziWei.getWuBuYuShi());
data.put("yearGan", ziWei.getYearGan());
data.put("monthGan", ziWei.getMonthGan());
data.put("dayGan", ziWei.getDayGan());
data.put("hourGan", ziWei.getHourGan());
data.put("yearZhi", ziWei.getYearZhi());
data.put("monthZhi", ziWei.getMonthZhi());
data.put("dayZhi", ziWei.getDayZhi());
data.put("hourZhi", ziWei.getHourZhi());
private Map<String, Object> buildZiWeiData(ZiWei ziWei) {
Map<String, Object> data = new HashMap<>();
data.put("name", ziWei.getName());
data.put("sex", ziWei.getSex());
data.put("age", ziWei.getAge());
data.put("yearGanZhiNaYin", ziWei.getYearGanZhiNaYin());
data.put("monthGanZhiNaYin", ziWei.getMonthGanZhiNaYin());
data.put("dayGanZhiNaYin", ziWei.getDayGanZhiNaYin());
data.put("hourGanZhiNaYin", ziWei.getHourGanZhiNaYin());
data.put("nanNvYinYang", ziWei.getNanNvYinYang());
data.put("mingGongGongWei", ziWei.getMingGongGongWei());
data.put("shenGongGongWei", ziWei.getShenGongGongWei());
data.put("solar", ziWei.getSolarStr2());
data.put("wuXingJu", ziWei.getWuXingJu());
data.put("lunar", ziWei.getLunarStr2());
data.put("shengXiao", ziWei.getShengXiao());
data.put("xingZuo", ziWei.getXingZuo());
data.put("wuXingJu", ziWei.getWuXingJu());
data.put("yearGanZhi", ziWei.getYearGanZhi());
data.put("monthGanZhi", ziWei.getMonthGanZhi());
data.put("dayGanZhi", ziWei.getDayGanZhi());
data.put("ziWeiXingGongWei", ziWei.getZiWeiXingGongWei());
data.put("tianFuXingGongWei", ziWei.getTianFuXingGongWei());
data.put("ziWeiXingGongDiZhi", ziWei.getZiWeiXingGongDiZhi());
data.put("tianFuXingGongDiZhi", ziWei.getTianFuXingGongDiZhi());
data.put("ziWeiXingXiZhuXing", ziWei.getZiWeiXingXiZhuXing());
data.put("tianFuXingXiZhuXing", ziWei.getTianFuXingXiZhuXing());
data.put("shiZhiZhuXing", ziWei.getShiZhiZhuXing());
data.put("yueZhiZhuXing", ziWei.getYueZhiZhuXing());
data.put("nianGanZhuXing", ziWei.getNianGanZhuXing());
data.put("nianZhiZhuXing", ziWei.getNianZhiZhuXing());
data.put("dayZhuXing", ziWei.getDayZhuXing());
data.put("shiErZhangSheng", ziWei.getShiErZhangSheng());
data.put("jieLuKongWang", ziWei.getJieLuKongWang());
data.put("xunZhongKongWang", ziWei.getXunZhongKongWang());
data.put("tianShangTianShiXing", ziWei.getTianShangTianShiXing());
data.put("daXian", ziWei.getDaXian());
data.put("xiaoXian", ziWei.getXiaoXian());
data.put("shiErGongBoShi", ziWei.getShiErGongBoShi());
data.put("liuNianSuiQianZhuXing", ziWei.getLiuNianSuiQianZhuXing());
data.put("liuNianJiangQianZhuXing", ziWei.getLiuNianJiangQianZhuXing());
data.put("hourGanZhi", ziWei.getHourGanZhi());
data.put("shiErGongZhuXingGuanXi", ziWei.getShiErGongZhuXingGuanXi());
data.put("yearGanZhiNaYin", ziWei.getYearGanZhiNaYin());
data.put("monthGanZhiNaYin", ziWei.getMonthGanZhiNaYin());
data.put("dayGanZhiNaYin", ziWei.getDayGanZhiNaYin());
data.put("hourGanZhiNaYin", ziWei.getHourGanZhiNaYin());
data.put("huaLuXingGongWei", ziWei.getHuaLuXingGongWei());
data.put("huaQuanXingGongWei", ziWei.getHuaQuanXingGongWei());
data.put("huaKeXingGongWei", ziWei.getHuaKeXingGongWei());
data.put("huaJiXingGongWei", ziWei.getHuaJiXingGongWei());
data.put("mingGongDiZhi", ziWei.getMingGongDiZhi());
data.put("shenGongDiZhi", ziWei.getShenGongDiZhi());
data.put("mingGongGongWei", ziWei.getMingGongGongWei());
data.put("shenGongGongWei", ziWei.getShenGongGongWei());
data.put("mingZhu", ziWei.getMingZhu());
data.put("shenZhu", ziWei.getShenZhu());
data.put("nanNvYinYang", ziWei.getNanNvYinYang());
data.put("shiErGongDiZhi", ziWei.getShiErGongDiZhi());
data.put("shiErGongTianGan", ziWei.getShiErGongTianGan());
data.put("shiErMingGong", ziWei.getShiErMingGong());
data.put("shiErShenGong", ziWei.getShiErShenGong());
data.put("shiErGongZhuXing", ziWei.getShiErGongZhuXing(false));
data.put("shiErGongSiHuaXing", ziWei.getShiErGongSiHuaXing());
data.put("shiErGongSiHuaXingGuanXi", ziWei.getShiErGongSiHuaXingGuanXi());
data.put("shiErZhangSheng", ziWei.getShiErZhangSheng());
data.put("daXian", ziWei.getDaXian());
data.put("mingZhu", ziWei.getMingZhu());
data.put("shenZhu", ziWei.getShenZhu());
data.put("ziDou", ziWei.getZiDou());
data.put("liuDou", ziWei.getLiuDou());
data.put("prevJie", ziWei.getPrevJie());
data.put("nextJie", ziWei.getNextJie());
data.put("chuShengJie", ziWei.getChuShengJie());
data.put("prevQi", ziWei.getPrevQi());
data.put("nextQi", ziWei.getNextQi());
data.put("chuShengQi", ziWei.getChuShengQi());
data.put("huaLuXingGongWeiJieDu", ziWei.getHuaLuXingGongWeiJieDu());
data.put("huaQuanXingGongWeiJieDu", ziWei.getHuaQuanXingGongWeiJieDu());
data.put("huaKeXingGongWeiJieDu", ziWei.getHuaKeXingGongWeiJieDu());
data.put("huaJiXingGongWeiJieDu", ziWei.getHuaJiXingGongWeiJieDu());
data.put("jieDu", buildJieDu(ziWei, shiErMingGong, shiErGongZhuXing));
return data;
}
private Map<String, Object> buildJieDu(ZiWei ziWei, List<String> shiErMingGong, List<List<String>> shiErGongZhuXing) {
Map<String, Object> jieDu = new HashMap<>();
int mingGongIndex = ziWei.getMingGongGongWei() - 1;
List<String> mingGongStars = shiErGongZhuXing.get(mingGongIndex);
public static class ZiWeiRequest {
private String name;
private String occupy;
private Integer sex;
private String date;
private Integer dateType;
private Integer leapMonthType;
private Integer xuShiSuiType;
private Integer jieQiType;
private Integer wuXingJuType;
private Integer yearGanZhiType;
private Integer monthGanZhiType;
private Integer dayGanZhiType;
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public String getOccupy() { return occupy; }
public void setOccupy(String occupy) { this.occupy = occupy; }
public Integer getSex() { return sex; }
public void setSex(Integer sex) { this.sex = sex; }
public String getDate() { return date; }
for (String star : mingGongStars) {
if (ZHU_XING_JIE_DU.containsKey(star)) {
public void setDate(String date) { this.date = date; }
public Integer getDateType() { return dateType; }
public void setDateType(Integer dateType) { this.dateType = dateType; }
public Integer getLeapMonthType() { return leapMonthType; }
public void setLeapMonthType(Integer leapMonthType) { this.leapMonthType = leapMonthType; }
@@ -1,5 +1,6 @@
package xuan.repository;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
@@ -15,6 +16,6 @@ public interface DeductRecordRepository extends JpaRepository<DeductRecord, Long
List<DeductRecord> findByUserIdAndTypeOrderByCreateTimeDesc(String userId, String type);
@Query("SELECT d FROM DeductRecord d WHERE d.userId = :userId ORDER BY d.createTime DESC LIMIT 10")
List<DeductRecord> findTop10ByUserIdOrderByCreateTimeDesc(@Param("userId") String userId);
@Query("SELECT d FROM DeductRecord d WHERE d.userId = :userId ORDER BY d.createTime DESC")
List<DeductRecord> findTop10ByUserIdOrderByCreateTimeDesc(@Param("userId") String userId, Pageable pageable);
}
@@ -1,6 +1,7 @@
package xuan.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import xuan.entity.DeductRecord;
@@ -33,7 +34,7 @@ public class DeductRecordService {
}
public List<DeductRecord> findRecentByUserId(String userId) {
return recordRepository.findTop10ByUserIdOrderByCreateTimeDesc(userId);
return recordRepository.findTop10ByUserIdOrderByCreateTimeDesc(userId, PageRequest.of(0, 10));
}
@Transactional
@@ -165,6 +165,7 @@
<script>
import { markdownToHtml } from '@/utils/markdownToHtml.js'
import api from '@/utils/api.js'
export default {
data() {
@@ -212,6 +213,7 @@ export default {
const app = getApp()
if (app.globalData && app.globalData.baziResult) {
this.result = app.globalData.baziResult
this.saveRecord()
} else {
uni.showToast({
title: '未获取到排盘数据',
@@ -243,6 +245,24 @@ export default {
return this.result.baZiKongWang[index]
}
return '-'
},
async saveRecord() {
try {
await api.recordApi.save({
type: 'bazi',
name: this.result.name || '某人',
sex: this.result.sex,
inputDate: this.result.solar,
inputData: JSON.stringify({
date: this.result.solar,
sex: this.result.sex
}),
resultData: JSON.stringify(this.result),
aiAnalysis: this.result.aiAnalysis
})
} catch (e) {
console.log('保存记录失败:', e)
}
}
}
}
+138 -10
View File
@@ -15,16 +15,21 @@
</view>
<view class="record-list" v-else>
<view class="record-item" v-for="(item, index) in records" :key="index" @click="viewDetail(item)">
<view class="record-icon" :style="{ background: item.color }">
<text class="record-icon-text">{{ item.icon }}</text>
<view class="record-item" v-for="(item, index) in records" :key="item.id" @click="viewDetail(item)">
<view class="record-icon" :style="{ background: getTypeColor(item.type) }">
<text class="record-icon-text">{{ getTypeIcon(item.type) }}</text>
</view>
<view class="record-content">
<text class="record-title">{{ item.title }}</text>
<text class="record-time">{{ item.time }}</text>
<text class="record-title">{{ item.name }} - {{ getTypeName(item.type) }}</text>
<text class="record-time">{{ formatTime(item.createTime) }}</text>
</view>
<view class="record-arrow">
<text class="arrow-text">›</text>
<view class="record-actions">
<view class="delete-btn" @click.stop="deleteRecord(item.id, index)">
<text class="delete-text">删除</text>
</view>
<view class="record-arrow">
<text class="arrow-text">›</text>
</view>
</view>
</view>
</view>
@@ -35,6 +40,7 @@
<script>
import CustomTabbar from '@/components/custom-tabbar/custom-tabbar.vue'
import api from '@/utils/api.js'
export default {
components: {
@@ -49,28 +55,133 @@ export default {
this.loadRecords()
},
methods: {
loadRecords() {
const savedRecords = uni.getStorageSync('deductRecords') || []
this.records = savedRecords
async loadRecords() {
try {
const data = await api.recordApi.getList()
this.records = data || []
} catch (e) {
console.log('加载记录失败:', e)
this.records = []
}
},
formatTime(time) {
if (!time) return ''
if (Array.isArray(time)) {
const [year, month, day, hour = 0, minute = 0] = time
return `${year}-${String(month).padStart(2, '0')}-${String(day).padStart(2, '0')} ${String(hour).padStart(2, '0')}:${String(minute).padStart(2, '0')}`
}
const date = new Date(time)
const year = date.getFullYear()
const month = String(date.getMonth() + 1).padStart(2, '0')
const day = String(date.getDate()).padStart(2, '0')
const hour = String(date.getHours()).padStart(2, '0')
const minute = String(date.getMinutes()).padStart(2, '0')
return `${year}-${month}-${day} ${hour}:${minute}`
},
getTypeIcon(type) {
const icons = {
bazi: '命',
ziwei: '紫',
liuyao: '卦',
meihua: '梅',
qimen: '奇',
daliuren: '壬',
taluo: '塔',
zhanxing: '星'
}
return icons[type] || '算'
},
getTypeColor(type) {
const colors = {
bazi: '#DC2626',
ziwei: '#8B5CF6',
liuyao: '#059669',
meihua: '#EC4899',
qimen: '#7C3AED',
daliuren: '#0891B2',
taluo: '#F59E0B',
zhanxing: '#3B82F6'
}
return colors[type] || '#6B7280'
},
getTypeName(type) {
const names = {
bazi: '八字命理',
ziwei: '紫微斗数',
liuyao: '六爻占卜',
meihua: '梅花易数',
qimen: '奇门遁甲',
daliuren: '大六壬',
taluo: '塔罗牌',
zhanxing: '西方占星'
}
return names[type] || '命理测算'
},
viewDetail(item) {
const app = getApp()
if (!app.globalData) {
app.globalData = {}
}
let resultData = {}
try {
resultData = JSON.parse(item.resultData || '{}')
} catch (e) {
resultData = {}
}
if (item.aiAnalysis) {
resultData.aiAnalysis = item.aiAnalysis
}
if (item.type === 'bazi') {
app.globalData.baziResult = resultData
uni.navigateTo({ url: '/pages/bazi/result' })
} else if (item.type === 'ziwei') {
app.globalData.ziweiResult = resultData
uni.navigateTo({ url: '/pages/ziwei/result' })
} else if (item.type === 'zhanxing') {
app.globalData.zhanxingResult = resultData
uni.navigateTo({ url: '/pages/zhanxing/result' })
} else if (item.type === 'taluo') {
app.globalData.taLuoResult = resultData
uni.navigateTo({ url: '/pages/taluo/result' })
} else if (item.type === 'liuyao') {
app.globalData.liuyaoResult = resultData
uni.navigateTo({ url: '/pages/liuyao/result' })
} else if (item.type === 'meihua') {
app.globalData.meihuaResult = resultData
uni.navigateTo({ url: '/pages/meihua/result' })
} else if (item.type === 'qimen') {
app.globalData.qimenResult = resultData
uni.navigateTo({ url: '/pages/qimen/result' })
} else if (item.type === 'daliuren') {
app.globalData.daliurenResult = resultData
uni.navigateTo({ url: '/pages/daliuren/result' })
}
},
async deleteRecord(id, index) {
uni.showModal({
title: '确认删除',
content: '确定要删除这条记录吗?',
success: async (res) => {
if (res.confirm) {
try {
await api.recordApi.delete(id)
this.records.splice(index, 1)
uni.showToast({
title: '删除成功',
icon: 'success'
})
} catch (e) {
uni.showToast({
title: '删除失败',
icon: 'none'
})
}
}
}
})
}
}
}
@@ -175,6 +286,8 @@ page {
.record-icon-text {
font-size: 40rpx;
color: #ffffff;
font-weight: bold;
}
.record-content {
@@ -194,6 +307,21 @@ page {
color: #9CA3AF;
}
.record-actions {
display: flex;
align-items: center;
}
.delete-btn {
padding: 12rpx 24rpx;
margin-right: 16rpx;
}
.delete-text {
font-size: 24rpx;
color: #EF4444;
}
.record-arrow {
width: 48rpx;
height: 48rpx;
+49
View File
@@ -176,6 +176,54 @@ const taLuoApi = {
}
}
const recordApi = {
save: (data) => {
return request({
url: '/records/save',
method: 'POST',
data: {
userId: data.userId || 'default_user',
type: data.type,
name: data.name,
occupy: data.occupy,
sex: data.sex,
inputDate: data.inputDate,
inputData: data.inputData,
resultData: data.resultData,
aiAnalysis: data.aiAnalysis
}
})
},
getList: (userId) => {
return request({
url: '/records/list',
method: 'GET',
data: { userId: userId || 'default_user' }
})
},
getDetail: (id) => {
return request({
url: '/records/detail',
method: 'GET',
data: { id: id }
})
},
delete: (id) => {
return request({
url: '/records/delete',
method: 'DELETE',
data: { id: id }
})
},
clear: (userId) => {
return request({
url: '/records/clear',
method: 'DELETE',
data: { userId: userId || 'default_user' }
})
}
}
export default {
baziApi,
liuyaoApi,
@@ -184,5 +232,6 @@ export default {
ziweiApi,
zhanxingApi,
taLuoApi,
recordApi,
request
}