账号支持只看本账号看板;模板重新解析改为保留原映射并多轮补缺。
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+8
-1
@@ -70,8 +70,15 @@ public class InquiryAgentController {
|
||||
: (mappingObj instanceof String ? (String) mappingObj : JSON.toJSONString(mappingObj));
|
||||
String parseMessage = body.get("parseMessage") == null ? null : String.valueOf(body.get("parseMessage"));
|
||||
String parseStatus = body.get("parseStatus") == null ? null : String.valueOf(body.get("parseStatus"));
|
||||
boolean mergeExisting = false;
|
||||
Object mergeFlag = body.get("mergeExisting");
|
||||
if (mergeFlag instanceof Boolean) {
|
||||
mergeExisting = (Boolean) mergeFlag;
|
||||
} else if (mergeFlag != null) {
|
||||
mergeExisting = "true".equalsIgnoreCase(String.valueOf(mergeFlag));
|
||||
}
|
||||
try {
|
||||
quoteTemplateService.applyParseResult(versionId, ok, mappingJson, parseMessage, parseStatus);
|
||||
quoteTemplateService.applyParseResult(versionId, ok, mappingJson, parseMessage, parseStatus, mergeExisting);
|
||||
return Result.OK("ok");
|
||||
} catch (IllegalArgumentException e) {
|
||||
return Result.error(e.getMessage());
|
||||
|
||||
+8
-2
@@ -185,8 +185,14 @@ public class InquiryQuoteTemplateController {
|
||||
@ApiOperation("对最新 Excel 版重新提交 AI 异步解析(已解析/失败/映射未完成均可;解析中跳过)")
|
||||
@RequiresPermissions("inquiry:template:edit")
|
||||
@PostMapping("/reparse")
|
||||
public Result<String> reparse(@RequestParam String id) {
|
||||
String msg = quoteTemplateService.reparseLatest(id);
|
||||
public Result<String> reparse(@RequestParam(required = false) String id,
|
||||
@RequestParam(required = false) String templateId) {
|
||||
// 兼容旧前端只传 id、新前端传 templateId,任一即可。
|
||||
String key = StringUtils.isNotBlank(templateId) ? templateId : id;
|
||||
if (StringUtils.isBlank(key)) {
|
||||
return Result.error("templateId 不能为空");
|
||||
}
|
||||
String msg = quoteTemplateService.reparseLatest(key);
|
||||
return Result.OK(msg);
|
||||
}
|
||||
|
||||
|
||||
+4
-1
@@ -89,8 +89,11 @@ public interface IInquiryQuoteTemplateService extends IService<InquiryQuoteTempl
|
||||
* 智能体回写 AI 映射结果(成功 mappingJson 或失败原因)。
|
||||
* <p>
|
||||
* 终态优先:已是 parsed/mapping_incomplete/failed 时可被同 version 覆盖;pending 必接受。
|
||||
*
|
||||
* @param mergeExisting true=未换文件重新解析:旧字段优先保留,只补新映射;false=整份覆盖
|
||||
*/
|
||||
void applyParseResult(String versionId, boolean ok, String mappingJson, String parseMessage, String parseStatus);
|
||||
void applyParseResult(String versionId, boolean ok, String mappingJson, String parseMessage,
|
||||
String parseStatus, boolean mergeExisting);
|
||||
|
||||
/**
|
||||
* 纠偏:把「已有映射点但仍标 mapping_incomplete」的版本改为 parsed。
|
||||
|
||||
+78
-4
@@ -2,6 +2,8 @@ package org.jeecg.modules.inquiry.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.shiro.SecurityUtils;
|
||||
import org.jeecg.common.system.vo.LoginUser;
|
||||
import org.jeecg.modules.inquiry.domain.TicketStatuses;
|
||||
import org.jeecg.modules.inquiry.entity.InquiryStaffRole;
|
||||
import org.jeecg.modules.inquiry.entity.InquiryTicket;
|
||||
@@ -75,9 +77,11 @@ public class InquiryDashboardServiceImpl implements IInquiryDashboardService {
|
||||
Date from = Date.from(fromDate.atStartOfDay(ZONE).toInstant());
|
||||
Date to = Date.from(LocalDateTime.of(today, LocalTime.MAX).atZone(ZONE).toInstant());
|
||||
|
||||
List<InquiryTicket> tickets = ticketMapper.selectList(new LambdaQueryWrapper<InquiryTicket>()
|
||||
LambdaQueryWrapper<InquiryTicket> qw = new LambdaQueryWrapper<InquiryTicket>()
|
||||
.ge(InquiryTicket::getCreateTime, from)
|
||||
.le(InquiryTicket::getCreateTime, to));
|
||||
.le(InquiryTicket::getCreateTime, to);
|
||||
applyDashboardSelfOnly(qw);
|
||||
List<InquiryTicket> tickets = ticketMapper.selectList(qw);
|
||||
|
||||
long total = tickets.size();
|
||||
long deal = tickets.stream().filter(t -> TicketStatuses.DEAL.equals(t.getStatus())).count();
|
||||
@@ -115,9 +119,11 @@ public class InquiryDashboardServiceImpl implements IInquiryDashboardService {
|
||||
|
||||
// 近 7 日趋势 + 成本:一次拉周内工单,避免 7 次 count
|
||||
Date weekFrom = Date.from(today.minusDays(6).atStartOfDay(ZONE).toInstant());
|
||||
List<InquiryTicket> weekTickets = ticketMapper.selectList(new LambdaQueryWrapper<InquiryTicket>()
|
||||
LambdaQueryWrapper<InquiryTicket> weekQw = new LambdaQueryWrapper<InquiryTicket>()
|
||||
.ge(InquiryTicket::getCreateTime, weekFrom)
|
||||
.le(InquiryTicket::getCreateTime, to));
|
||||
.le(InquiryTicket::getCreateTime, to);
|
||||
applyDashboardSelfOnly(weekQw);
|
||||
List<InquiryTicket> weekTickets = ticketMapper.selectList(weekQw);
|
||||
|
||||
List<String> trendDates = new ArrayList<>(7);
|
||||
List<Map<String, Object>> trend = new ArrayList<>(7);
|
||||
@@ -442,4 +448,72 @@ public class InquiryDashboardServiceImpl implements IInquiryDashboardService {
|
||||
}
|
||||
return "sales";
|
||||
}
|
||||
|
||||
/**
|
||||
* 账号勾选「只看自己数据」时收紧看板工单范围。
|
||||
* <p>
|
||||
* 口径对齐原项目:非超管且 dashboardSelfOnly=true →
|
||||
* 工单 salesWecomId = 当前账号工号(企微 id)或 createBy = 登录名。
|
||||
* 超管(username=admin 或角色 admin)忽略开关。
|
||||
*/
|
||||
private void applyDashboardSelfOnly(LambdaQueryWrapper<InquiryTicket> qw) {
|
||||
LoginUser login = currentLoginUser();
|
||||
if (login == null) {
|
||||
return;
|
||||
}
|
||||
if (isAdministrator(login)) {
|
||||
return;
|
||||
}
|
||||
if (!Boolean.TRUE.equals(login.getDashboardSelfOnly())) {
|
||||
return;
|
||||
}
|
||||
String username = StringUtils.trimToEmpty(login.getUsername());
|
||||
String wecom = StringUtils.trimToEmpty(login.getWorkNo());
|
||||
qw.and(w -> {
|
||||
boolean any = false;
|
||||
if (StringUtils.isNotBlank(wecom)) {
|
||||
w.eq(InquiryTicket::getSalesWecomId, wecom);
|
||||
any = true;
|
||||
}
|
||||
if (StringUtils.isNotBlank(username)) {
|
||||
if (any) {
|
||||
w.or();
|
||||
}
|
||||
w.eq(InquiryTicket::getCreateBy, username);
|
||||
any = true;
|
||||
}
|
||||
if (!any) {
|
||||
// 未绑企微且无用户名:宁可不看数据,也不漏看全站
|
||||
w.apply("1 = 0");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static LoginUser currentLoginUser() {
|
||||
try {
|
||||
Object p = SecurityUtils.getSubject().getPrincipal();
|
||||
if (p instanceof LoginUser) {
|
||||
return (LoginUser) p;
|
||||
}
|
||||
} catch (Exception ignored) {
|
||||
// 无登录上下文时不做过滤
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/** 超管:登录名 admin,或 Shiro 角色 admin/管理员。 */
|
||||
private static boolean isAdministrator(LoginUser login) {
|
||||
if (login == null) {
|
||||
return false;
|
||||
}
|
||||
if ("admin".equalsIgnoreCase(StringUtils.trimToEmpty(login.getUsername()))) {
|
||||
return true;
|
||||
}
|
||||
try {
|
||||
return SecurityUtils.getSubject().hasRole("admin")
|
||||
|| SecurityUtils.getSubject().hasRole("管理员");
|
||||
} catch (Exception e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+151
-16
@@ -245,8 +245,8 @@ public class InquiryQuoteTemplateServiceImpl extends ServiceImpl<InquiryQuoteTem
|
||||
version.setMappingPointCount(0);
|
||||
version.setMappingJson("{\"schemaVersion\":\"quote-template-mapping-v1\",\"fields\":{},\"status\":\"pending\"}");
|
||||
versionMapper.insert(version);
|
||||
// 事务提交后再入队,避免 Worker 回写时版本行尚未可见
|
||||
scheduleAiParseAfterCommit(version.getId(), template.getId(), fileBytes);
|
||||
// 事务提交后再入队,避免 Worker 回写时版本行尚未可见;换文件 = 整份覆盖
|
||||
scheduleAiParseAfterCommit(version.getId(), template.getId(), fileBytes, false);
|
||||
}
|
||||
|
||||
if (isDefault) {
|
||||
@@ -269,8 +269,11 @@ public class InquiryQuoteTemplateServiceImpl extends ServiceImpl<InquiryQuoteTem
|
||||
|
||||
/**
|
||||
* 事务提交后提交 AI 解析;无事务时立刻提交。
|
||||
*
|
||||
* @param mergeExisting true=未换文件的重新解析,回写时与旧 mapping 补缺合并;false=换文件/新上传,整份覆盖
|
||||
*/
|
||||
private void scheduleAiParseAfterCommit(String versionId, String templateId, byte[] fileBytes) {
|
||||
private void scheduleAiParseAfterCommit(String versionId, String templateId, byte[] fileBytes,
|
||||
boolean mergeExisting) {
|
||||
Runnable job = () -> {
|
||||
try {
|
||||
InquiryQuoteTemplateVersion ver = versionMapper.selectById(versionId);
|
||||
@@ -279,7 +282,7 @@ public class InquiryQuoteTemplateServiceImpl extends ServiceImpl<InquiryQuoteTem
|
||||
log.warn("AI 解析入队跳过:版本或模板不存在 versionId={}", versionId);
|
||||
return;
|
||||
}
|
||||
String enqueueErr = submitAiParse(ver, template, fileBytes);
|
||||
String enqueueErr = submitAiParse(ver, template, fileBytes, mergeExisting);
|
||||
if (enqueueErr != null) {
|
||||
ver.setParseStatus("failed");
|
||||
ver.setParseMessage(enqueueErr);
|
||||
@@ -313,8 +316,11 @@ public class InquiryQuoteTemplateServiceImpl extends ServiceImpl<InquiryQuoteTem
|
||||
|
||||
/**
|
||||
* 抽单元格清单并提交智能体入队。成功返回 null,失败返回中文原因。
|
||||
*
|
||||
* @param mergeExisting 重新解析时 true:带上旧 mapping,回写补缺合并
|
||||
*/
|
||||
private String submitAiParse(InquiryQuoteTemplateVersion version, InquiryQuoteTemplate template, byte[] fileBytes) {
|
||||
private String submitAiParse(InquiryQuoteTemplateVersion version, InquiryQuoteTemplate template,
|
||||
byte[] fileBytes, boolean mergeExisting) {
|
||||
try {
|
||||
JSONObject inventory;
|
||||
try (InputStream in = new ByteArrayInputStream(fileBytes)) {
|
||||
@@ -335,6 +341,19 @@ public class InquiryQuoteTemplateServiceImpl extends ServiceImpl<InquiryQuoteTem
|
||||
body.put("cellCount", inventory.getIntValue("cellCount"));
|
||||
body.put("yellowCount", inventory.getIntValue("yellowCount"));
|
||||
body.put("truncated", inventory.getBooleanValue("truncated"));
|
||||
body.put("mergeExisting", mergeExisting);
|
||||
// 仅补缺合并时附带旧映射;换文件不传,避免模型/回写误用
|
||||
if (mergeExisting && StringUtils.isNotBlank(version.getMappingJson())) {
|
||||
try {
|
||||
JSONObject old = JSON.parseObject(version.getMappingJson());
|
||||
if (old != null && old.getJSONObject("fields") != null
|
||||
&& !old.getJSONObject("fields").isEmpty()) {
|
||||
body.put("existingMapping", old);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.warn("重新解析附带旧 mapping 失败 versionId={} err={}", version.getId(), e.getMessage());
|
||||
}
|
||||
}
|
||||
body.put("requestId", version.getId() + "-" + System.currentTimeMillis());
|
||||
return aiParseClient.enqueueParse(body);
|
||||
} catch (Exception e) {
|
||||
@@ -980,7 +999,7 @@ public class InquiryQuoteTemplateServiceImpl extends ServiceImpl<InquiryQuoteTem
|
||||
ver.setParseMessage("解析中,请稍候");
|
||||
ver.setUpdateTime(new Date());
|
||||
versionMapper.updateById(ver);
|
||||
scheduleAiParseAfterCommit(ver.getId(), template.getId(), fileBytes);
|
||||
scheduleAiParseAfterCommit(ver.getId(), template.getId(), fileBytes, true);
|
||||
enqueueSnapshotPreview(ver.getId());
|
||||
} catch (Exception e) {
|
||||
log.error("模板补解析失败 templateId={} objectKey={}", templateId, ver.getObjectKey(), e);
|
||||
@@ -993,10 +1012,13 @@ public class InquiryQuoteTemplateServiceImpl extends ServiceImpl<InquiryQuoteTem
|
||||
|
||||
/**
|
||||
* 智能体回写 AI 映射。校验白名单与 feeRegion 增行配置;未映射必填 → mapping_incomplete。
|
||||
* <p>
|
||||
* mergeExisting=true 时:与库中旧 mapping 补缺合并(旧字段/旧区域优先,新结果只补没有的)。
|
||||
*/
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void applyParseResult(String versionId, boolean ok, String mappingJson, String parseMessage, String parseStatus) {
|
||||
public void applyParseResult(String versionId, boolean ok, String mappingJson, String parseMessage,
|
||||
String parseStatus, boolean mergeExisting) {
|
||||
if (StringUtils.isBlank(versionId)) {
|
||||
throw new IllegalArgumentException("versionId 必填");
|
||||
}
|
||||
@@ -1032,6 +1054,20 @@ public class InquiryQuoteTemplateServiceImpl extends ServiceImpl<InquiryQuoteTem
|
||||
versionMapper.updateById(ver);
|
||||
return;
|
||||
}
|
||||
// 未换文件重新解析:旧映射优先,避免已对上的字段被新一轮 AI 冲掉
|
||||
if (mergeExisting && StringUtils.isNotBlank(ver.getMappingJson())) {
|
||||
try {
|
||||
JSONObject old = JSON.parseObject(ver.getMappingJson());
|
||||
if (old != null) {
|
||||
mapping = mergeMappingPreferExisting(old, mapping);
|
||||
log.info("模板映射补缺合并 versionId={} keepFields={}",
|
||||
versionId,
|
||||
old.getJSONObject("fields") == null ? 0 : old.getJSONObject("fields").size());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.warn("旧 mapping 合并跳过 versionId={} err={}", versionId, e.getMessage());
|
||||
}
|
||||
}
|
||||
String validateErr = validateAiMapping(mapping);
|
||||
if (validateErr != null) {
|
||||
ver.setParseStatus("failed");
|
||||
@@ -1049,12 +1085,103 @@ public class InquiryQuoteTemplateServiceImpl extends ServiceImpl<InquiryQuoteTem
|
||||
applyExcelMapping(ver, template != null ? template : new InquiryQuoteTemplate(), mapping);
|
||||
if (StringUtils.isNotBlank(parseMessage) && "failed".equals(ver.getParseStatus())) {
|
||||
ver.setParseMessage(parseMessage);
|
||||
} else if (mergeExisting && !"failed".equals(ver.getParseStatus())) {
|
||||
// 合并后重算摘要点数,避免沿用 AI 单轮字数
|
||||
int pts = ver.getMappingPointCount() == null ? 0 : ver.getMappingPointCount();
|
||||
String base = "已解析 " + pts + " 个映射点(保留原映射并补缺)";
|
||||
if (StringUtils.isNotBlank(parseMessage) && parseMessage.contains("未解析到")) {
|
||||
int idx = parseMessage.indexOf(";未解析到");
|
||||
if (idx > 0) {
|
||||
base = base + parseMessage.substring(idx);
|
||||
}
|
||||
}
|
||||
ver.setParseMessage(base);
|
||||
}
|
||||
ver.setUpdateBy("agent");
|
||||
ver.setUpdateTime(new Date());
|
||||
versionMapper.updateById(ver);
|
||||
}
|
||||
|
||||
/**
|
||||
* 补缺合并:旧 fields / feeRegions / routeRegions 优先;新结果只补充旧里没有的 code/id。
|
||||
* 不删旧项;mappingGaps 以新结果为准(智能体已按合并后复评时更准,此处兜底保留新 gaps)。
|
||||
*/
|
||||
static JSONObject mergeMappingPreferExisting(JSONObject oldMapping, JSONObject newMapping) {
|
||||
JSONObject out = new JSONObject(true);
|
||||
if (newMapping != null) {
|
||||
out.putAll(newMapping);
|
||||
}
|
||||
if (oldMapping == null) {
|
||||
return out;
|
||||
}
|
||||
// fields:先放新的,再用旧的覆盖同名 → 旧优先
|
||||
JSONObject fields = new JSONObject(true);
|
||||
JSONObject newFields = newMapping == null ? null : newMapping.getJSONObject("fields");
|
||||
JSONObject oldFields = oldMapping.getJSONObject("fields");
|
||||
if (newFields != null) {
|
||||
fields.putAll(newFields);
|
||||
}
|
||||
if (oldFields != null) {
|
||||
for (String code : oldFields.keySet()) {
|
||||
Object cell = oldFields.get(code);
|
||||
if (cell != null && StringUtils.isNotBlank(String.valueOf(cell))) {
|
||||
fields.put(code, cell);
|
||||
}
|
||||
}
|
||||
}
|
||||
out.put("fields", fields);
|
||||
out.put("feeRegions", mergeRegionsById(
|
||||
oldMapping.getJSONArray("feeRegions"),
|
||||
newMapping == null ? null : newMapping.getJSONArray("feeRegions")));
|
||||
out.put("routeRegions", mergeRegionsById(
|
||||
oldMapping.getJSONArray("routeRegions"),
|
||||
newMapping == null ? null : newMapping.getJSONArray("routeRegions")));
|
||||
// 说明性标签:齐全时清空;否则保留新的
|
||||
if (out.getJSONArray("mappingGaps") != null && out.getJSONArray("mappingGaps").isEmpty()) {
|
||||
out.put("unmappedRequired", new JSONArray());
|
||||
out.put("unmappedLabels", new JSONArray());
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
/**
|
||||
* 区域按 id 合并:旧 id 整段保留;新 id 追加在后。
|
||||
*/
|
||||
private static JSONArray mergeRegionsById(JSONArray oldArr, JSONArray newArr) {
|
||||
JSONArray out = new JSONArray();
|
||||
Set<String> seen = new LinkedHashSet<>();
|
||||
if (oldArr != null) {
|
||||
for (int i = 0; i < oldArr.size(); i++) {
|
||||
JSONObject r = oldArr.getJSONObject(i);
|
||||
if (r == null) {
|
||||
continue;
|
||||
}
|
||||
String id = StringUtils.defaultString(r.getString("id"));
|
||||
if (StringUtils.isNotBlank(id)) {
|
||||
seen.add(id);
|
||||
}
|
||||
out.add(r);
|
||||
}
|
||||
}
|
||||
if (newArr != null) {
|
||||
for (int i = 0; i < newArr.size(); i++) {
|
||||
JSONObject r = newArr.getJSONObject(i);
|
||||
if (r == null) {
|
||||
continue;
|
||||
}
|
||||
String id = StringUtils.defaultString(r.getString("id"));
|
||||
if (StringUtils.isNotBlank(id) && seen.contains(id)) {
|
||||
continue;
|
||||
}
|
||||
if (StringUtils.isNotBlank(id)) {
|
||||
seen.add(id);
|
||||
}
|
||||
out.add(r);
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
/**
|
||||
* 把误标为 mapping_incomplete、但已有映射点的版本纠偏为 parsed。
|
||||
*/
|
||||
@@ -1384,7 +1511,10 @@ public class InquiryQuoteTemplateServiceImpl extends ServiceImpl<InquiryQuoteTem
|
||||
|
||||
/**
|
||||
* 从 mappingJson 取出智能体对照清单算出的未映射说明(中文)。
|
||||
* 若 mappingGaps 为空,用 unmappedLabels / unmappedRequired 兜底,保证列表能提示。
|
||||
* <p>
|
||||
* 口径:有 {@code mappingGaps} 字段时(含空数组)只认它——空数组即「系统判定齐全、角标 0」;
|
||||
* 仅旧数据完全没有该字段时,才用 unmappedLabels / unmappedRequired 兜底。
|
||||
* 说明性 unmappedLabels 不得在齐全时抬高列表「未解析」角标。
|
||||
*/
|
||||
private static List<String> extractMappingGaps(String mappingJson) {
|
||||
if (StringUtils.isBlank(mappingJson)) {
|
||||
@@ -1392,19 +1522,24 @@ public class InquiryQuoteTemplateServiceImpl extends ServiceImpl<InquiryQuoteTem
|
||||
}
|
||||
try {
|
||||
JSONObject obj = JSON.parseObject(mappingJson);
|
||||
JSONArray arr = obj.getJSONArray("mappingGaps");
|
||||
if (obj == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
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);
|
||||
// 显式有 mappingGaps 键:空也算齐全,禁止再用 AI 自报标签抬角标
|
||||
if (obj.containsKey("mappingGaps")) {
|
||||
JSONArray arr = obj.getJSONArray("mappingGaps");
|
||||
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;
|
||||
}
|
||||
// 旧版映射无 mappingGaps:才兜底
|
||||
JSONArray labels = obj.getJSONArray("unmappedLabels");
|
||||
if (labels != null) {
|
||||
for (int i = 0; i < labels.size(); i++) {
|
||||
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
-- 账号「只看本账号看板数据」+ 确保 admin 绑定超级管理员角色
|
||||
-- 库:inquiry_robot;幂等。
|
||||
|
||||
-- 1) 列
|
||||
SET @has_col := (
|
||||
SELECT COUNT(*) FROM information_schema.columns
|
||||
WHERE table_schema = DATABASE()
|
||||
AND table_name = 'sys_user'
|
||||
AND column_name = 'dashboard_self_only'
|
||||
);
|
||||
SET @sql := IF(@has_col = 0,
|
||||
'ALTER TABLE sys_user ADD COLUMN dashboard_self_only tinyint(1) NOT NULL DEFAULT 0 COMMENT ''只看本账号看板数据:1=是,0=否;超管忽略''',
|
||||
'SELECT 1');
|
||||
PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
|
||||
|
||||
-- 2) admin 绑定 role_code=admin(无则补一行)
|
||||
INSERT INTO sys_user_role (id, user_id, role_id)
|
||||
SELECT REPLACE(UUID(), '-', ''), u.id, r.id
|
||||
FROM sys_user u
|
||||
JOIN sys_role r ON r.role_code = 'admin'
|
||||
WHERE u.username = 'admin'
|
||||
AND NOT EXISTS (
|
||||
SELECT 1 FROM sys_user_role ur
|
||||
WHERE ur.user_id = u.id AND ur.role_id = r.id
|
||||
)
|
||||
LIMIT 1;
|
||||
Reference in New Issue
Block a user