补齐超时响应唯一 timeoutCode 与智能体按码查询,并增强报价模板预览与解析落库。
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+33
@@ -38,6 +38,9 @@ public class InquiryAgentController {
|
||||
@Autowired
|
||||
private org.jeecg.modules.inquiry.service.IInquiryStaffRoleService staffRoleService;
|
||||
|
||||
@Autowired
|
||||
private org.jeecg.modules.inquiry.service.IInquiryResponseTimeoutService responseTimeoutService;
|
||||
|
||||
@Autowired
|
||||
private org.jeecg.modules.inquiry.tms.InquiryTmsCabinService cabinService;
|
||||
|
||||
@@ -234,6 +237,36 @@ public class InquiryAgentController {
|
||||
return Result.OK(staffRoleService.findDisplayByName(firstStr(body, "name")));
|
||||
}
|
||||
|
||||
@ApiOperation("按唯一超时编码取小时数(timeoutCode,如 sea_sales)")
|
||||
@PostMapping("/responseTimeout/byCode")
|
||||
public Result<Map<String, Object>> responseTimeoutByCode(@RequestBody Map<String, Object> body,
|
||||
HttpServletRequest http) {
|
||||
if (!authed(http)) {
|
||||
return Result.error(401, "unauthorized");
|
||||
}
|
||||
String code = firstStr(body, "timeoutCode", "timeout_code", "code");
|
||||
if (StringUtils.isBlank(code)) {
|
||||
return Result.error("timeoutCode 必填");
|
||||
}
|
||||
responseTimeoutService.ensureTimeoutCodes();
|
||||
org.jeecg.modules.inquiry.entity.InquiryResponseTimeout row =
|
||||
responseTimeoutService.getByTimeoutCode(code);
|
||||
Map<String, Object> out = new java.util.LinkedHashMap<>();
|
||||
if (row == null) {
|
||||
out.put("found", false);
|
||||
out.put("timeoutCode", code);
|
||||
return Result.OK(out);
|
||||
}
|
||||
out.put("found", true);
|
||||
out.put("timeoutCode", row.getTimeoutCode());
|
||||
out.put("timeoutHours", row.getTimeoutHours());
|
||||
out.put("transportMode", row.getTransportMode());
|
||||
out.put("roleCode", row.getRoleCode());
|
||||
out.put("routeCategory", row.getRouteCategory());
|
||||
out.put("transportType", row.getTransportType());
|
||||
return Result.OK(out);
|
||||
}
|
||||
|
||||
@ApiOperation("记下海运协同群(不改六态)")
|
||||
@PostMapping("/ticket/bindCollabGroup")
|
||||
public Result<Map<String, Object>> bindCollabGroup(@RequestBody Map<String, Object> body,
|
||||
|
||||
+4
-2
@@ -20,9 +20,10 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 响应超时配置 API(系统设置页)。
|
||||
* 响应超时配置 API(后台「超时响应」页)。
|
||||
* <p>
|
||||
* 只提供全量列表与改小时数;无 add/delete,维度由 SQL 种子固定。
|
||||
* 每条有唯一 timeoutCode(如 sea_sales),智能体单码调用;roleCode 仅为角色维度可重复。
|
||||
* 本接口供管理后台使用;工单超时判定尚未接入。
|
||||
* </p>
|
||||
*/
|
||||
@@ -59,8 +60,9 @@ public class InquiryResponseTimeoutController
|
||||
@ApiOperation("全量列表(按 sort_no,供系统设置页一次加载)")
|
||||
@GetMapping("/listAll")
|
||||
public Result<List<InquiryResponseTimeout>> listAll() {
|
||||
// 打开系统设置时顺带纠偏旧运输类型文案(幂等,已是新文案则无写)
|
||||
// 打开超时响应页时顺带纠偏旧运输类型文案、回填唯一编码(幂等)
|
||||
responseTimeoutService.ensureCanonicalTransportTypeLabels();
|
||||
responseTimeoutService.ensureTimeoutCodes();
|
||||
QueryWrapper<InquiryResponseTimeout> qw = new QueryWrapper<>();
|
||||
qw.orderByAsc("sort_no");
|
||||
return Result.OK(responseTimeoutService.list(qw));
|
||||
|
||||
+5
-1
@@ -18,6 +18,7 @@ import java.util.Date;
|
||||
* 响应超时配置,对应 inq_response_timeout。
|
||||
* <p>
|
||||
* 维度行由种子固定(运输方式/角色/线路类别/运输类型),后台只允许改 timeoutHours。
|
||||
* timeoutCode 每条唯一,供智能体单码查询超时小时;roleCode 仅为角色维度,可重复。
|
||||
* 本表是配置真相;工单超时判定后续再读本表,本期仅 CRUD 小时数。
|
||||
* </p>
|
||||
*/
|
||||
@@ -33,10 +34,13 @@ public class InquiryResponseTimeout implements Serializable {
|
||||
@TableId(type = IdType.ASSIGN_ID)
|
||||
private String id;
|
||||
|
||||
@ApiModelProperty("唯一业务编码(智能体调用),如 sea_sales / land_hk_express")
|
||||
private String timeoutCode;
|
||||
|
||||
@ApiModelProperty("运输方式 AIR/SEA/LAND")
|
||||
private String transportMode;
|
||||
|
||||
@ApiModelProperty("角色 sales/product/air_route")
|
||||
@ApiModelProperty("角色维度 sales/product/air_route(可重复,非调用主键)")
|
||||
private String roleCode;
|
||||
|
||||
@ApiModelProperty("线路类别(仅陆运产品有值,其余空串)")
|
||||
|
||||
+11
@@ -28,4 +28,15 @@ public interface IInquiryResponseTimeoutService extends IService<InquiryResponse
|
||||
* </p>
|
||||
*/
|
||||
void ensureCanonicalTransportTypeLabels();
|
||||
|
||||
/**
|
||||
* 回填每条规则的唯一 timeoutCode(已有则跳过)。
|
||||
* 智能体只凭 timeoutCode 取超时小时。
|
||||
*/
|
||||
void ensureTimeoutCodes();
|
||||
|
||||
/**
|
||||
* 按唯一业务编码查超时配置;无则 null。
|
||||
*/
|
||||
InquiryResponseTimeout getByTimeoutCode(String timeoutCode);
|
||||
}
|
||||
|
||||
+58
-3
@@ -132,7 +132,10 @@ public class InquiryQuoteTemplateServiceImpl extends ServiceImpl<InquiryQuoteTem
|
||||
m.put("objectKey", ver.getObjectKey());
|
||||
m.put("parseMessage", ver.getParseMessage());
|
||||
m.put("unmappedRequired", extractUnmapped(ver.getMappingJson()));
|
||||
m.put("unmappedLabels", extractUnmappedLabels(ver.getMappingJson()));
|
||||
m.put("mappingGaps", extractMappingGaps(ver.getMappingJson()));
|
||||
// 列表提示用:合并后的未解析条数(有则运营要点开看)
|
||||
m.put("unparsedHintCount", countUnparsedHints(ver.getMappingJson()));
|
||||
} else {
|
||||
m.put("versionId", null);
|
||||
m.put("fileName", "");
|
||||
@@ -1355,14 +1358,14 @@ public class InquiryQuoteTemplateServiceImpl extends ServiceImpl<InquiryQuoteTem
|
||||
}
|
||||
|
||||
/**
|
||||
* 从 mappingJson 取出智能体对照清单算出的未映射说明(中文)。
|
||||
* AI 自报「表上标签未落点」原文。
|
||||
*/
|
||||
private static List<String> extractMappingGaps(String mappingJson) {
|
||||
private static List<String> extractUnmappedLabels(String mappingJson) {
|
||||
if (StringUtils.isBlank(mappingJson)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
try {
|
||||
JSONArray arr = JSON.parseObject(mappingJson).getJSONArray("mappingGaps");
|
||||
JSONArray arr = JSON.parseObject(mappingJson).getJSONArray("unmappedLabels");
|
||||
if (arr == null || arr.isEmpty()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
@@ -1379,6 +1382,58 @@ public class InquiryQuoteTemplateServiceImpl extends ServiceImpl<InquiryQuoteTem
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 从 mappingJson 取出智能体对照清单算出的未映射说明(中文)。
|
||||
* 若 mappingGaps 为空,用 unmappedLabels / unmappedRequired 兜底,保证列表能提示。
|
||||
*/
|
||||
private static List<String> extractMappingGaps(String mappingJson) {
|
||||
if (StringUtils.isBlank(mappingJson)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
try {
|
||||
JSONObject obj = JSON.parseObject(mappingJson);
|
||||
JSONArray arr = obj.getJSONArray("mappingGaps");
|
||||
List<String> out = new ArrayList<>();
|
||||
if (arr != null) {
|
||||
for (int i = 0; i < arr.size(); i++) {
|
||||
String s = arr.getString(i);
|
||||
if (StringUtils.isNotBlank(s)) {
|
||||
out.add(s);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!out.isEmpty()) {
|
||||
return out;
|
||||
}
|
||||
JSONArray labels = obj.getJSONArray("unmappedLabels");
|
||||
if (labels != null) {
|
||||
for (int i = 0; i < labels.size(); i++) {
|
||||
String s = labels.getString(i);
|
||||
if (StringUtils.isNotBlank(s)) {
|
||||
out.add("表标签未落点:" + s);
|
||||
}
|
||||
}
|
||||
}
|
||||
JSONArray required = obj.getJSONArray("unmappedRequired");
|
||||
if (required != null) {
|
||||
for (int i = 0; i < required.size(); i++) {
|
||||
String s = required.getString(i);
|
||||
if (StringUtils.isNotBlank(s)) {
|
||||
out.add("字段未映射:" + s);
|
||||
}
|
||||
}
|
||||
}
|
||||
return out;
|
||||
} catch (Exception e) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
|
||||
/** 列表角标用的未解析条数(与 extractMappingGaps 口径一致)。 */
|
||||
private static int countUnparsedHints(String mappingJson) {
|
||||
return extractMappingGaps(mappingJson).size();
|
||||
}
|
||||
|
||||
private static String mapParseStatusLabel(String status) {
|
||||
if ("parsed".equals(status)) {
|
||||
return "已解析";
|
||||
|
||||
+80
-1
@@ -1,5 +1,6 @@
|
||||
package org.jeecg.modules.inquiry.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import org.jeecg.modules.inquiry.entity.InquiryResponseTimeout;
|
||||
import org.jeecg.modules.inquiry.mapper.InquiryResponseTimeoutMapper;
|
||||
@@ -9,13 +10,15 @@ import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 响应超时配置:只改 timeout_hours,不动维度列(文案纠偏除外)。
|
||||
* 响应超时配置:只改 timeout_hours,不动维度列(文案纠偏与编码回填除外)。
|
||||
* <p>
|
||||
* 为何单独方法而不用 updateById 整行:避免前端误传改写运输方式/角色等固定种子。
|
||||
* 运输类型纠偏是产品确认的一次性旧→新映射,不开放通用改维度接口。
|
||||
* timeoutCode 每条唯一,供智能体单码调用。
|
||||
* </p>
|
||||
*/
|
||||
@Service
|
||||
@@ -23,12 +26,20 @@ public class InquiryResponseTimeoutServiceImpl
|
||||
extends ServiceImpl<InquiryResponseTimeoutMapper, InquiryResponseTimeout>
|
||||
implements IInquiryResponseTimeoutService {
|
||||
|
||||
private static final org.slf4j.Logger log =
|
||||
org.slf4j.LoggerFactory.getLogger(InquiryResponseTimeoutServiceImpl.class);
|
||||
|
||||
/**
|
||||
* 陆运产品运输类型:旧逗号写法 → 产品确认的括号写法。
|
||||
* 急件文案不变,不在此映射。
|
||||
*/
|
||||
private static final Map<String, String> LAND_PRODUCT_TYPE_RENAMES = new LinkedHashMap<>();
|
||||
|
||||
/**
|
||||
* 种子 id → 唯一业务编码。智能体只认 timeoutCode,不认 roleCode。
|
||||
*/
|
||||
private static final Map<String, String> ID_TO_TIMEOUT_CODE = new LinkedHashMap<>();
|
||||
|
||||
static {
|
||||
LAND_PRODUCT_TYPE_RENAMES.put(
|
||||
"普件,中港、东南亚常用线路/常规车型",
|
||||
@@ -45,10 +56,31 @@ public class InquiryResponseTimeoutServiceImpl
|
||||
LAND_PRODUCT_TYPE_RENAMES.put(
|
||||
"普件,非常用线路/临时线路/特种车型",
|
||||
"普件(非常用线路/临时线路/特种车型)");
|
||||
|
||||
ID_TO_TIMEOUT_CODE.put("rt100000000000000000000000000001", "air_sales");
|
||||
ID_TO_TIMEOUT_CODE.put("rt100000000000000000000000000002", "air_route");
|
||||
ID_TO_TIMEOUT_CODE.put("rt100000000000000000000000000003", "sea_sales");
|
||||
ID_TO_TIMEOUT_CODE.put("rt100000000000000000000000000004", "land_sales");
|
||||
ID_TO_TIMEOUT_CODE.put("rt100000000000000000000000000005", "sea_product_ptp");
|
||||
ID_TO_TIMEOUT_CODE.put("rt100000000000000000000000000006", "sea_product_other");
|
||||
ID_TO_TIMEOUT_CODE.put("rt100000000000000000000000000007", "land_hk_common");
|
||||
ID_TO_TIMEOUT_CODE.put("rt100000000000000000000000000008", "land_hk_cross");
|
||||
ID_TO_TIMEOUT_CODE.put("rt100000000000000000000000000009", "land_hk_special");
|
||||
ID_TO_TIMEOUT_CODE.put("rt100000000000000000000000000010", "land_hk_express");
|
||||
ID_TO_TIMEOUT_CODE.put("rt100000000000000000000000000011", "land_ltl_common");
|
||||
ID_TO_TIMEOUT_CODE.put("rt100000000000000000000000000012", "land_ltl_special");
|
||||
ID_TO_TIMEOUT_CODE.put("rt100000000000000000000000000013", "land_ltl_express");
|
||||
ID_TO_TIMEOUT_CODE.put("rt100000000000000000000000000014", "land_city_common");
|
||||
ID_TO_TIMEOUT_CODE.put("rt100000000000000000000000000015", "land_city_special");
|
||||
ID_TO_TIMEOUT_CODE.put("rt100000000000000000000000000016", "land_city_express");
|
||||
ID_TO_TIMEOUT_CODE.put("rt100000000000000000000000000017", "land_sea_common");
|
||||
ID_TO_TIMEOUT_CODE.put("rt100000000000000000000000000018", "land_sea_special");
|
||||
ID_TO_TIMEOUT_CODE.put("rt100000000000000000000000000019", "land_sea_express");
|
||||
}
|
||||
|
||||
/** 本进程内只纠偏一次,避免每次打开系统设置都打 UPDATE */
|
||||
private volatile boolean labelsEnsured;
|
||||
private volatile boolean codesEnsured;
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
@@ -94,4 +126,51 @@ public class InquiryResponseTimeoutServiceImpl
|
||||
labelsEnsured = true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void ensureTimeoutCodes() {
|
||||
if (codesEnsured) {
|
||||
return;
|
||||
}
|
||||
synchronized (this) {
|
||||
if (codesEnsured) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
Date now = new Date();
|
||||
List<InquiryResponseTimeout> all = list();
|
||||
for (InquiryResponseTimeout row : all) {
|
||||
if (row == null || StringUtils.isNotBlank(row.getTimeoutCode())) {
|
||||
continue;
|
||||
}
|
||||
String code = ID_TO_TIMEOUT_CODE.get(row.getId());
|
||||
if (StringUtils.isBlank(code)) {
|
||||
// 非种子行:用 id 兜底,保证唯一
|
||||
code = "rt_" + row.getId();
|
||||
}
|
||||
lambdaUpdate()
|
||||
.eq(InquiryResponseTimeout::getId, row.getId())
|
||||
.set(InquiryResponseTimeout::getTimeoutCode, code)
|
||||
.set(InquiryResponseTimeout::getUpdateBy, "system")
|
||||
.set(InquiryResponseTimeout::getUpdateTime, now)
|
||||
.update();
|
||||
}
|
||||
codesEnsured = true;
|
||||
} catch (Exception e) {
|
||||
// 列未迁移时不挡列表;运维执行 migration_inq_response_timeout_code_v1.sql 后即可
|
||||
log.warn("ensureTimeoutCodes 跳过(请确认已执行 timeout_code 迁移): {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public InquiryResponseTimeout getByTimeoutCode(String timeoutCode) {
|
||||
if (StringUtils.isBlank(timeoutCode)) {
|
||||
return null;
|
||||
}
|
||||
return lambdaQuery()
|
||||
.eq(InquiryResponseTimeout::getTimeoutCode, timeoutCode.trim())
|
||||
.one();
|
||||
}
|
||||
}
|
||||
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
-- 超时响应:每条规则增加唯一业务编码 timeout_code,供智能体单码调用。
|
||||
-- 仅改 inquiry_robot;幂等可重复执行。
|
||||
-- 库:inquiry_robot
|
||||
|
||||
SET NAMES utf8mb4;
|
||||
|
||||
-- 1) 加列(已存在则跳过:手工执行前请先 DESCRIBE 确认)
|
||||
ALTER TABLE `inq_response_timeout`
|
||||
ADD COLUMN `timeout_code` varchar(64) NULL COMMENT '唯一业务编码,智能体调用' AFTER `id`;
|
||||
|
||||
-- 2) 按主键回填(与种子 id 对齐)
|
||||
UPDATE `inq_response_timeout` SET `timeout_code`='air_sales' WHERE `id`='rt100000000000000000000000000001' AND (`timeout_code` IS NULL OR `timeout_code`='');
|
||||
UPDATE `inq_response_timeout` SET `timeout_code`='air_route' WHERE `id`='rt100000000000000000000000000002' AND (`timeout_code` IS NULL OR `timeout_code`='');
|
||||
UPDATE `inq_response_timeout` SET `timeout_code`='sea_sales' WHERE `id`='rt100000000000000000000000000003' AND (`timeout_code` IS NULL OR `timeout_code`='');
|
||||
UPDATE `inq_response_timeout` SET `timeout_code`='land_sales' WHERE `id`='rt100000000000000000000000000004' AND (`timeout_code` IS NULL OR `timeout_code`='');
|
||||
UPDATE `inq_response_timeout` SET `timeout_code`='sea_product_ptp' WHERE `id`='rt100000000000000000000000000005' AND (`timeout_code` IS NULL OR `timeout_code`='');
|
||||
UPDATE `inq_response_timeout` SET `timeout_code`='sea_product_other' WHERE `id`='rt100000000000000000000000000006' AND (`timeout_code` IS NULL OR `timeout_code`='');
|
||||
UPDATE `inq_response_timeout` SET `timeout_code`='land_hk_common' WHERE `id`='rt100000000000000000000000000007' AND (`timeout_code` IS NULL OR `timeout_code`='');
|
||||
UPDATE `inq_response_timeout` SET `timeout_code`='land_hk_cross' WHERE `id`='rt100000000000000000000000000008' AND (`timeout_code` IS NULL OR `timeout_code`='');
|
||||
UPDATE `inq_response_timeout` SET `timeout_code`='land_hk_special' WHERE `id`='rt100000000000000000000000000009' AND (`timeout_code` IS NULL OR `timeout_code`='');
|
||||
UPDATE `inq_response_timeout` SET `timeout_code`='land_hk_express' WHERE `id`='rt100000000000000000000000000010' AND (`timeout_code` IS NULL OR `timeout_code`='');
|
||||
UPDATE `inq_response_timeout` SET `timeout_code`='land_ltl_common' WHERE `id`='rt100000000000000000000000000011' AND (`timeout_code` IS NULL OR `timeout_code`='');
|
||||
UPDATE `inq_response_timeout` SET `timeout_code`='land_ltl_special' WHERE `id`='rt100000000000000000000000000012' AND (`timeout_code` IS NULL OR `timeout_code`='');
|
||||
UPDATE `inq_response_timeout` SET `timeout_code`='land_ltl_express' WHERE `id`='rt100000000000000000000000000013' AND (`timeout_code` IS NULL OR `timeout_code`='');
|
||||
UPDATE `inq_response_timeout` SET `timeout_code`='land_city_common' WHERE `id`='rt100000000000000000000000000014' AND (`timeout_code` IS NULL OR `timeout_code`='');
|
||||
UPDATE `inq_response_timeout` SET `timeout_code`='land_city_special' WHERE `id`='rt100000000000000000000000000015' AND (`timeout_code` IS NULL OR `timeout_code`='');
|
||||
UPDATE `inq_response_timeout` SET `timeout_code`='land_city_express' WHERE `id`='rt100000000000000000000000000016' AND (`timeout_code` IS NULL OR `timeout_code`='');
|
||||
UPDATE `inq_response_timeout` SET `timeout_code`='land_sea_common' WHERE `id`='rt100000000000000000000000000017' AND (`timeout_code` IS NULL OR `timeout_code`='');
|
||||
UPDATE `inq_response_timeout` SET `timeout_code`='land_sea_special' WHERE `id`='rt100000000000000000000000000018' AND (`timeout_code` IS NULL OR `timeout_code`='');
|
||||
UPDATE `inq_response_timeout` SET `timeout_code`='land_sea_express' WHERE `id`='rt100000000000000000000000000019' AND (`timeout_code` IS NULL OR `timeout_code`='');
|
||||
|
||||
-- 3) 唯一索引(列已全部非空后再建;若仍有 NULL 会失败)
|
||||
UPDATE `inq_response_timeout` SET `timeout_code`=CONCAT('rt_', `id`) WHERE `timeout_code` IS NULL OR `timeout_code`='';
|
||||
ALTER TABLE `inq_response_timeout`
|
||||
MODIFY COLUMN `timeout_code` varchar(64) NOT NULL COMMENT '唯一业务编码,智能体调用';
|
||||
ALTER TABLE `inq_response_timeout`
|
||||
ADD UNIQUE KEY `uk_inq_resp_timeout_code` (`timeout_code`);
|
||||
Reference in New Issue
Block a user