From 1490df4f27fb6c42dbc4fad21c6f55f794eb1542 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E9=BD=8A?= <23>
Date: Sun, 16 Aug 2026 10:59:31 +0800
Subject: [PATCH 1/3] =?UTF-8?q?=E4=BF=AE=E6=94=B9?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
application-tenant/tenant-admin/pom.xml | 13 +-
.../tenant/controller/DocxTemplateController.java | 141 -----------
.../upms/controller/DocxTemplateController.java | 260 +++++++++++++++++++++
.../upms/controller/LoginController.java | 46 ++++
.../upms/service/SupersetSsoService.java | 121 ++++++++++
.../src/main/resources/application-config.yml | 5 +
common/common-association/pom.xml | 9 +-
.../association/controller/HomeController.java | 2 +-
.../plugin/active/ActivityInfoDetailsPlugin.java | 35 ++-
.../active/ActivityInvitationSavePlugin.java | 48 ++++
.../member/MembershipApplyListFilterPlugin.java | 153 +++++++++++-
.../member/MembershipBenefitsUpdatePlugin.java | 48 ++++
.../plugin/member/MembershipFeePayPlugin.java | 5 +
.../plugin/member/MembershipPayPopupPlugin.java | 4 -
.../plugin/member/MembershipSavePlugin.java | 9 +-
.../member/UnitNameChangeFormInitPlugin.java | 4 +
.../plugin/member/UnitNameChangePopupPlugin.java | 128 ----------
common/common-qy/pom.xml | 5 +-
18 files changed, 738 insertions(+), 298 deletions(-)
delete mode 100644 application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/controller/DocxTemplateController.java
create mode 100644 application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/controller/DocxTemplateController.java
create mode 100644 application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/service/SupersetSsoService.java
create mode 100644 common/common-association/src/main/java/apelet/association/plugin/active/ActivityInvitationSavePlugin.java
create mode 100644 common/common-association/src/main/java/apelet/association/plugin/member/MembershipBenefitsUpdatePlugin.java
diff --git a/application-tenant/tenant-admin/pom.xml b/application-tenant/tenant-admin/pom.xml
index 9c94759..867b72e 100644
--- a/application-tenant/tenant-admin/pom.xml
+++ b/application-tenant/tenant-admin/pom.xml
@@ -141,7 +141,7 @@
apelet
common-flow-online
- 1.0.0
+ 1.0.2
apelet
@@ -174,6 +174,17 @@
1.0.0
+
+ org.docx4j
+ docx4j-JAXB-ReferenceImpl
+ 8.3.9
+
+
+ org.docx4j
+ docx4j-export-fo
+ 8.3.9
+
+
diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/controller/DocxTemplateController.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/controller/DocxTemplateController.java
deleted file mode 100644
index 1c3af35..0000000
--- a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/tenant/controller/DocxTemplateController.java
+++ /dev/null
@@ -1,141 +0,0 @@
-
-package apelet.tenantadmin.tenant.controller;
-
-import apelet.common.core.annotation.MyRequestBody;
-import apelet.common.core.exception.MyRuntimeException;
-import apelet.common.core.object.ObjectValue;
-import apelet.common.generator.utils.OrmGenDataSourceUtil;
-import cn.hutool.core.io.IoUtil;
-import org.apache.poi.xwpf.usermodel.*;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.core.io.ClassPathResource;
-import org.springframework.web.bind.annotation.*;
-
-import javax.servlet.http.HttpServletResponse;
-import java.io.*;
-import java.net.URLEncoder;
-import java.nio.charset.StandardCharsets;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-@RestController
-@RequestMapping("/api/template")
-public class DocxTemplateController {
-
-
- @Autowired
- private OrmGenDataSourceUtil ormGenDataSourceUtil;
-
- private static final Pattern PLACEHOLDER_PATTERN = Pattern.compile("\\$\\{(.+?)\\}");
-
- /**
- * 替换字符串中 ${key},JDK8兼容
- */
- private String replaceContent(String text, Map dataMap) {
- if (text == null) return "";
- Matcher matcher = PLACEHOLDER_PATTERN.matcher(text);
- StringBuffer sb = new StringBuffer();
- while (matcher.find()) {
- String key = matcher.group(1).trim();
- Object val = dataMap.get(key);
- String realVal = val == null ? "" : val.toString();
- matcher.appendReplacement(sb, Matcher.quoteReplacement(realVal));
- }
- matcher.appendTail(sb);
- return sb.toString();
- }
-
- @GetMapping("/downloadDocx")
- public void downloadDocx(@RequestParam("id") String quotationId, HttpServletResponse response) {
- XWPFDocument document = null;
- InputStream templateIs = null;
- OutputStream out = null;
- try {
- // 1. 查数据
- ObjectValue objectValue = ormGenDataSourceUtil.queryOne("quotation", quotationId);
- Map hashMap = new HashMap<>();
- hashMap.put("name", objectValue.getString("name"));
- hashMap.put("number", objectValue.getString("number"));
- hashMap.put("create_time", objectValue.getString("create_time"));
- hashMap.put("qty", objectValue.getObjectValue("qty"));
- ObjectValue managerperson = objectValue.getObjectValue("managerperson");
- if(managerperson != null){
- managerperson = ormGenDataSourceUtil.queryOne(managerperson.getTableName(), managerperson.getString("id"));
- hashMap.put("managerperson",1);
- }
- // 2. 读模板
- File file = new File("D:\\work\\xykj-project\\xhgl\\zz-resource\\quotation_template.docx");
- templateIs = new FileInputStream(file);
- document = new XWPFDocument(templateIs);
- // 3. 替换占位符
- replaceParagraph(document, hashMap);
- replaceTable(document, hashMap);
- // 4. 设置响应头 —— 关键!
- response.setContentType("application/vnd.openxmlformats-officedocument.wordprocessingml.document");
- response.setCharacterEncoding("UTF-8");
- String fileName = URLEncoder.encode("报价单.docx", StandardCharsets.UTF_8.name())
- .replaceAll("\\+", "%20");
- // 兼容各浏览器的文件名写法
- response.setHeader("Content-Disposition",
- "attachment; filename=\"" + fileName + "\"; filename*=UTF-8''" + fileName);
- response.setHeader("Pragma", "no-cache");
- response.setHeader("Cache-Control", "no-cache");
- response.setDateHeader("Expires", 0);
- // 5. 写出
- out = response.getOutputStream();
- document.write(out);
- out.flush();
- } catch (Exception e) {
- e.printStackTrace();
- throw new MyRuntimeException("导出失败: " + e.getMessage());
- } finally {
- IoUtil.close(document);
- IoUtil.close(templateIs);
- IoUtil.close(out);
- }
- }
-
-
-
-
- private void replaceParagraph(XWPFDocument document, Map dataMap) {
- for (XWPFParagraph paragraph : document.getParagraphs()) {
- String text = paragraph.getText();
- Matcher matcher = PLACEHOLDER_PATTERN.matcher(text);
- if (!matcher.find()) {
- continue;
- }
- //清空原有run
- for (XWPFRun run : paragraph.getRuns()) {
- run.setText("", 0);
- }
- String newText = replaceContent(text, dataMap);
- paragraph.createRun().setText(newText);
- }
- }
-
- private void replaceTable(XWPFDocument document, Map dataMap) {
- for (XWPFTable table : document.getTables()) {
- for (XWPFTableRow row : table.getRows()) {
- for (XWPFTableCell cell : row.getTableCells()) {
- for (XWPFParagraph paragraph : cell.getParagraphs()) {
- String text = paragraph.getText();
- Matcher matcher = PLACEHOLDER_PATTERN.matcher(text);
- if (!matcher.find()) {
- continue;
- }
- for (XWPFRun run : paragraph.getRuns()) {
- run.setText("", 0);
- }
- String newText = replaceContent(text, dataMap);
- paragraph.createRun().setText(newText);
- }
- }
- }
- }
- }
-
-
-}
diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/controller/DocxTemplateController.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/controller/DocxTemplateController.java
new file mode 100644
index 0000000..e9659fe
--- /dev/null
+++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/controller/DocxTemplateController.java
@@ -0,0 +1,260 @@
+package apelet.tenantadmin.upms.controller;
+
+import apelet.common.core.exception.MyRuntimeException;
+import apelet.common.core.object.ObjectValue;
+import apelet.common.core.object.TokenData;
+import apelet.common.generator.utils.OrmGenDataSourceUtil;
+import apelet.common.online.service.OnlineDictService;
+import cn.hutool.core.io.IoUtil;
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
+import org.apache.poi.xwpf.usermodel.*;
+import org.docx4j.Docx4J;
+import org.docx4j.openpackaging.packages.WordprocessingMLPackage;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+import javax.servlet.http.HttpServletResponse;
+import java.io.*;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.net.URLEncoder;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+@RestController
+@RequestMapping("/api/template")
+public class DocxTemplateController {
+
+ @Autowired
+ private OrmGenDataSourceUtil ormGenDataSourceUtil;
+ @Autowired
+ private OnlineDictService onlineDictService;
+
+ private static final Pattern PLACEHOLDER_PATTERN = Pattern.compile("#\\{(.+?)\\}");
+
+ private String replaceContent(String text, Map dataMap) {
+ if (text == null) return "";
+ Matcher matcher = PLACEHOLDER_PATTERN.matcher(text);
+ StringBuffer sb = new StringBuffer();
+ while (matcher.find()) {
+ String key = matcher.group(1).trim();
+ if ("personimage".equals(key)) {
+ matcher.appendReplacement(sb, Matcher.quoteReplacement(matcher.group(0)));
+ continue;
+ }
+ Object val = dataMap.get(key);
+ String realVal = val == null ? "" : val.toString();
+ matcher.appendReplacement(sb, Matcher.quoteReplacement(realVal));
+ }
+ matcher.appendTail(sb);
+ return sb.toString();
+ }
+
+
+ @GetMapping("/downloadDocx")
+ public void downloadDocx(@RequestParam("id") String quotationId, @RequestParam("userId") String userId, HttpServletResponse response) {
+ XWPFDocument document = null;
+ InputStream templateIs = null;
+ OutputStream out = null;
+ ByteArrayOutputStream pdfOut = null;
+ try {
+ // 1. 查数据
+ ObjectValue objectValue = ormGenDataSourceUtil.queryOne("quotation", quotationId);
+ ObjectValue user = ormGenDataSourceUtil.queryOne("xy_sys_user", userId);
+ Map dictMap = onlineDictService.getDictMap(2052225870772310016L);
+ Map hashMap = new HashMap<>();
+ hashMap.put("name", objectValue.getString("name"));
+ hashMap.put("type", dictMap.get(String.valueOf(objectValue.get("type", ""))));
+ hashMap.put("number", objectValue.getString("number"));
+ hashMap.put("create_time", objectValue.getString("create_time"));
+ hashMap.put("qty", objectValue.getString("qty"));
+
+ String signatureImage = user.getString("signature_image");
+ String imageUrl = getUrl(signatureImage);
+ byte[] imageBytes = null;
+ if (StringUtils.isNotEmpty(imageUrl)) {
+ imageBytes = downloadImage(imageUrl);
+ }
+ ObjectValue managerperson = objectValue.getObjectValue("managerperson");
+ if (managerperson != null) {
+ managerperson = ormGenDataSourceUtil.queryOne(managerperson.getTableName(), managerperson.getString("id"));
+ hashMap.put("managerperson", managerperson.getString("show_name"));
+ }
+ // 2. 读模板
+ String savePath = System.getProperty("user.dir") + "\\zz-resource\\";
+ File file = new File(savePath + "quotation_template.docx");
+ templateIs = Files.newInputStream(file.toPath());
+ document = new XWPFDocument(templateIs);
+ // 3. 替换文本占位符
+ replaceParagraph(document, hashMap);
+ replaceTable(document, hashMap);
+ // 4. 替换图片占位符
+ replaceImageInParagraphs(document, "personimage", imageBytes, 100, 100);
+ replaceImageInTables(document, "personimage", imageBytes, 100, 100);
+ // 5. 将填充后的 DOCX 转为 PDF(使用 docx4j)
+ ByteArrayOutputStream docxOut = new ByteArrayOutputStream();
+ document.write(docxOut);
+ byte[] docxBytes = docxOut.toByteArray();
+ docxOut.close();
+
+ WordprocessingMLPackage wordMLPackage = WordprocessingMLPackage.load(new ByteArrayInputStream(docxBytes));
+ pdfOut = new ByteArrayOutputStream();
+ Docx4J.toPDF(wordMLPackage, pdfOut);
+ byte[] pdfBytes = pdfOut.toByteArray();
+
+ // 6. 设置响应头
+ response.setContentType("application/pdf");
+ response.setCharacterEncoding("UTF-8");
+ String fileName = URLEncoder.encode("报价单.pdf", StandardCharsets.UTF_8.name())
+ .replaceAll("\\+", "%20");
+ response.setHeader("Content-Disposition",
+ "attachment; filename=\"" + fileName + "\"; filename*=UTF-8''" + fileName);
+ response.setHeader("Pragma", "no-cache");
+ response.setHeader("Cache-Control", "no-cache");
+ response.setDateHeader("Expires", 0);
+ // 7. 写出 PDF
+ out = response.getOutputStream();
+ out.write(pdfBytes);
+ out.flush();
+ } catch (Exception e) {
+ e.printStackTrace();
+ throw new MyRuntimeException("导出失败: " + e.getMessage());
+ } finally {
+ IoUtil.close(pdfOut);
+ IoUtil.close(document);
+ IoUtil.close(templateIs);
+ IoUtil.close(out);
+ }
+ }
+
+
+ String getUrl(String signatureImage) {
+ if (StringUtils.isEmpty(signatureImage)) {
+ return "";
+ }
+ JSONArray jsonArray = JSON.parseArray(signatureImage);
+ if (jsonArray.isEmpty()) {
+ return "";
+ }
+ JSONObject jsonObject = jsonArray.getJSONObject(0);
+ String baseUrl = jsonObject.getString("baseUrl");
+ String uploadPath = jsonObject.getString("uploadPath");
+ String filename = jsonObject.getString("filename");
+
+ return baseUrl + "/" + uploadPath + "/" + filename;
+ }
+
+ private void replaceParagraph(XWPFDocument document, Map dataMap) {
+ for (XWPFParagraph paragraph : document.getParagraphs()) {
+ String text = paragraph.getText();
+ Matcher matcher = PLACEHOLDER_PATTERN.matcher(text);
+ if (!matcher.find()) {
+ continue;
+ }
+ for (XWPFRun run : paragraph.getRuns()) {
+ run.setText("", 0);
+ }
+ String newText = replaceContent(text, dataMap);
+ paragraph.createRun().setText(newText);
+ }
+ }
+
+ private void replaceTable(XWPFDocument document, Map dataMap) {
+ for (XWPFTable table : document.getTables()) {
+ for (XWPFTableRow row : table.getRows()) {
+ for (XWPFTableCell cell : row.getTableCells()) {
+ for (XWPFParagraph paragraph : cell.getParagraphs()) {
+ String text = paragraph.getText();
+ Matcher matcher = PLACEHOLDER_PATTERN.matcher(text);
+ if (!matcher.find()) {
+ continue;
+ }
+ for (XWPFRun run : paragraph.getRuns()) {
+ run.setText("", 0);
+ }
+ String newText = replaceContent(text, dataMap);
+ paragraph.createRun().setText(newText);
+ }
+ }
+ }
+ }
+ }
+
+ private byte[] downloadImage(String imageUrl) throws IOException {
+ URL url = new URL(imageUrl);
+ HttpURLConnection conn = (HttpURLConnection) url.openConnection();
+ conn.setRequestMethod("GET");
+ conn.setConnectTimeout(5000);
+ conn.setReadTimeout(10000);
+ InputStream is = conn.getInputStream();
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ byte[] buffer = new byte[4096];
+ int len;
+ while ((len = is.read(buffer)) != -1) {
+ baos.write(buffer, 0, len);
+ }
+ IoUtil.close(is);
+ return baos.toByteArray();
+ }
+
+
+
+ private void replaceImageInParagraphs(XWPFDocument document, String placeholderKey, byte[] imageBytes, int width, int height) throws IOException, InvalidFormatException {
+ String placeholder = "#{" + placeholderKey + "}";
+ for (XWPFParagraph paragraph : document.getParagraphs()) {
+ String text = paragraph.getText();
+ if (text != null && text.contains(placeholder)) {
+ List runs = paragraph.getRuns();
+ for (int i = runs.size() - 1; i >= 0; i--) {
+ XWPFRun run = runs.get(i);
+ String runText = run.getText(0);
+ if (runText != null && runText.contains(placeholder)) {
+ run.setText("", 0);
+ if (imageBytes != null) {
+ run.addPicture(new ByteArrayInputStream(imageBytes), XWPFDocument.PICTURE_TYPE_PNG, placeholderKey + ".png",
+ width * 9525, height * 9525);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ private void replaceImageInTables(XWPFDocument document, String placeholderKey, byte[] imageBytes, int width, int height) throws IOException, InvalidFormatException {
+ String placeholder = "#{" + placeholderKey + "}";
+ for (XWPFTable table : document.getTables()) {
+ for (XWPFTableRow row : table.getRows()) {
+ for (XWPFTableCell cell : row.getTableCells()) {
+ for (XWPFParagraph paragraph : cell.getParagraphs()) {
+ String text = paragraph.getText();
+ if (text != null && text.contains(placeholder)) {
+ List runs = paragraph.getRuns();
+ for (int i = runs.size() - 1; i >= 0; i--) {
+ XWPFRun run = runs.get(i);
+ String runText = run.getText(0);
+ if (runText != null && runText.contains(placeholder)) {
+ run.setText("", 0);
+ if (imageBytes != null) {
+ run.addPicture(new ByteArrayInputStream(imageBytes), XWPFDocument.PICTURE_TYPE_PNG, placeholderKey + ".png",
+ width * 9525, height * 9525);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/controller/LoginController.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/controller/LoginController.java
index 7387b36..c38e7ee 100644
--- a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/controller/LoginController.java
+++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/controller/LoginController.java
@@ -126,6 +126,10 @@ public class LoginController {
private UpDownloaderFactory upDownloaderFactory;
@Autowired
private DataFilterProperties dataFilterProperties;
+
+ @Autowired
+ private SupersetSsoService supersetSsoService;
+
@Value("${hubCloud.secret}")
private String HubCloudSecret;
@Value("${dingTalk.secret}")
@@ -196,6 +200,48 @@ public class LoginController {
return ResponseResult.success(myPageData);
}
+
+
+
+ @GetMapping("/getSupersetDashboardUrl")
+ public ResponseResult getSupersetDashboardUrl(
+ @RequestParam(name = "dashboardId") String dashboardId) {
+ try {
+ String accessToken = supersetSsoService.getAccessToken();
+ JSONObject result = new JSONObject();
+ result.put("accessToken", accessToken);
+ result.put("dashboardUrl", supersetSsoService.buildDashboardUrl(dashboardId));
+ return ResponseResult.success(result);
+ } catch (Exception e) {
+ log.error("获取Superset仪表盘地址失败", e);
+ return ResponseResult.error("500", "获取Superset仪表盘地址失败: " + e.getMessage());
+ }
+ }
+
+ @GetMapping("/getSupersetGuestToken")
+ public ResponseResult getSupersetGuestToken(
+ @RequestParam(name = "dashboardId") String dashboardId,
+ @RequestParam(name = "username", required = false) String username,
+ @RequestParam(name = "firstName", required = false, defaultValue = "Guest") String firstName,
+ @RequestParam(name = "lastName", required = false, defaultValue = "User") String lastName) {
+ try {
+ TokenData tokenData = TokenData.takeFromRequest();
+ if (StrUtil.isBlank(username)) {
+ username = tokenData != null ? tokenData.getLoginName() : "guest_user";
+ }
+ String guestToken = supersetSsoService.getGuestToken(
+ dashboardId, username, firstName, lastName, null);
+ JSONObject result = new JSONObject();
+ result.put("guestToken", guestToken);
+ result.put("embeddedUrl", supersetSsoService.buildEmbeddedDashboardUrl(dashboardId, guestToken));
+ return ResponseResult.success(result);
+ } catch (Exception e) {
+ log.error("获取Superset Guest Token失败", e);
+ return ResponseResult.error("500", "获取Superset Guest Token失败: " + e.getMessage());
+ }
+ }
+
+
private BillData convertOvToBillData(ObjectValue objectValue) {
BillData billData = new BillData();
diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/service/SupersetSsoService.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/service/SupersetSsoService.java
new file mode 100644
index 0000000..b8a9e82
--- /dev/null
+++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/service/SupersetSsoService.java
@@ -0,0 +1,121 @@
+package apelet.tenantadmin.upms.service;
+
+import cn.hutool.http.HttpRequest;
+import cn.hutool.http.HttpResponse;
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONObject;
+import lombok.extern.slf4j.Slf4j;
+import org.redisson.api.RBucket;
+import org.redisson.api.RedissonClient;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Service;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+
+@Slf4j
+@Service
+public class SupersetSsoService {
+
+ @Value("${superset.baseUrl}")
+ private String supersetBaseUrl;
+
+ @Value("${superset.username}")
+ private String supersetUsername;
+
+ @Value("${superset.password}")
+ private String supersetPassword;
+
+ @Autowired
+ private RedissonClient redissonClient;
+
+ private static final String SUPERSET_TOKEN_CACHE_KEY = "superset:access_token";
+ private static final int TOKEN_CACHE_SECONDS = 3500;
+
+ public String getAccessToken() {
+ RBucket cached = redissonClient.getBucket(SUPERSET_TOKEN_CACHE_KEY);
+ if (cached.isExists()) {
+ return cached.get();
+ }
+ String url = supersetBaseUrl + "/api/v1/security/login";
+ JSONObject payload = new JSONObject();
+ payload.put("username", supersetUsername);
+ payload.put("password", supersetPassword);
+ payload.put("provider", "db");
+ payload.put("refresh", true);
+
+ HttpResponse response = HttpRequest.post(url)
+ .header("Content-Type", "application/json")
+ .body(payload.toJSONString())
+ .timeout(10000)
+ .execute();
+
+ if (!response.isOk()) {
+ log.error("Superset login failed, status={}, body={}", response.getStatus(), response.body());
+ throw new RuntimeException("Superset认证失败: " + response.body());
+ }
+
+ JSONObject result = JSON.parseObject(response.body());
+ String accessToken = result.getString("access_token");
+
+ cached.set(accessToken, TOKEN_CACHE_SECONDS, TimeUnit.SECONDS);
+ log.info("Superset access token refreshed successfully.");
+ return accessToken;
+ }
+
+ public String getGuestToken(String dashboardId, String username, String firstName, String lastName,
+ List