ok1
This commit is contained in:
+3
-14
@@ -15,19 +15,7 @@
|
||||
票务API入口
|
||||
</description>
|
||||
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.ai</groupId>
|
||||
<artifactId>spring-ai-bom</artifactId>
|
||||
<version>2.0.0-RC1</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
|
||||
<dependencies>
|
||||
<dependencies>
|
||||
|
||||
<!-- spring-boot-devtools -->
|
||||
<dependency>
|
||||
@@ -205,6 +193,7 @@
|
||||
<dependency>
|
||||
<groupId>org.springframework.ai</groupId>
|
||||
<artifactId>spring-ai-starter-model-openai</artifactId>
|
||||
<version>2.0.0-RC1</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
@@ -234,7 +223,7 @@
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
<finalName>${project.artifactId}</finalName>
|
||||
<finalName>piaowu</finalName>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
+2
-2
@@ -9,12 +9,12 @@ import org.springframework.scheduling.annotation.EnableAsync;
|
||||
*/
|
||||
@SpringBootApplication
|
||||
@EnableAsync
|
||||
public class RuoPiaoApplication
|
||||
public class PiaoWuApplication
|
||||
{
|
||||
public static void main(String[] args)
|
||||
{
|
||||
// System.setProperty("spring.devtools.restart.enabled", "false");
|
||||
SpringApplication.run(RuoPiaoApplication.class, args);
|
||||
SpringApplication.run(PiaoWuApplication.class, args);
|
||||
System.out.println(">>若票系统启动成功 :-) \n" +
|
||||
" ____ ____ _\n" +
|
||||
" | _ \\ _ _ ___ | _ \\ (_) __ _ ___\n" +
|
||||
+2
-2
@@ -8,11 +8,11 @@ import org.springframework.boot.web.servlet.support.SpringBootServletInitializer
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
public class RuoPiaoServletInitializer extends SpringBootServletInitializer
|
||||
public class PiaoWuServletInitializer extends SpringBootServletInitializer
|
||||
{
|
||||
@Override
|
||||
protected SpringApplicationBuilder configure(SpringApplicationBuilder application)
|
||||
{
|
||||
return application.sources(RuoPiaoApplication.class);
|
||||
return application.sources(PiaoWuApplication.class);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,204 @@
|
||||
package com.ruoyi.piao.controller;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.ruoyi.common.annotation.Anonymous;
|
||||
import com.ruoyi.piao.domain.ResponseDTO;
|
||||
import com.ruoyi.piao.domain.entity.Change;
|
||||
import com.ruoyi.piao.domain.entity.Order;
|
||||
import com.ruoyi.piao.domain.entity.Refund;
|
||||
import com.ruoyi.piao.service.ChangeService;
|
||||
import com.ruoyi.piao.service.OrderService;
|
||||
import com.ruoyi.piao.service.RefundService;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.springframework.ai.chat.client.ChatClient;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.*;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/ai")
|
||||
public class AIController {
|
||||
|
||||
private static List<Map<String, Object>> hotlineCache = null;
|
||||
private static LocalDateTime hotlineCacheTime = null;
|
||||
private static final long CACHE_DURATION_HOURS = 1;
|
||||
|
||||
@Resource
|
||||
private OrderService orderService;
|
||||
|
||||
@Autowired
|
||||
private RefundService refundService;
|
||||
|
||||
@Autowired
|
||||
private ChangeService changeService;
|
||||
|
||||
@Resource
|
||||
private ChatClient chatClient;
|
||||
|
||||
@Anonymous
|
||||
@GetMapping("/hotline")
|
||||
public ResponseDTO<List<Map<String, Object>>> hotline() {
|
||||
if (hotlineCache != null && hotlineCacheTime != null
|
||||
&& LocalDateTime.now().minusHours(CACHE_DURATION_HOURS).isBefore(hotlineCacheTime)) {
|
||||
return ResponseDTO.ok(hotlineCache);
|
||||
}
|
||||
|
||||
String prompt = "你是一个机票票务AI助手。请根据当前中国民航市场情况,生成当前最热门的国内航线列表。" +
|
||||
"请直接返回JSON数组,不要加markdown代码块标记,格式为:[{\"route\": \"北京-上海\", \"reason\": \"商务航线,客流量大\", \"rank\": 1}]。" +
|
||||
"返回8条,按热门程度排序,每条reason用中文简要描述热门原因。";
|
||||
|
||||
String aiResponse = chatClient.prompt()
|
||||
.system("你是一个民航数据分析助手,只返回纯JSON数组,不要任何其他文字和markdown标记。")
|
||||
.user(prompt)
|
||||
.call()
|
||||
.content();
|
||||
|
||||
if (aiResponse != null) {
|
||||
aiResponse = aiResponse.trim();
|
||||
if (aiResponse.startsWith("```")) {
|
||||
aiResponse = aiResponse.replaceAll("```json\\s*", "").replaceAll("```\\s*", "").trim();
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
if (aiResponse != null) {
|
||||
List<Map<String, Object>> list = JSON.parseObject(aiResponse, new com.alibaba.fastjson.TypeReference<List<Map<String, Object>>>(){});
|
||||
if (list != null && !list.isEmpty()) {
|
||||
hotlineCache = list;
|
||||
hotlineCacheTime = LocalDateTime.now();
|
||||
return ResponseDTO.ok(list);
|
||||
}
|
||||
}
|
||||
} catch (Exception ignored) {}
|
||||
|
||||
if (hotlineCache != null) {
|
||||
return ResponseDTO.ok(hotlineCache);
|
||||
}
|
||||
|
||||
List<Map<String, Object>> fallback = new ArrayList<>();
|
||||
Map<String, Object> item = new HashMap<>();
|
||||
item.put("route", "北京-上海");
|
||||
item.put("reason", "热门商务航线");
|
||||
item.put("rank", 1);
|
||||
fallback.add(item);
|
||||
item = new HashMap<>();
|
||||
item.put("route", "广州-深圳");
|
||||
item.put("reason", "热门商务航线");
|
||||
item.put("rank", 2);
|
||||
fallback.add(item);
|
||||
return ResponseDTO.ok(fallback);
|
||||
}
|
||||
|
||||
@Anonymous
|
||||
@GetMapping("/chat")
|
||||
public String chat(@RequestParam String message,
|
||||
@RequestParam(required = false) String context) {
|
||||
StringBuilder systemPrompt = new StringBuilder("你是一个机票票务系统的AI助手,帮助用户解答关于票务系统的问题。" +
|
||||
"用中文回答,简洁清晰,必要时可以给出数据统计。");
|
||||
|
||||
if (StrUtil.isNotBlank(context)) {
|
||||
JSONObject ctx = JSON.parseObject(context);
|
||||
String route = ctx.getString("route");
|
||||
String pageTitle = ctx.getString("title");
|
||||
|
||||
if (StrUtil.isNotBlank(pageTitle)) {
|
||||
systemPrompt.append("\n用户当前在「").append(pageTitle).append("」页面。");
|
||||
}
|
||||
|
||||
if (route != null) {
|
||||
systemPrompt.append(injectPageData(route));
|
||||
}
|
||||
|
||||
Object pageDataObj = ctx.get("data");
|
||||
if (pageDataObj != null) {
|
||||
String pageData = pageDataObj instanceof String ? (String) pageDataObj : pageDataObj.toString();
|
||||
systemPrompt.append("\n页面自定义数据:").append(pageData);
|
||||
}
|
||||
}
|
||||
|
||||
return chatClient.prompt()
|
||||
.system(systemPrompt.toString())
|
||||
.user(message)
|
||||
.call()
|
||||
.content();
|
||||
}
|
||||
|
||||
private String injectPageData(String route) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
try {
|
||||
if (route.contains("/order") || route.contains("/sale")) {
|
||||
long pendingCount = orderService.lambdaQuery().eq(Order::getStatus, "1").count();
|
||||
long issuingCount = orderService.lambdaQuery().eq(Order::getStatus, "21").count();
|
||||
if (pendingCount > 0) {
|
||||
List<Order> urgent = orderService.lambdaQuery()
|
||||
.eq(Order::getStatus, "1")
|
||||
.orderByAsc(Order::getLimittime)
|
||||
.last("LIMIT 5")
|
||||
.list();
|
||||
sb.append("\n待处理订单共").append(pendingCount).append("单,出票中").append(issuingCount).append("单。");
|
||||
if (!urgent.isEmpty()) {
|
||||
sb.append("\n最紧急的5单:");
|
||||
for (Order o : urgent) {
|
||||
sb.append("\n- 订单").append(o.getId())
|
||||
.append(" 航班").append(o.getFlightNumber())
|
||||
.append(" ").append(o.getDepCode()).append("→").append(o.getArrCode())
|
||||
.append(" 金额").append(o.getAmount())
|
||||
.append(" 最晚出票").append(o.getLimittime());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (route.contains("/order/list")) {
|
||||
sb.append("\n可查询订单列表、状态、金额等信息。");
|
||||
}
|
||||
if (route.contains("/order/waiting") || route.contains("/order/urgent")) {
|
||||
long waiting = orderService.lambdaQuery().eq(Order::getStatus, "1").count();
|
||||
sb.append("\n待处理订单").append(waiting).append("单。");
|
||||
}
|
||||
if (route.contains("/order/mine")) {
|
||||
sb.append("\n这是我的订单页面,可查询当前用户认领的订单。");
|
||||
}
|
||||
if (route.contains("/refund")) {
|
||||
long refundPending = refundService.lambdaQuery().eq(Refund::getRefundStatus, 0).count();
|
||||
sb.append("\n待退票共").append(refundPending).append("单。");
|
||||
}
|
||||
if (route.contains("/change")) {
|
||||
long changePending = changeService.lambdaQuery().eq(Change::getChangeStatus, 0).count();
|
||||
sb.append("\n待改签共").append(changePending).append("单。");
|
||||
}
|
||||
if (route.contains("/report")) {
|
||||
sb.append("\n这是报表页面,可查询业绩、利润、结算等统计数据。");
|
||||
}
|
||||
if (route.contains("/config")) {
|
||||
sb.append("\n这是配置管理页面,包含航空公司、舱位、渠道、客户、航线、规则等配置。");
|
||||
}
|
||||
if (route.contains("/sale/list")) {
|
||||
sb.append("\n这是销售列表页面,可查询待处理销售订单。");
|
||||
}
|
||||
if (route.contains("/policy")) {
|
||||
sb.append("\n这是政策管理页面,包含价格政策、OTA政策等。");
|
||||
}
|
||||
if (route.contains("/data/crawl")) {
|
||||
sb.append("\n这是数据抓取页面,用于管理爬虫任务。");
|
||||
}
|
||||
if (route.equals("/home/index") || route.equals("/home") || route.equals("/index")) {
|
||||
long orderCount = orderService.lambdaQuery().eq(Order::getStatus, "1").count();
|
||||
long refundCount = refundService.lambdaQuery().eq(Refund::getRefundStatus, 0).count();
|
||||
long changeCount = changeService.lambdaQuery().eq(Change::getChangeStatus, 0).count();
|
||||
sb.append("\n首页概览:待处理订单").append(orderCount)
|
||||
.append("单,待退票").append(refundCount)
|
||||
.append("单,待改签").append(changeCount).append("单。");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
sb.append("\n(查询业务数据时出现异常)");
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
@@ -2,8 +2,11 @@ package com.ruoyi.piao.controller;
|
||||
|
||||
import com.ruoyi.common.annotation.Anonymous;
|
||||
import com.ruoyi.piao.domain.ResponseDTO;
|
||||
import com.ruoyi.piao.service.MailService;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
@RestController
|
||||
@@ -14,4 +17,17 @@ public class HomeController {
|
||||
return ResponseDTO.ok();
|
||||
}
|
||||
|
||||
|
||||
@Resource
|
||||
MailService mailService;
|
||||
|
||||
@GetMapping("/mail")
|
||||
@Anonymous
|
||||
public ResponseDTO<String> sendMail(@RequestParam(defaultValue = "344656718@qq.com") String to,
|
||||
@RequestParam(defaultValue = "测试邮件") String subject,
|
||||
@RequestParam(defaultValue = "这是一封测试邮件") String content) {
|
||||
mailService.sendEmail(to, subject, content);
|
||||
return ResponseDTO.ok("邮件发送成功");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package com.ruoyi.piao.controller;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.ruoyi.common.annotation.Anonymous;
|
||||
@@ -17,7 +16,6 @@ import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import jakarta.annotation.Resource;
|
||||
import org.springframework.ai.chat.client.ChatClient;
|
||||
import java.util.HashMap;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
@@ -191,112 +189,4 @@ public class MessageController {
|
||||
}
|
||||
}
|
||||
|
||||
@Resource
|
||||
private ChatClient chatClient;
|
||||
|
||||
@GetMapping("/chat")
|
||||
public String chat(@RequestParam String message,
|
||||
@RequestParam(required = false) String context) {
|
||||
StringBuilder systemPrompt = new StringBuilder("你是一个机票票务系统的AI助手,帮助用户解答关于票务系统的问题。" +
|
||||
"用中文回答,简洁清晰,必要时可以给出数据统计。");
|
||||
|
||||
if (StrUtil.isNotBlank(context)) {
|
||||
JSONObject ctx = JSON.parseObject(context);
|
||||
String route = ctx.getString("route");
|
||||
String pageTitle = ctx.getString("title");
|
||||
|
||||
if (StrUtil.isNotBlank(pageTitle)) {
|
||||
systemPrompt.append("\n用户当前在「").append(pageTitle).append("」页面。");
|
||||
}
|
||||
|
||||
if (route != null) {
|
||||
systemPrompt.append(injectPageData(route));
|
||||
}
|
||||
|
||||
Object pageDataObj = ctx.get("data");
|
||||
if (pageDataObj != null) {
|
||||
String pageData = pageDataObj instanceof String ? (String) pageDataObj : pageDataObj.toString();
|
||||
systemPrompt.append("\n页面自定义数据:").append(pageData);
|
||||
}
|
||||
}
|
||||
|
||||
return chatClient.prompt()
|
||||
.system(systemPrompt.toString())
|
||||
.user(message)
|
||||
.call()
|
||||
.content();
|
||||
}
|
||||
|
||||
private String injectPageData(String route) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
try {
|
||||
if (route.contains("/order") || route.contains("/sale")) {
|
||||
long pendingCount = orderService.lambdaQuery().eq(Order::getStatus, "1").count();
|
||||
long issuingCount = orderService.lambdaQuery().eq(Order::getStatus, "21").count();
|
||||
if (pendingCount > 0) {
|
||||
List<Order> urgent = orderService.lambdaQuery()
|
||||
.eq(Order::getStatus, "1")
|
||||
.orderByAsc(Order::getLimittime)
|
||||
.last("LIMIT 5")
|
||||
.list();
|
||||
sb.append("\n待处理订单共").append(pendingCount).append("单,出票中").append(issuingCount).append("单。");
|
||||
if (!urgent.isEmpty()) {
|
||||
sb.append("\n最紧急的5单:");
|
||||
for (Order o : urgent) {
|
||||
sb.append("\n- 订单").append(o.getId())
|
||||
.append(" 航班").append(o.getFlightNumber())
|
||||
.append(" ").append(o.getDepCode()).append("→").append(o.getArrCode())
|
||||
.append(" 金额").append(o.getAmount())
|
||||
.append(" 最晚出票").append(o.getLimittime());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (route.contains("/order/list")) {
|
||||
sb.append("\n可查询订单列表、状态、金额等信息。");
|
||||
}
|
||||
if (route.contains("/order/waiting") || route.contains("/order/urgent")) {
|
||||
long waiting = orderService.lambdaQuery().eq(Order::getStatus, "1").count();
|
||||
sb.append("\n待处理订单").append(waiting).append("单。");
|
||||
}
|
||||
if (route.contains("/order/mine")) {
|
||||
sb.append("\n这是我的订单页面,可查询当前用户认领的订单。");
|
||||
}
|
||||
if (route.contains("/refund")) {
|
||||
long refundPending = refundService.lambdaQuery().eq(Refund::getRefundStatus, 0).count();
|
||||
sb.append("\n待退票共").append(refundPending).append("单。");
|
||||
}
|
||||
if (route.contains("/change")) {
|
||||
long changePending = changeService.lambdaQuery().eq(Change::getChangeStatus, 0).count();
|
||||
sb.append("\n待改签共").append(changePending).append("单。");
|
||||
}
|
||||
if (route.contains("/report")) {
|
||||
sb.append("\n这是报表页面,可查询业绩、利润、结算等统计数据。");
|
||||
}
|
||||
if (route.contains("/config")) {
|
||||
sb.append("\n这是配置管理页面,包含航空公司、舱位、渠道、客户、航线、规则等配置。");
|
||||
}
|
||||
if (route.contains("/sale/list")) {
|
||||
sb.append("\n这是销售列表页面,可查询待处理销售订单。");
|
||||
}
|
||||
if (route.contains("/policy")) {
|
||||
sb.append("\n这是政策管理页面,包含价格政策、OTA政策等。");
|
||||
}
|
||||
if (route.contains("/data/crawl")) {
|
||||
sb.append("\n这是数据抓取页面,用于管理爬虫任务。");
|
||||
}
|
||||
if (route.equals("/home/index") || route.equals("/home") || route.equals("/index")) {
|
||||
long orderCount = orderService.lambdaQuery().eq(Order::getStatus, "1").count();
|
||||
long refundCount = refundService.lambdaQuery().eq(Refund::getRefundStatus, 0).count();
|
||||
long changeCount = changeService.lambdaQuery().eq(Change::getChangeStatus, 0).count();
|
||||
sb.append("\n首页概览:待处理订单").append(orderCount)
|
||||
.append("单,待退票").append(refundCount)
|
||||
.append("单,待改签").append(changeCount).append("单。");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
sb.append("\n(查询业务数据时出现异常)");
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -114,7 +114,7 @@ public class SysConfigController extends BaseController
|
||||
public AjaxResult editByKeyValue(@PathVariable String key,
|
||||
@PathVariable String value)
|
||||
{
|
||||
SysConfig cfg = configService.selectConfigById(Long.parseLong(key));
|
||||
SysConfig cfg = configService.selectConfigByConfigKey(key);
|
||||
if (cfg == null) return error("参数未找到");
|
||||
cfg.setUpdateBy(getUsername());
|
||||
cfg.setConfigValue(value);
|
||||
|
||||
@@ -7,7 +7,7 @@ ruoyi:
|
||||
# 版权年份
|
||||
copyrightYear: 2025
|
||||
# 上传文件路径 示例( Windows配置D:/ruoyi/uploadPath,Linux配置 /home/ruoyi/uploadPath 或 docker 中的挂载目录)
|
||||
profile: /app/upload/piao/
|
||||
profile: /app/upload/piaowu/
|
||||
# 获取ip地址开关
|
||||
addressEnabled: false
|
||||
# 验证码类型 math 数字计算 char 字符验证
|
||||
@@ -16,7 +16,7 @@ ruoyi:
|
||||
# 开发环境配置
|
||||
server:
|
||||
# 服务器的HTTP端口,默认为8080
|
||||
port: 8080
|
||||
port: 1024
|
||||
servlet:
|
||||
# 应用的访问路径
|
||||
context-path: /
|
||||
@@ -63,9 +63,9 @@ spring:
|
||||
druid:
|
||||
# 主库数据源 Abcd1234 P0i8a1o9@#()
|
||||
master:
|
||||
url: jdbc:mysql://mysql/piaoruo?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
|
||||
url: jdbc:mysql://piaowumysql:3306/piaowu?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
|
||||
username: root
|
||||
password: P0i8a1o9@#()
|
||||
password: P2i0a2o6w@u
|
||||
# 从库数据源
|
||||
slave:
|
||||
# 从数据源开关/默认关闭
|
||||
@@ -136,10 +136,10 @@ spring:
|
||||
enabled: true
|
||||
data:
|
||||
redis:
|
||||
host: redis
|
||||
host: piaowuredis
|
||||
port: 6379
|
||||
database: 0
|
||||
password: Rds123!@$
|
||||
password: Rds234!@$
|
||||
|
||||
ai:
|
||||
openai:
|
||||
|
||||
@@ -5,7 +5,7 @@ ruoyi:
|
||||
# 版本
|
||||
version: 3.9.2
|
||||
# 版权年份
|
||||
copyrightYear: 2025
|
||||
copyrightYear: 2026
|
||||
# 文件路径 示例( Windows配置D:/ruoyi/uploadPath,Linux配置 /home/ruoyi/uploadPath)
|
||||
profile: D:/ruoyi/uploadPath
|
||||
# 获取ip地址开关
|
||||
|
||||
Reference in New Issue
Block a user