diff --git a/src/main/java/xuan/controller/RecordController.java b/src/main/java/xuan/controller/RecordController.java index bd7e1ca..737fb30 100644 --- a/src/main/java/xuan/controller/RecordController.java +++ b/src/main/java/xuan/controller/RecordController.java @@ -1,6 +1,9 @@ package xuan.controller; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; import org.springframework.web.bind.annotation.*; import xuan.entity.DeductRecord; import xuan.service.DeductRecordService; @@ -32,12 +35,26 @@ public class RecordController { } @GetMapping("/list") - public Map getRecords(@RequestParam String userId) { + public Map getRecords( + @RequestParam String userId, + @RequestParam(defaultValue = "1") int page, + @RequestParam(defaultValue = "10") int size, + @RequestParam(required = false) String keyword, + @RequestParam(required = false) String type) { Map result = new HashMap<>(); try { - List records = recordService.findByUserId(userId); + Pageable pageable = PageRequest.of(page - 1, size); + Page pageResult = recordService.findByUserIdWithSearch(userId, keyword, type, pageable); + + Map data = new HashMap<>(); + data.put("records", pageResult.getContent()); + data.put("total", pageResult.getTotalElements()); + data.put("page", page); + data.put("size", size); + data.put("totalPages", pageResult.getTotalPages()); + result.put("success", true); - result.put("data", records); + result.put("data", data); } catch (Exception e) { result.put("success", false); result.put("error", e.getMessage()); @@ -109,6 +126,21 @@ public class RecordController { return result; } + @PostMapping("/deleteBatch") + public Map deleteBatch(@RequestBody Map params) { + Map result = new HashMap<>(); + try { + List ids = (List) params.get("ids"); + recordService.deleteByIds(ids); + result.put("success", true); + result.put("message", "批量删除成功"); + } catch (Exception e) { + result.put("success", false); + result.put("error", e.getMessage()); + } + return result; + } + @PostMapping("/clear") public Map clearRecords(@RequestBody Map params) { Map result = new HashMap<>(); diff --git a/src/main/java/xuan/controller/ShareController.java b/src/main/java/xuan/controller/ShareController.java index 3748250..3c34582 100644 --- a/src/main/java/xuan/controller/ShareController.java +++ b/src/main/java/xuan/controller/ShareController.java @@ -1,402 +1,76 @@ package xuan.controller; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; -import java.util.*; +import xuan.entity.ShareRecord; +import xuan.repository.ShareRecordRepository; + +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; @RestController @RequestMapping("/api/share") public class ShareController { - - @PostMapping("/bazi") - public Map shareBazi(@RequestBody ShareRequest request) { + + @Autowired + private ShareRecordRepository shareRecordRepository; + + @PostMapping("/create") + public Map createShare(@RequestBody Map params) { Map result = new HashMap<>(); + try { - Map data = request.getData(); + String type = (String) params.get("type"); + String resultData = (String) params.get("resultData"); + String name = (String) params.get("name"); - StringBuilder shareText = new StringBuilder(); - shareText.append("【八字命理分析】\n\n"); + ShareRecord shareRecord = new ShareRecord(); + shareRecord.setShareId(UUID.randomUUID().toString().replace("-", "").substring(0, 16)); + shareRecord.setType(type); + shareRecord.setResultData(resultData); + shareRecord.setName(name); - if (data.containsKey("name")) { - shareText.append("👤 姓名:").append(data.get("name")).append("\n"); - } - if (data.containsKey("solar")) { - shareText.append("📅 出生时间:").append(data.get("solar")).append("\n"); - } - if (data.containsKey("bazi")) { - Map bazi = (Map) data.get("bazi"); - shareText.append("\n【八字排盘】\n"); - shareText.append("年柱:").append(bazi.get("yearGan")).append(bazi.get("yearZhi")).append("\n"); - shareText.append("月柱:").append(bazi.get("monthGan")).append(bazi.get("monthZhi")).append("\n"); - shareText.append("日柱:").append(bazi.get("dayGan")).append(bazi.get("dayZhi")).append("\n"); - shareText.append("时柱:").append(bazi.get("hourGan")).append(bazi.get("hourZhi")).append("\n"); - } - - if (data.containsKey("interpretation")) { - Map interpretation = (Map) data.get("interpretation"); - shareText.append("\n【命理解读】\n"); - if (interpretation.containsKey("personality")) { - shareText.append("\n性格特质:\n").append(interpretation.get("personality")).append("\n"); - } - if (interpretation.containsKey("career")) { - shareText.append("\n事业发展:\n").append(interpretation.get("career")).append("\n"); - } - if (interpretation.containsKey("marriage")) { - shareText.append("\n婚姻感情:\n").append(interpretation.get("marriage")).append("\n"); - } - if (interpretation.containsKey("wealth")) { - shareText.append("\n财运状况:\n").append(interpretation.get("wealth")).append("\n"); - } - if (interpretation.containsKey("health")) { - shareText.append("\n健康建议:\n").append(interpretation.get("health")).append("\n"); - } - if (interpretation.containsKey("advice")) { - shareText.append("\n人生建议:\n").append(interpretation.get("advice")).append("\n"); - } - } - - shareText.append("\n——来自玄学测算APP"); + shareRecordRepository.save(shareRecord); result.put("success", true); - result.put("data", shareText.toString()); + result.put("shareId", shareRecord.getShareId()); + result.put("message", "分享链接创建成功"); } catch (Exception e) { result.put("success", false); - result.put("error", e.getMessage()); + result.put("error", "创建分享链接失败:" + e.getMessage()); } + return result; } - - @PostMapping("/ziwei") - public Map shareZiwei(@RequestBody ShareRequest request) { + + @GetMapping("/view/{shareId}") + public Map viewShare(@PathVariable String shareId) { Map result = new HashMap<>(); + try { - Map data = request.getData(); + ShareRecord shareRecord = shareRecordRepository.findByShareId(shareId) + .orElse(null); - StringBuilder shareText = new StringBuilder(); - shareText.append("【紫微斗数命盘】\n\n"); - - if (data.containsKey("name")) { - shareText.append("👤 姓名:").append(data.get("name")).append("\n"); - } - if (data.containsKey("solar")) { - shareText.append("📅 出生时间:").append(data.get("solar")).append("\n"); + if (shareRecord == null) { + result.put("success", false); + result.put("error", "分享链接不存在或已过期"); + return result; } - if (data.containsKey("mingGong")) { - shareText.append("\n【命宫主星】\n").append(data.get("mingGong")).append("\n"); + if (shareRecord.getExpireTime() != null && shareRecord.getExpireTime().before(new java.util.Date())) { + result.put("success", false); + result.put("error", "分享链接已过期"); + return result; } - if (data.containsKey("interpretation")) { - Map interpretation = (Map) data.get("interpretation"); - shareText.append("\n【命理解读】\n"); - if (interpretation.containsKey("personality")) { - shareText.append("\n性格特质:\n").append(interpretation.get("personality")).append("\n"); - } - if (interpretation.containsKey("career")) { - shareText.append("\n事业发展:\n").append(interpretation.get("career")).append("\n"); - } - if (interpretation.containsKey("wealth")) { - shareText.append("\n财运状况:\n").append(interpretation.get("wealth")).append("\n"); - } - if (interpretation.containsKey("marriage")) { - shareText.append("\n婚姻感情:\n").append(interpretation.get("marriage")).append("\n"); - } - } - - shareText.append("\n——来自玄学测算APP"); - result.put("success", true); - result.put("data", shareText.toString()); + result.put("data", shareRecord); } catch (Exception e) { result.put("success", false); - result.put("error", e.getMessage()); + result.put("error", "获取分享数据失败:" + e.getMessage()); } + return result; } - - @PostMapping("/zhanxing") - public Map shareZhanxing(@RequestBody ShareRequest request) { - Map result = new HashMap<>(); - try { - Map data = request.getData(); - - StringBuilder shareText = new StringBuilder(); - shareText.append("【星盘解析】\n\n"); - - if (data.containsKey("name")) { - shareText.append("👤 姓名:").append(data.get("name")).append("\n"); - } - - shareText.append("\n【核心星座】\n"); - if (data.containsKey("sunSign")) { - shareText.append("☀️ 太阳星座:").append(data.get("sunSign")).append("\n"); - } - if (data.containsKey("moonSign")) { - shareText.append("🌙 月亮星座:").append(data.get("moonSign")).append("\n"); - } - if (data.containsKey("risingSign")) { - shareText.append("⬆️ 上升星座:").append(data.get("risingSign")).append("\n"); - } - - if (data.containsKey("interpretation")) { - Map interpretation = (Map) data.get("interpretation"); - shareText.append("\n【命理解读】\n"); - if (interpretation.containsKey("personality")) { - shareText.append("\n性格特质:\n").append(interpretation.get("personality")).append("\n"); - } - if (interpretation.containsKey("love")) { - shareText.append("\n感情运势:\n").append(interpretation.get("love")).append("\n"); - } - if (interpretation.containsKey("career")) { - shareText.append("\n事业发展:\n").append(interpretation.get("career")).append("\n"); - } - if (interpretation.containsKey("wealth")) { - shareText.append("\n财运状况:\n").append(interpretation.get("wealth")).append("\n"); - } - if (interpretation.containsKey("health")) { - shareText.append("\n健康建议:\n").append(interpretation.get("health")).append("\n"); - } - if (interpretation.containsKey("advice")) { - shareText.append("\n人生建议:\n").append(interpretation.get("advice")).append("\n"); - } - } - - shareText.append("\n——来自玄学测算APP"); - - result.put("success", true); - result.put("data", shareText.toString()); - } catch (Exception e) { - result.put("success", false); - result.put("error", e.getMessage()); - } - return result; - } - - @PostMapping("/taluo") - public Map shareTaluo(@RequestBody ShareRequest request) { - Map result = new HashMap<>(); - try { - Map data = request.getData(); - - StringBuilder shareText = new StringBuilder(); - shareText.append("【塔罗牌占卜结果】\n\n"); - - if (data.containsKey("spreadName")) { - shareText.append("🔮 牌阵:").append(data.get("spreadName")).append("\n"); - } - if (data.containsKey("question")) { - shareText.append("❓ 问题:").append(data.get("question")).append("\n"); - } - - if (data.containsKey("cards")) { - List> cards = (List>) data.get("cards"); - shareText.append("\n【抽到的牌】\n"); - for (Map card : cards) { - String position = (Boolean) card.get("isReversed") ? "逆位" : "正位"; - shareText.append(card.get("positionMeaning")).append(":") - .append(card.get("name")).append("(").append(position).append(")\n"); - } - } - - if (data.containsKey("interpretation")) { - shareText.append("\n【牌义解读】\n").append(data.get("interpretation")).append("\n"); - } - - if (data.containsKey("summary")) { - Map summary = (Map) data.get("summary"); - shareText.append("\n【综合解读】\n"); - if (summary.containsKey("personality")) { - shareText.append("\n性格特质:\n").append(summary.get("personality")).append("\n"); - } - if (summary.containsKey("emotion")) { - shareText.append("\n感情运势:\n").append(summary.get("emotion")).append("\n"); - } - if (summary.containsKey("career")) { - shareText.append("\n事业发展:\n").append(summary.get("career")).append("\n"); - } - if (summary.containsKey("overall")) { - shareText.append("\n总体建议:\n").append(summary.get("overall")).append("\n"); - } - } - - shareText.append("\n——来自玄学测算APP"); - - result.put("success", true); - result.put("data", shareText.toString()); - } catch (Exception e) { - result.put("success", false); - result.put("error", e.getMessage()); - } - return result; - } - - @PostMapping("/meihua") - public Map shareMeihua(@RequestBody ShareRequest request) { - Map result = new HashMap<>(); - try { - Map data = request.getData(); - - StringBuilder shareText = new StringBuilder(); - shareText.append("【梅花易数占卜结果】\n\n"); - - if (data.containsKey("name")) { - shareText.append("👤 占卜者:").append(data.get("name")).append("\n"); - } - if (data.containsKey("solar")) { - shareText.append("📅 时间:").append(data.get("solar")).append("\n"); - } - if (data.containsKey("lunar")) { - shareText.append("🌙 农历:").append(data.get("lunar")).append("\n"); - } - - shareText.append("\n【卦象总览】\n"); - if (data.containsKey("benGuaName")) { - shareText.append("本卦:").append(data.get("benGuaName")); - if (data.containsKey("benGuaAs")) { - shareText.append(" ").append(data.get("benGuaAs")); - } - shareText.append("\n"); - } - if (data.containsKey("bianGuaName")) { - shareText.append("变卦:").append(data.get("bianGuaName")); - if (data.containsKey("bianGuaAs")) { - shareText.append(" ").append(data.get("bianGuaAs")); - } - shareText.append("\n"); - } - if (data.containsKey("huGuaName")) { - shareText.append("互卦:").append(data.get("huGuaName")); - if (data.containsKey("huGuaAs")) { - shareText.append(" ").append(data.get("huGuaAs")); - } - shareText.append("\n"); - } - if (data.containsKey("dongYaoStr")) { - shareText.append("动爻:").append(data.get("dongYaoStr")).append("爻\n"); - } - - if (data.containsKey("benGuaGuaCi")) { - shareText.append("\n【本卦卦辞】\n").append(data.get("benGuaGuaCi")).append("\n"); - } - if (data.containsKey("bianGuaGuaCi")) { - shareText.append("\n【变卦卦辞】\n").append(data.get("bianGuaGuaCi")).append("\n"); - } - - shareText.append("\n——来自玄学测算APP"); - - result.put("success", true); - result.put("data", shareText.toString()); - } catch (Exception e) { - result.put("success", false); - result.put("error", e.getMessage()); - } - return result; - } - - @PostMapping("/liuyao") - public Map shareLiuyao(@RequestBody ShareRequest request) { - Map result = new HashMap<>(); - try { - Map data = request.getData(); - - StringBuilder shareText = new StringBuilder(); - shareText.append("【六爻占卜结果】\n\n"); - - if (data.containsKey("name")) { - shareText.append("👤 占卜者:").append(data.get("name")).append("\n"); - } - if (data.containsKey("solar")) { - shareText.append("📅 时间:").append(data.get("solar")).append("\n"); - } - - if (data.containsKey("guaName")) { - shareText.append("\n【卦象】\n").append(data.get("guaName")).append("\n"); - } - - if (data.containsKey("interpretation")) { - shareText.append("\n【卦辞解读】\n").append(data.get("interpretation")).append("\n"); - } - - shareText.append("\n——来自玄学测算APP"); - - result.put("success", true); - result.put("data", shareText.toString()); - } catch (Exception e) { - result.put("success", false); - result.put("error", e.getMessage()); - } - return result; - } - - @PostMapping("/qimen") - public Map shareQimen(@RequestBody ShareRequest request) { - Map result = new HashMap<>(); - try { - Map data = request.getData(); - - StringBuilder shareText = new StringBuilder(); - shareText.append("【奇门遁甲排盘】\n\n"); - - if (data.containsKey("name")) { - shareText.append("👤 占卜者:").append(data.get("name")).append("\n"); - } - if (data.containsKey("solar")) { - shareText.append("📅 时间:").append(data.get("solar")).append("\n"); - } - - if (data.containsKey("juName")) { - shareText.append("\n【局数】\n").append(data.get("juName")).append("\n"); - } - - if (data.containsKey("interpretation")) { - shareText.append("\n【盘面解读】\n").append(data.get("interpretation")).append("\n"); - } - - shareText.append("\n——来自玄学测算APP"); - - result.put("success", true); - result.put("data", shareText.toString()); - } catch (Exception e) { - result.put("success", false); - result.put("error", e.getMessage()); - } - return result; - } - - @PostMapping("/daliuren") - public Map shareDaliuren(@RequestBody ShareRequest request) { - Map result = new HashMap<>(); - try { - Map data = request.getData(); - - StringBuilder shareText = new StringBuilder(); - shareText.append("【大六壬排盘】\n\n"); - - if (data.containsKey("name")) { - shareText.append("👤 占卜者:").append(data.get("name")).append("\n"); - } - if (data.containsKey("solar")) { - shareText.append("📅 时间:").append(data.get("solar")).append("\n"); - } - - if (data.containsKey("interpretation")) { - shareText.append("\n【盘面解读】\n").append(data.get("interpretation")).append("\n"); - } - - shareText.append("\n——来自玄学测算APP"); - - result.put("success", true); - result.put("data", shareText.toString()); - } catch (Exception e) { - result.put("success", false); - result.put("error", e.getMessage()); - } - return result; - } - - public static class ShareRequest { - private Map data; - - public Map getData() { return data; } - public void setData(Map data) { this.data = data; } - } } diff --git a/src/main/java/xuan/controller/TaLuoController.java b/src/main/java/xuan/controller/TaLuoController.java index 1c472fe..643a471 100644 --- a/src/main/java/xuan/controller/TaLuoController.java +++ b/src/main/java/xuan/controller/TaLuoController.java @@ -1,17 +1,12 @@ package xuan.controller; import org.springframework.web.bind.annotation.*; -import xuan.service.HunYuanAiService; -import xuan.service.PromptTemplates; - import java.util.*; @RestController @RequestMapping("/api/taluo") public class TaLuoController { - private final HunYuanAiService aiService = new HunYuanAiService(); - private static final List> MAJOR_ARCANA = Arrays.asList( createCard(0, "愚者", "The Fool", "新的开始、冒险、天真、自由", "鲁莽、冲动、不负责任", "代表新的旅程和无限可能"), createCard(1, "魔术师", "The Magician", "创造力、技能、意志力、自信", "欺骗、操纵、缺乏计划", "代表运用技能实现目标"), @@ -37,6 +32,8 @@ public class TaLuoController { createCard(21, "世界", "The World", "完成、成就、整合、圆满", "未完成、缺乏闭合、延迟成功", "代表完成和圆满") ); + private static final List> MINOR_ARCANA = createMinorArcana(); + private static Map createCard(int number, String name, String englishName, String uprightMeaning, String reversedMeaning, String description) { Map card = new HashMap<>(); @@ -46,10 +43,82 @@ public class TaLuoController { card.put("uprightMeaning", uprightMeaning); card.put("reversedMeaning", reversedMeaning); card.put("description", description); - card.put("type", "major"); + card.put("type", number < 22 ? "major" : "minor"); return card; } + private static List> createMinorArcana() { + List> cards = new ArrayList<>(); + + String[] suits = {"权杖", "圣杯", "宝剑", "星币"}; + String[] suitMeanings = {"行动与创造", "情感与关系", "思想与沟通", "物质与现实"}; + String[] ranks = {"Ace", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight", "Nine", "Ten", "侍从", "骑士", "王后", "国王"}; + + int cardNumber = 22; + for (int suitIndex = 0; suitIndex < suits.length; suitIndex++) { + String suit = suits[suitIndex]; + String suitMeaning = suitMeanings[suitIndex]; + + for (int rankIndex = 0; rankIndex < ranks.length; rankIndex++) { + String rank = ranks[rankIndex]; + String cardName = suit + rank; + + Map card = new HashMap<>(); + card.put("number", cardNumber++); + card.put("name", cardName); + card.put("englishName", rank + " of " + getSuitEnglish(suit)); + card.put("suit", suit); + card.put("rank", rank); + card.put("uprightMeaning", generateUprightMeaning(suit, rank, suitMeaning)); + card.put("reversedMeaning", generateReversedMeaning(suit, rank, suitMeaning)); + card.put("description", suitMeaning + "领域的" + getRankDescription(rank)); + card.put("type", "minor"); + + cards.add(card); + } + } + + return cards; + } + + private static String getSuitEnglish(String suit) { + switch (suit) { + case "权杖": return "Wands"; + case "圣杯": return "Cups"; + case "宝剑": return "Swords"; + case "星币": return "Pentacles"; + default: return suit; + } + } + + private static String getRankDescription(String rank) { + switch (rank) { + case "Ace": return "新的开始"; + case "Two": return "选择与平衡"; + case "Three": return "发展与创造"; + case "Four": return "稳定与基础"; + case "Five": return "冲突与挑战"; + case "Six": return "和谐与胜利"; + case "Seven": return "评估与选择"; + case "Eight": return "行动与速度"; + case "Nine": return "接近完成"; + case "Ten": return "完成与圆满"; + case "侍从": return "学习与探索"; + case "骑士": return "行动与追求"; + case "王后": return "内在智慧"; + case "国王": return "掌控与权威"; + default: return rank; + } + } + + private static String generateUprightMeaning(String suit, String rank, String suitMeaning) { + return suitMeaning + "方面的" + getRankDescription(rank) + ",正位时能量流动顺畅"; + } + + private static String generateReversedMeaning(String suit, String rank, String suitMeaning) { + return suitMeaning + "方面的" + getRankDescription(rank) + ",逆位时需要反思调整"; + } + @PostMapping("/divine") public Map divine(@RequestBody TaLuoRequest request) { Map result = new HashMap<>(); @@ -57,7 +126,9 @@ public class TaLuoController { String spreadType = request.getSpreadType() != null ? request.getSpreadType() : "single"; int cardCount = getCardCount(spreadType); - List> allCards = new ArrayList<>(MAJOR_ARCANA); + List> allCards = new ArrayList<>(); + allCards.addAll(MAJOR_ARCANA); + allCards.addAll(MINOR_ARCANA); Collections.shuffle(allCards, new Random()); @@ -76,14 +147,9 @@ public class TaLuoController { data.put("spreadName", getSpreadName(spreadType)); data.put("question", request.getQuestion()); data.put("cards", selectedCards); + data.put("interpretation", generateInterpretation(selectedCards, spreadType, request.getQuestion())); data.put("timestamp", new Date()); - String aiAnalysis = aiService.chat( - PromptTemplates.getTaLuoSystemPrompt(), - PromptTemplates.getTaLuoUserPrompt(data) - ); - data.put("aiAnalysis", aiAnalysis); - result.put("success", true); result.put("data", data); } catch (Exception e) { @@ -139,6 +205,109 @@ public class TaLuoController { return "未知"; } + private String generateInterpretation(List> cards, String spreadType, String question) { + StringBuilder interpretation = new StringBuilder(); + + if ("single".equals(spreadType)) { + Map card = cards.get(0); + String name = (String) card.get("name"); + boolean isReversed = (Boolean) card.get("isReversed"); + String meaning = isReversed ? (String) card.get("reversedMeaning") : (String) card.get("uprightMeaning"); + + interpretation.append("您抽到的牌是【").append(name).append("】"); + interpretation.append(isReversed ? "(逆位)" : "(正位)").append("。\n\n"); + interpretation.append("牌义解读:").append(meaning).append("。\n\n"); + interpretation.append((String) card.get("description")); + } else if ("three".equals(spreadType)) { + interpretation.append("三张牌阵解读:\n\n"); + + for (Map card : cards) { + String name = (String) card.get("name"); + boolean isReversed = (Boolean) card.get("isReversed"); + String position = (String) card.get("positionMeaning"); + String meaning = isReversed ? (String) card.get("reversedMeaning") : (String) card.get("uprightMeaning"); + + interpretation.append("【").append(position).append("】"); + interpretation.append(name).append(isReversed ? "(逆位)" : "(正位)").append("\n"); + interpretation.append(meaning).append("\n\n"); + } + + interpretation.append("综合解读:从过去到现在再到未来,牌面展示了事情的发展脉络。"); + } else if ("celtic".equals(spreadType)) { + interpretation.append("凯尔特十字牌阵解读:\n\n"); + interpretation.append("核心区域(位置1-6):\n"); + + for (int i = 0; i < 6; i++) { + Map card = cards.get(i); + String name = (String) card.get("name"); + boolean isReversed = (Boolean) card.get("isReversed"); + String position = (String) card.get("positionMeaning"); + String meaning = isReversed ? (String) card.get("reversedMeaning") : (String) card.get("uprightMeaning"); + + interpretation.append("【").append(position).append("】"); + interpretation.append(name).append(isReversed ? "(逆位)" : "(正位)").append(":"); + interpretation.append(meaning).append("\n"); + } + + interpretation.append("\n延伸区域(位置7-10):\n"); + + for (int i = 6; i < 10; i++) { + Map card = cards.get(i); + String name = (String) card.get("name"); + boolean isReversed = (Boolean) card.get("isReversed"); + String position = (String) card.get("positionMeaning"); + String meaning = isReversed ? (String) card.get("reversedMeaning") : (String) card.get("uprightMeaning"); + + interpretation.append("【").append(position).append("】"); + interpretation.append(name).append(isReversed ? "(逆位)" : "(正位)").append(":"); + interpretation.append(meaning).append("\n"); + } + + interpretation.append("\n综合解读:凯尔特十字牌阵为您提供了全方位的视角,帮助您深入理解问题的各个方面。"); + } + + return interpretation.toString(); + } + + @GetMapping("/cards") + public Map getAllCards() { + Map result = new HashMap<>(); + try { + List> allCards = new ArrayList<>(); + allCards.addAll(MAJOR_ARCANA); + allCards.addAll(MINOR_ARCANA); + + result.put("success", true); + result.put("data", allCards); + } catch (Exception e) { + result.put("success", false); + result.put("error", e.getMessage()); + } + return result; + } + + @GetMapping("/card/{number}") + public Map getCard(@PathVariable int number) { + Map result = new HashMap<>(); + try { + List> allCards = new ArrayList<>(); + allCards.addAll(MAJOR_ARCANA); + allCards.addAll(MINOR_ARCANA); + + if (number >= 0 && number < allCards.size()) { + result.put("success", true); + result.put("data", allCards.get(number)); + } else { + result.put("success", false); + result.put("error", "Card not found"); + } + } catch (Exception e) { + result.put("success", false); + result.put("error", e.getMessage()); + } + return result; + } + public static class TaLuoRequest { private String question; private String spreadType; diff --git a/src/main/java/xuan/repository/DeductRecordRepository.java b/src/main/java/xuan/repository/DeductRecordRepository.java index d22bc8c..d419937 100644 --- a/src/main/java/xuan/repository/DeductRecordRepository.java +++ b/src/main/java/xuan/repository/DeductRecordRepository.java @@ -1,5 +1,6 @@ package xuan.repository; +import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; @@ -14,8 +15,18 @@ public interface DeductRecordRepository extends JpaRepository findByUserIdOrderByCreateTimeDesc(String userId); + Page findByUserIdOrderByCreateTimeDesc(String userId, Pageable pageable); + List findByUserIdAndTypeOrderByCreateTimeDesc(String userId, String type); + Page findByUserIdAndType(String userId, String type, Pageable pageable); + @Query("SELECT d FROM DeductRecord d WHERE d.userId = :userId ORDER BY d.createTime DESC") List findTop10ByUserIdOrderByCreateTimeDesc(@Param("userId") String userId, Pageable pageable); + + @Query("SELECT d FROM DeductRecord d WHERE d.userId = :userId AND (d.name LIKE %:keyword% OR d.occupy LIKE %:keyword%) ORDER BY d.createTime DESC") + Page findByUserIdAndKeyword(@Param("userId") String userId, @Param("keyword") String keyword, Pageable pageable); + + @Query("SELECT d FROM DeductRecord d WHERE d.userId = :userId AND d.type = :type AND (d.name LIKE %:keyword% OR d.occupy LIKE %:keyword%) ORDER BY d.createTime DESC") + Page findByUserIdAndTypeAndKeyword(@Param("userId") String userId, @Param("type") String type, @Param("keyword") String keyword, Pageable pageable); } diff --git a/src/main/java/xuan/service/DeductRecordService.java b/src/main/java/xuan/service/DeductRecordService.java index cab3124..d36c283 100644 --- a/src/main/java/xuan/service/DeductRecordService.java +++ b/src/main/java/xuan/service/DeductRecordService.java @@ -1,7 +1,9 @@ package xuan.service; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import xuan.entity.DeductRecord; @@ -29,6 +31,18 @@ public class DeductRecordService { return recordRepository.findByUserIdOrderByCreateTimeDesc(userId); } + public Page findByUserIdWithSearch(String userId, String keyword, String type, Pageable pageable) { + if (keyword != null && !keyword.trim().isEmpty() && type != null && !type.trim().isEmpty()) { + return recordRepository.findByUserIdAndTypeAndKeyword(userId, type, keyword, pageable); + } else if (keyword != null && !keyword.trim().isEmpty()) { + return recordRepository.findByUserIdAndKeyword(userId, keyword, pageable); + } else if (type != null && !type.trim().isEmpty()) { + return recordRepository.findByUserIdAndType(userId, type, pageable); + } else { + return recordRepository.findByUserIdOrderByCreateTimeDesc(userId, pageable); + } + } + public List findByUserIdAndType(String userId, String type) { return recordRepository.findByUserIdAndTypeOrderByCreateTimeDesc(userId, type); } @@ -42,6 +56,11 @@ public class DeductRecordService { recordRepository.deleteById(id); } + @Transactional + public void deleteByIds(List ids) { + recordRepository.deleteAllById(ids); + } + @Transactional public void deleteByUserId(String userId) { List records = recordRepository.findByUserIdOrderByCreateTimeDesc(userId); diff --git a/xuan-deduct-uniapp/src/pages/records/records.vue b/xuan-deduct-uniapp/src/pages/records/records.vue index 5533ee5..5493c0b 100644 --- a/xuan-deduct-uniapp/src/pages/records/records.vue +++ b/xuan-deduct-uniapp/src/pages/records/records.vue @@ -8,20 +8,54 @@ 查看历史测算结果 - + + + + + + + + + ✓ + + 全选 + + + + + + + + 📝 暂无测算记录 开始您的第一次测算吧 - - - {{ getTypeIcon(item.type) }} + + + + ✓ + - - {{ item.name }} - {{ getTypeName(item.type) }} - {{ formatTime(item.createTime) }} + + + {{ getTypeIcon(item.type) }} + + + {{ item.name }} - {{ getTypeName(item.type) }} + {{ formatTime(item.createTime) }} + @@ -34,6 +68,12 @@ + + + {{ page }} / {{ totalPages }} + + + @@ -48,7 +88,19 @@ export default { }, data() { return { - records: [] + records: [], + page: 1, + size: 10, + total: 0, + totalPages: 0, + keyword: '', + selectedIds: [], + loading: false + } + }, + computed: { + isAllSelected() { + return this.records.length > 0 && this.selectedIds.length === this.records.length } }, onShow() { @@ -56,23 +108,153 @@ export default { }, methods: { async loadRecords() { + if (this.loading) return + + this.loading = true try { - console.log('加载测算记录...') - const data = await api.recordApi.getList() + console.log('加载测算记录...', { page: this.page, size: this.size, keyword: this.keyword }) + + const data = await api.recordApi.getList({ + page: this.page, + size: this.size, + keyword: this.keyword || undefined + }) + console.log('加载记录结果:', data) if (data && data.success) { - this.records = data.data || [] + const resultData = data.data || {} + this.records = resultData.records || [] + this.total = resultData.total || 0 + this.totalPages = resultData.totalPages || 0 } else { - this.records = data || [] + this.records = [] + this.total = 0 + this.totalPages = 0 } + this.selectedIds = [] console.log('记录列表:', this.records) } catch (e) { console.error('加载记录失败:', e) this.records = [] + this.total = 0 + this.totalPages = 0 + } finally { + this.loading = false } }, + + handleSearch() { + this.page = 1 + this.loadRecords() + }, + + changePage(newPage) { + if (newPage < 1 || newPage > this.totalPages) return + this.page = newPage + this.loadRecords() + }, + + toggleSelectAll() { + if (this.isAllSelected) { + this.selectedIds = [] + } else { + this.selectedIds = this.records.map(r => r.id) + } + }, + + toggleSelect(id) { + const index = this.selectedIds.indexOf(id) + if (index > -1) { + this.selectedIds.splice(index, 1) + } else { + this.selectedIds.push(id) + } + }, + + async deleteSelected() { + if (this.selectedIds.length === 0) return + + uni.showModal({ + title: '确认删除', + content: `确定要删除选中的 ${this.selectedIds.length} 条记录吗?`, + success: async (res) => { + if (res.confirm) { + try { + uni.showLoading({ title: '删除中...' }) + + const result = await api.recordApi.deleteBatch(this.selectedIds) + + uni.hideLoading() + + if (result.success) { + uni.showToast({ + title: '删除成功', + icon: 'success' + }) + this.selectedIds = [] + this.loadRecords() + } else { + uni.showToast({ + title: result.error || '删除失败', + icon: 'none' + }) + } + } catch (e) { + uni.hideLoading() + console.error('批量删除失败:', e) + uni.showToast({ + title: '删除失败:' + (e.message || '未知错误'), + icon: 'none' + }) + } + } + } + }) + }, + + async clearAll() { + uni.showModal({ + title: '确认清空', + content: '确定要清空所有测算记录吗?此操作不可恢复!', + success: async (res) => { + if (res.confirm) { + try { + uni.showLoading({ title: '清空中...' }) + + const result = await api.recordApi.clear() + + uni.hideLoading() + + if (result.success) { + uni.showToast({ + title: '清空成功', + icon: 'success' + }) + this.records = [] + this.total = 0 + this.totalPages = 0 + this.selectedIds = [] + } else { + uni.showToast({ + title: result.error || '清空失败', + icon: 'none' + }) + } + } catch (e) { + uni.hideLoading() + console.error('清空记录失败:', e) + uni.showToast({ + title: '清空失败:' + (e.message || '未知错误'), + icon: 'none' + }) + } + } + } + }) + }, + formatTime(time) { if (!time) return '' if (Array.isArray(time)) { @@ -87,6 +269,7 @@ export default { const minute = String(date.getMinutes()).padStart(2, '0') return `${year}-${month}-${day} ${hour}:${minute}` }, + getTypeIcon(type) { const icons = { bazi: '命', @@ -100,6 +283,7 @@ export default { } return icons[type] || '算' }, + getTypeColor(type) { const colors = { bazi: '#3B82F6', @@ -113,6 +297,7 @@ export default { } return colors[type] || '#3B82F6' }, + getTypeName(type) { const names = { bazi: '八字命理', @@ -126,6 +311,7 @@ export default { } return names[type] || '命理测算' }, + viewDetail(item) { const app = getApp() if (!app.globalData) { @@ -169,6 +355,7 @@ export default { uni.navigateTo({ url: '/pages/daliuren/result' }) } }, + async deleteRecord(id, index) { uni.showModal({ title: '确认删除', @@ -186,6 +373,11 @@ export default { title: '删除成功', icon: 'success' }) + + if (this.records.length === 0 && this.page > 1) { + this.page-- + this.loadRecords() + } } else { uni.showToast({ title: result.error || '删除失败', @@ -256,6 +448,97 @@ page { color: rgba(255, 255, 255, 0.8); } +.search-bar { + display: flex; + gap: 16rpx; + margin-bottom: 24rpx; +} + +.search-input { + flex: 1; + background: rgba(255, 255, 255, 0.95); + border-radius: 24rpx; + padding: 20rpx 32rpx; + font-size: 28rpx; + border: 2rpx solid rgba(59, 130, 246, 0.2); +} + +.search-btn { + background: linear-gradient(135deg, #3B82F6 0%, #1D4ED8 100%); + color: #fff; + border: none; + padding: 0 32rpx; + border-radius: 24rpx; + font-size: 28rpx; + font-weight: 600; +} + +.action-bar { + display: flex; + justify-content: space-between; + align-items: center; + background: rgba(255, 255, 255, 0.95); + border-radius: 24rpx; + padding: 20rpx 32rpx; + margin-bottom: 24rpx; + box-shadow: 0 8rpx 32rpx rgba(59, 130, 246, 0.1); +} + +.select-all { + display: flex; + align-items: center; + gap: 12rpx; +} + +.checkbox { + width: 40rpx; + height: 40rpx; + border: 3rpx solid #3B82F6; + border-radius: 8rpx; + display: flex; + align-items: center; + justify-content: center; + background: #fff; +} + +.checkbox.checked { + background: #3B82F6; +} + +.check-icon { + color: #fff; + font-size: 24rpx; + font-weight: bold; +} + +.select-text { + font-size: 28rpx; + color: #1E40AF; + font-weight: 600; +} + +.action-buttons { + display: flex; + gap: 16rpx; +} + +.action-btn { + padding: 12rpx 24rpx; + border-radius: 16rpx; + font-size: 24rpx; + border: none; +} + +.delete-btn { + background: #FEE2E2; + color: #DC2626; +} + +.clear-btn { + background: #FEF3C7; + color: #D97706; +} + .empty-state { text-align: center; padding: 120rpx 0; @@ -295,6 +578,16 @@ page { border: 2rpx solid rgba(59, 130, 246, 0.1); } +.checkbox-wrapper { + margin-right: 20rpx; +} + +.record-content { + flex: 1; + display: flex; + align-items: center; +} + .record-icon { width: 80rpx; height: 80rpx; @@ -311,7 +604,7 @@ page { font-weight: bold; } -.record-content { +.record-info { flex: 1; } @@ -356,4 +649,32 @@ page { color: #93C5FD; font-weight: 300; } + +.pagination { + display: flex; + justify-content: center; + align-items: center; + gap: 24rpx; + margin-top: 32rpx; + padding: 20rpx; +} + +.page-btn { + background: rgba(255, 255, 255, 0.95); + color: #3B82F6; + border: 2rpx solid #3B82F6; + padding: 12rpx 32rpx; + border-radius: 16rpx; + font-size: 26rpx; +} + +.page-btn[disabled] { + opacity: 0.5; +} + +.page-info { + font-size: 28rpx; + color: #1E40AF; + font-weight: 600; +} diff --git a/xuan-deduct-uniapp/src/pages/zhanxing/result.vue b/xuan-deduct-uniapp/src/pages/zhanxing/result.vue index a6b7d8d..0ac285c 100644 --- a/xuan-deduct-uniapp/src/pages/zhanxing/result.vue +++ b/xuan-deduct-uniapp/src/pages/zhanxing/result.vue @@ -213,7 +213,6 @@