- Add Python script for downloading C4C attachments via OData and web scraping - Add Java wrapper for programmatic access with typed API - Add DSM upload utility for Synology NAS integration - Add CLAUDE.md documentation for future development - Add .gitignore for Python, Java, and sensitive files
508 lines
22 KiB
Java
508 lines
22 KiB
Java
import com.fasterxml.jackson.databind.JsonNode;
|
||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||
|
||
import java.io.*;
|
||
import java.nio.charset.StandardCharsets;
|
||
import java.nio.file.Path;
|
||
import java.nio.file.Paths;
|
||
import java.util.ArrayList;
|
||
import java.util.List;
|
||
import java.util.concurrent.TimeUnit;
|
||
|
||
/**
|
||
* SAP C4C 附件下载工具 - Java 调用封装
|
||
*
|
||
* <p>通过 ProcessBuilder 调用 Python 脚本 sap-c4c-AttachmentFolder.py,
|
||
* 以 --json 模式获取结构化结果。</p>
|
||
*
|
||
* <h3>前置条件</h3>
|
||
* <pre>
|
||
* pip install requests scrapling[all] playwright
|
||
* python -m playwright install chromium
|
||
* </pre>
|
||
*
|
||
* <h3>使用示例</h3>
|
||
* <pre>{@code
|
||
* C4CAttachmentDownloader downloader = new C4CAttachmentDownloader(
|
||
* "/path/to/sap-c4c-AttachmentFolder.py",
|
||
* "https://xxx.c4c.saphybriscloud.cn",
|
||
* "admin",
|
||
* "password"
|
||
* );
|
||
*
|
||
* // 仅列出附件清单
|
||
* C4CAttachmentDownloader.Result result = downloader.listAttachments("24588");
|
||
*
|
||
* // 下载全部附件到指定目录
|
||
* C4CAttachmentDownloader.Result result = downloader.download("24588", "/tmp/ticket_24588");
|
||
* for (C4CAttachmentDownloader.DownloadedFile f : result.getDownloadedFiles()) {
|
||
* System.out.println(f.getSavedPath());
|
||
* }
|
||
*
|
||
* // 下载附件并上传到群晖 DSM
|
||
* downloader.setDsmConfig("http://10.0.10.235:5000", "PLM", "123456", "/Newgonow/AU-SPFJ");
|
||
* C4CAttachmentDownloader.Result result = downloader.download("24588", "/tmp/ticket_24588");
|
||
* for (C4CAttachmentDownloader.DsmUploadEntry u : result.getDsmUpload()) {
|
||
* System.out.println(u.getFile() + " -> " + u.getRemotePath() + " success=" + u.isSuccess());
|
||
* }
|
||
* }</pre>
|
||
*/
|
||
|
||
// [2026-03-12 11:46:10] INFO: Fetched (200) <GET https://regentrv.my.salesforce.com/sfc/p/#2t000000cc9V/a/W2000002EZ3B/4JQvOAb263K5kCW8sIAbYT.nFMeW8z7hT9QS4h0P9Mg> (referer: https://www.google.com/)
|
||
// [2026-03-12 11:46:24] INFO: Fetched (200) <GET https://regentrv.my.salesforce.com/sfc/p/#2t000000cc9V/a/W2000002EZ4n/ppCBH.gcnV2K.3qCA_U9ZDtZ9r_UffyJGEuteuWMFAE> (referer: https://www.google.com/)
|
||
// [2026-03-12 11:46:40] INFO: Fetched (200) <GET https://regentrv.my.salesforce.com/sfc/p/#2t000000cc9V/a/W2000002EZ6P/mDuF2NKyP96SGjWzuxXl7eejFb84ORKtcG0bPUAqrsg> (referer: https://www.google.com/)
|
||
// [2026-03-12 11:46:56] INFO: Fetched (200) <GET https://regentrv.my.salesforce.com/sfc/p/#2t000000cc9V/a/W2000002EWRu/v46Grw_mnbsjKrU1LlWrN7Ysh9vT5iOydQbr0nSYsxw> (referer: https://www.google.com/)
|
||
// {
|
||
// "ticketId": "24588",
|
||
// "outputDir": "/tmp/att",
|
||
// "success": true,
|
||
// "error": null,
|
||
// "srAttachments": [
|
||
// {
|
||
// "UUID": "FA163E87-49F0-1FD0-A0C8-22793B4E3F77",
|
||
// "ObjectID": "FA163E8749F01FD0A0C822793B4E3F77",
|
||
// "ParentObjectID": "FA163E8749F01FD0A0C822793B4EDF77",
|
||
// "FileName": "Quotation",
|
||
// "MimeType": "application/octet-stream",
|
||
// "CategoryCode": "3",
|
||
// "LinkWebURI": "https://regentrv.my.salesforce.com/sfc/p/2t000000cc9V/a/W2000002EZ3B/4JQvOAb263K5kCW8sIAbYT.nFMeW8z7hT9QS4h0P9Mg",
|
||
// "DocumentLink": "",
|
||
// "SizeInkB": "0.00000000000000"
|
||
// },
|
||
// {
|
||
// "UUID": "FA163E87-49F0-1FD0-A0C8-22793B4E5F77",
|
||
// "ObjectID": "FA163E8749F01FD0A0C822793B4E5F77",
|
||
// "ParentObjectID": "FA163E8749F01FD0A0C822793B4EDF77",
|
||
// "FileName": "Proof of Purchase",
|
||
// "MimeType": "application/octet-stream",
|
||
// "CategoryCode": "3",
|
||
// "LinkWebURI": "https://regentrv.my.salesforce.com/sfc/p/2t000000cc9V/a/W2000002EZ4n/ppCBH.gcnV2K.3qCA_U9ZDtZ9r_UffyJGEuteuWMFAE",
|
||
// "DocumentLink": "",
|
||
// "SizeInkB": "0.00000000000000"
|
||
// },
|
||
// {
|
||
// "UUID": "FA163E87-49F0-1FD0-A0C8-22793B4E7F77",
|
||
// "ObjectID": "FA163E8749F01FD0A0C822793B4E7F77",
|
||
// "ParentObjectID": "FA163E8749F01FD0A0C822793B4EDF77",
|
||
// "FileName": "Chassis Number",
|
||
// "MimeType": "application/octet-stream",
|
||
// "CategoryCode": "3",
|
||
// "LinkWebURI": "https://regentrv.my.salesforce.com/sfc/p/2t000000cc9V/a/W2000002EZ6P/mDuF2NKyP96SGjWzuxXl7eejFb84ORKtcG0bPUAqrsg",
|
||
// "DocumentLink": "",
|
||
// "SizeInkB": "0.00000000000000"
|
||
// }
|
||
// ],
|
||
// "issueItems": [
|
||
// {
|
||
// "objectId": "FA163E8749F01FD0A0C822793B4F1F77",
|
||
// "uuid": "FA163E87-49F0-1FD0-A0C8-228700443F77",
|
||
// "description": "Camera not paring. tried 4 times not working",
|
||
// "attachments": [
|
||
// {
|
||
// "UUID": "FA163E82-A315-1FD0-A2C0-35B6AE95B9BA",
|
||
// "ObjectID": "FA163E82A3151FD0A2C035B6AE95B9BA",
|
||
// "ParentObjectID": "FA163E8749F01FD0A0C8228700449F77",
|
||
// "FileName": "IMG_4307.mp4",
|
||
// "MimeType": "video/mp4",
|
||
// "CategoryCode": "2",
|
||
// "LinkWebURI": "",
|
||
// "DocumentLink": "https://my300375.c4c.saphybriscloud.cn/sap/c4c/odata/cust/v1/custticketapi/BO_XSRIssueItemAttachmentCollection('FA163E8749F01FD0A0C8228700449F77')/BO_XSRIssueItemAttachmentFolder('FA163E82A3151FD0A2C035B6AE95B9BA')/Binary/$value",
|
||
// "SizeInkB": "2997017.00000000000000"
|
||
// },
|
||
// {
|
||
// "UUID": "FA163E87-49F0-1FD0-A0C8-22870048FF77",
|
||
// "ObjectID": "FA163E8749F01FD0A0C822870048FF77",
|
||
// "ParentObjectID": "FA163E8749F01FD0A0C8228700449F77",
|
||
// "FileName": "PD Camera video.docx",
|
||
// "MimeType": "application/octet-stream",
|
||
// "CategoryCode": "3",
|
||
// "LinkWebURI": "https://regentrv.my.salesforce.com/sfc/p/2t000000cc9V/a/W2000002EWRu/v46Grw_mnbsjKrU1LlWrN7Ysh9vT5iOydQbr0nSYsxw",
|
||
// "DocumentLink": "",
|
||
// "SizeInkB": "0.00000000000000"
|
||
// }
|
||
// ]
|
||
// }
|
||
// ],
|
||
// "downloadedFiles": [
|
||
// {
|
||
// "source": "SR",
|
||
// "c4cName": "Quotation",
|
||
// "type": "link",
|
||
// "linkUrl": "https://regentrv.my.salesforce.com/sfc/p/2t000000cc9V/a/W2000002EZ3B/4JQvOAb263K5kCW8sIAbYT.nFMeW8z7hT9QS4h0P9Mg",
|
||
// "savedPath": "/tmp/att/Warranty Quoting PD.xlsx",
|
||
// "savedName": "Warranty Quoting PD.xlsx"
|
||
// },
|
||
// {
|
||
// "source": "SR",
|
||
// "c4cName": "Proof of Purchase",
|
||
// "type": "link",
|
||
// "linkUrl": "https://regentrv.my.salesforce.com/sfc/p/2t000000cc9V/a/W2000002EZ4n/ppCBH.gcnV2K.3qCA_U9ZDtZ9r_UffyJGEuteuWMFAE",
|
||
// "savedPath": "/tmp/att/PD VIN.jpg",
|
||
// "savedName": "PD VIN.jpg"
|
||
// },
|
||
// {
|
||
// "source": "SR",
|
||
// "c4cName": "Chassis Number",
|
||
// "type": "link",
|
||
// "linkUrl": "https://regentrv.my.salesforce.com/sfc/p/2t000000cc9V/a/W2000002EZ6P/mDuF2NKyP96SGjWzuxXl7eejFb84ORKtcG0bPUAqrsg",
|
||
// "savedPath": "/tmp/att/PD Chassis.jpg",
|
||
// "savedName": "PD Chassis.jpg"
|
||
// },
|
||
// {
|
||
// "source": "IssueItem-FA163E8749F0",
|
||
// "c4cName": "PD Camera video.docx",
|
||
// "type": "link",
|
||
// "linkUrl": "https://regentrv.my.salesforce.com/sfc/p/2t000000cc9V/a/W2000002EWRu/v46Grw_mnbsjKrU1LlWrN7Ysh9vT5iOydQbr0nSYsxw",
|
||
// "savedPath": "/tmp/att/PD Camera video.docx",
|
||
// "savedName": "PD Camera video.docx"
|
||
// },
|
||
// {
|
||
// "source": "IssueItem-FA163E8749F0",
|
||
// "c4cName": "IMG_4307.mp4",
|
||
// "type": "file",
|
||
// "mime": "video/mp4",
|
||
// "savedPath": "/tmp/att/IMG_4307.mp4",
|
||
// "savedName": "IMG_4307.mp4"
|
||
// }
|
||
// ],
|
||
// "srObjectId": "FA163E8749F01FD0A0C822793B4EDF77"
|
||
// }
|
||
public class C4CAttachmentDownloader {
|
||
|
||
private final String pythonCmd;
|
||
private final String scriptPath;
|
||
private final String tenant;
|
||
private final String username;
|
||
private final String password;
|
||
private final long timeoutMinutes;
|
||
|
||
// 群晖 DSM 配置(可选)
|
||
private String dsmUrl;
|
||
private String dsmUser;
|
||
private String dsmPassword;
|
||
private String dsmPath;
|
||
|
||
/**
|
||
* @param scriptPath Python 脚本的绝对路径
|
||
* @param tenant C4C 租户地址 (如 https://xxx.c4c.saphybriscloud.cn)
|
||
* @param username C4C 用户名
|
||
* @param password C4C 密码
|
||
*/
|
||
public C4CAttachmentDownloader(String scriptPath, String tenant, String username, String password) {
|
||
this(scriptPath, tenant, username, password, "python3", 30);
|
||
}
|
||
|
||
/**
|
||
* @param scriptPath Python 脚本的绝对路径
|
||
* @param tenant C4C 租户地址
|
||
* @param username C4C 用户名
|
||
* @param password C4C 密码
|
||
* @param pythonCmd Python 命令 (python3 / python)
|
||
* @param timeoutMinutes 超时时间(分钟)
|
||
*/
|
||
public C4CAttachmentDownloader(String scriptPath, String tenant, String username, String password,
|
||
String pythonCmd, long timeoutMinutes) {
|
||
this.scriptPath = scriptPath;
|
||
this.tenant = tenant;
|
||
this.username = username;
|
||
this.password = password;
|
||
this.pythonCmd = pythonCmd;
|
||
this.timeoutMinutes = timeoutMinutes;
|
||
}
|
||
|
||
/**
|
||
* 设置群晖 DSM 上传配置(可选)。设置后,download() 会在下载完成后自动上传到 DSM。
|
||
*
|
||
* @param dsmUrl DSM 地址 (如 http://10.0.10.235:5000)
|
||
* @param dsmUser DSM 用户名
|
||
* @param dsmPassword DSM 密码
|
||
* @param dsmPath DSM 目标路径 (如 /Newgonow/AU-SPFJ)
|
||
*/
|
||
public void setDsmConfig(String dsmUrl, String dsmUser, String dsmPassword, String dsmPath) {
|
||
this.dsmUrl = dsmUrl;
|
||
this.dsmUser = dsmUser;
|
||
this.dsmPassword = dsmPassword;
|
||
this.dsmPath = dsmPath;
|
||
}
|
||
|
||
/**
|
||
* 仅列出附件清单,不下载
|
||
*/
|
||
public Result listAttachments(String ticketId) throws Exception {
|
||
return execute(ticketId, null, true);
|
||
}
|
||
|
||
/**
|
||
* 下载全部附件到默认 downloads 目录
|
||
*/
|
||
public Result download(String ticketId) throws Exception {
|
||
return execute(ticketId, null, false);
|
||
}
|
||
|
||
/**
|
||
* 下载全部附件到指定目录
|
||
*/
|
||
public Result download(String ticketId, String outputDir) throws Exception {
|
||
return execute(ticketId, outputDir, false);
|
||
}
|
||
|
||
private Result execute(String ticketId, String outputDir, boolean listOnly) throws Exception {
|
||
List<String> cmd = new ArrayList<>();
|
||
cmd.add(pythonCmd);
|
||
cmd.add(scriptPath);
|
||
cmd.add("--ticket");
|
||
cmd.add(ticketId);
|
||
cmd.add("--json");
|
||
|
||
if (outputDir != null && !outputDir.isEmpty()) {
|
||
cmd.add("--output-dir");
|
||
cmd.add(outputDir);
|
||
}
|
||
if (listOnly) {
|
||
cmd.add("--list-only");
|
||
}
|
||
|
||
ProcessBuilder pb = new ProcessBuilder(cmd);
|
||
// 凭证通过环境变量传递,比命令行参数更安全(不会出现在 ps 进程列表中)
|
||
pb.environment().put("C4C_TENANT", tenant);
|
||
pb.environment().put("C4C_USERNAME", username);
|
||
pb.environment().put("C4C_PASSWORD", password);
|
||
|
||
// DSM 配置通过环境变量传递
|
||
if (dsmUrl != null && !dsmUrl.isEmpty()) {
|
||
pb.environment().put("DSM_URL", dsmUrl);
|
||
pb.environment().put("DSM_USERNAME", dsmUser != null ? dsmUser : "");
|
||
pb.environment().put("DSM_PASSWORD", dsmPassword != null ? dsmPassword : "");
|
||
pb.environment().put("DSM_PATH", dsmPath != null ? dsmPath : "");
|
||
}
|
||
pb.redirectErrorStream(false);
|
||
Process process = pb.start();
|
||
|
||
String stdout;
|
||
String stderr;
|
||
try (
|
||
InputStream is = process.getInputStream();
|
||
InputStream es = process.getErrorStream()
|
||
) {
|
||
stdout = new String(is.readAllBytes(), StandardCharsets.UTF_8);
|
||
stderr = new String(es.readAllBytes(), StandardCharsets.UTF_8);
|
||
}
|
||
|
||
boolean finished = process.waitFor(timeoutMinutes, TimeUnit.MINUTES);
|
||
if (!finished) {
|
||
process.destroyForcibly();
|
||
throw new RuntimeException("Python 脚本执行超时 (" + timeoutMinutes + " 分钟)");
|
||
}
|
||
|
||
int exitCode = process.exitValue();
|
||
if (stdout.isBlank()) {
|
||
throw new RuntimeException("Python 脚本无输出, exitCode=" + exitCode + ", stderr=" + stderr);
|
||
}
|
||
|
||
ObjectMapper mapper = new ObjectMapper();
|
||
JsonNode root = mapper.readTree(stdout);
|
||
|
||
Result result = new Result();
|
||
result.setTicketId(root.path("ticketId").asText());
|
||
result.setOutputDir(root.path("outputDir").asText());
|
||
result.setSuccess(root.path("success").asBoolean());
|
||
result.setError(root.path("error").asText(null));
|
||
result.setSrObjectId(root.path("srObjectId").asText(null));
|
||
|
||
// SR 附件
|
||
for (JsonNode att : root.path("srAttachments")) {
|
||
result.getSrAttachments().add(parseAttachment(att));
|
||
}
|
||
|
||
// XIssueItem 附件
|
||
for (JsonNode item : root.path("issueItems")) {
|
||
IssueItem ii = new IssueItem();
|
||
ii.setObjectId(item.path("objectId").asText());
|
||
ii.setUuid(item.path("uuid").asText());
|
||
ii.setDescription(item.path("description").asText());
|
||
for (JsonNode att : item.path("attachments")) {
|
||
ii.getAttachments().add(parseAttachment(att));
|
||
}
|
||
result.getIssueItems().add(ii);
|
||
}
|
||
|
||
// 已下载文件
|
||
for (JsonNode df : root.path("downloadedFiles")) {
|
||
DownloadedFile f = new DownloadedFile();
|
||
f.setSource(df.path("source").asText());
|
||
f.setC4cName(df.path("c4cName").asText());
|
||
f.setType(df.path("type").asText());
|
||
f.setMime(df.path("mime").asText(null));
|
||
f.setLinkUrl(df.path("linkUrl").asText(null));
|
||
f.setSavedPath(df.path("savedPath").asText(null));
|
||
f.setSavedName(df.path("savedName").asText(null));
|
||
f.setError(df.path("error").asText(null));
|
||
result.getDownloadedFiles().add(f);
|
||
}
|
||
|
||
// DSM 上传结果
|
||
for (JsonNode du : root.path("dsmUpload")) {
|
||
DsmUploadEntry entry = new DsmUploadEntry();
|
||
entry.setFile(du.path("file").asText());
|
||
entry.setRemotePath(du.path("remotePath").asText());
|
||
entry.setSuccess(du.path("success").asBoolean(false));
|
||
entry.setError(du.path("error").asText(null));
|
||
result.getDsmUpload().add(entry);
|
||
}
|
||
|
||
return result;
|
||
}
|
||
|
||
private Attachment parseAttachment(JsonNode node) {
|
||
Attachment a = new Attachment();
|
||
a.setUuid(node.path("UUID").asText());
|
||
a.setObjectId(node.path("ObjectID").asText());
|
||
a.setFileName(node.path("FileName").asText());
|
||
a.setMimeType(node.path("MimeType").asText());
|
||
a.setCategoryCode(node.path("CategoryCode").asText());
|
||
a.setLinkWebURI(node.path("LinkWebURI").asText(null));
|
||
a.setDocumentLink(node.path("DocumentLink").asText(null));
|
||
String size = node.path("SizeInkB").asText(null);
|
||
if (size != null && !size.isEmpty()) {
|
||
try { a.setSizeInKB(Double.parseDouble(size)); } catch (NumberFormatException ignored) {}
|
||
}
|
||
return a;
|
||
}
|
||
|
||
// ==================== 数据模型 ====================
|
||
|
||
public static class Result {
|
||
private String ticketId;
|
||
private String outputDir;
|
||
private boolean success;
|
||
private String error;
|
||
private String srObjectId;
|
||
private List<Attachment> srAttachments = new ArrayList<>();
|
||
private List<IssueItem> issueItems = new ArrayList<>();
|
||
private List<DownloadedFile> downloadedFiles = new ArrayList<>();
|
||
private List<DsmUploadEntry> dsmUpload = new ArrayList<>();
|
||
|
||
public String getTicketId() { return ticketId; }
|
||
public void setTicketId(String ticketId) { this.ticketId = ticketId; }
|
||
public String getOutputDir() { return outputDir; }
|
||
public void setOutputDir(String outputDir) { this.outputDir = outputDir; }
|
||
public boolean isSuccess() { return success; }
|
||
public void setSuccess(boolean success) { this.success = success; }
|
||
public String getError() { return error; }
|
||
public void setError(String error) { this.error = error; }
|
||
public String getSrObjectId() { return srObjectId; }
|
||
public void setSrObjectId(String srObjectId) { this.srObjectId = srObjectId; }
|
||
public List<Attachment> getSrAttachments() { return srAttachments; }
|
||
public void setSrAttachments(List<Attachment> srAttachments) { this.srAttachments = srAttachments; }
|
||
public List<IssueItem> getIssueItems() { return issueItems; }
|
||
public void setIssueItems(List<IssueItem> issueItems) { this.issueItems = issueItems; }
|
||
public List<DownloadedFile> getDownloadedFiles() { return downloadedFiles; }
|
||
public void setDownloadedFiles(List<DownloadedFile> downloadedFiles) { this.downloadedFiles = downloadedFiles; }
|
||
public List<DsmUploadEntry> getDsmUpload() { return dsmUpload; }
|
||
public void setDsmUpload(List<DsmUploadEntry> dsmUpload) { this.dsmUpload = dsmUpload; }
|
||
|
||
/** 获取全部附件(SR + 所有 IssueItem)的合并列表 */
|
||
public List<Attachment> getAllAttachments() {
|
||
List<Attachment> all = new ArrayList<>(srAttachments);
|
||
for (IssueItem ii : issueItems) {
|
||
all.addAll(ii.getAttachments());
|
||
}
|
||
return all;
|
||
}
|
||
}
|
||
|
||
public static class Attachment {
|
||
private String uuid;
|
||
private String objectId;
|
||
private String fileName;
|
||
private String mimeType;
|
||
private String categoryCode; // "2"=文件, "3"=链接
|
||
private String linkWebURI;
|
||
private String documentLink;
|
||
private double sizeInKB;
|
||
|
||
public boolean isFile() { return "2".equals(categoryCode); }
|
||
public boolean isLink() { return "3".equals(categoryCode); }
|
||
|
||
public String getUuid() { return uuid; }
|
||
public void setUuid(String uuid) { this.uuid = uuid; }
|
||
public String getObjectId() { return objectId; }
|
||
public void setObjectId(String objectId) { this.objectId = objectId; }
|
||
public String getFileName() { return fileName; }
|
||
public void setFileName(String fileName) { this.fileName = fileName; }
|
||
public String getMimeType() { return mimeType; }
|
||
public void setMimeType(String mimeType) { this.mimeType = mimeType; }
|
||
public String getCategoryCode() { return categoryCode; }
|
||
public void setCategoryCode(String categoryCode) { this.categoryCode = categoryCode; }
|
||
public String getLinkWebURI() { return linkWebURI; }
|
||
public void setLinkWebURI(String linkWebURI) { this.linkWebURI = linkWebURI; }
|
||
public String getDocumentLink() { return documentLink; }
|
||
public void setDocumentLink(String documentLink) { this.documentLink = documentLink; }
|
||
public double getSizeInKB() { return sizeInKB; }
|
||
public void setSizeInKB(double sizeInKB) { this.sizeInKB = sizeInKB; }
|
||
}
|
||
|
||
public static class IssueItem {
|
||
private String objectId;
|
||
private String uuid;
|
||
private String description;
|
||
private List<Attachment> attachments = new ArrayList<>();
|
||
|
||
public String getObjectId() { return objectId; }
|
||
public void setObjectId(String objectId) { this.objectId = objectId; }
|
||
public String getUuid() { return uuid; }
|
||
public void setUuid(String uuid) { this.uuid = uuid; }
|
||
public String getDescription() { return description; }
|
||
public void setDescription(String description) { this.description = description; }
|
||
public List<Attachment> getAttachments() { return attachments; }
|
||
public void setAttachments(List<Attachment> attachments) { this.attachments = attachments; }
|
||
}
|
||
|
||
public static class DownloadedFile {
|
||
private String source; // "SR" / "IssueItem-xxx"
|
||
private String c4cName; // C4C 里的附件名
|
||
private String type; // "file" / "link"
|
||
private String mime;
|
||
private String linkUrl;
|
||
private String savedPath; // 下载后的绝对路径
|
||
private String savedName; // 下载后的文件名
|
||
private String error; // 下载失败的错误信息
|
||
|
||
public boolean isOk() { return savedPath != null && error == null; }
|
||
|
||
public String getSource() { return source; }
|
||
public void setSource(String source) { this.source = source; }
|
||
public String getC4cName() { return c4cName; }
|
||
public void setC4cName(String c4cName) { this.c4cName = c4cName; }
|
||
public String getType() { return type; }
|
||
public void setType(String type) { this.type = type; }
|
||
public String getMime() { return mime; }
|
||
public void setMime(String mime) { this.mime = mime; }
|
||
public String getLinkUrl() { return linkUrl; }
|
||
public void setLinkUrl(String linkUrl) { this.linkUrl = linkUrl; }
|
||
public String getSavedPath() { return savedPath; }
|
||
public void setSavedPath(String savedPath) { this.savedPath = savedPath; }
|
||
public String getSavedName() { return savedName; }
|
||
public void setSavedName(String savedName) { this.savedName = savedName; }
|
||
public String getError() { return error; }
|
||
public void setError(String error) { this.error = error; }
|
||
}
|
||
|
||
public static class DsmUploadEntry {
|
||
private String file; // 文件名
|
||
private String remotePath; // DSM 远程路径
|
||
private boolean success;
|
||
private String error;
|
||
|
||
public boolean isSuccess() { return success; }
|
||
|
||
public String getFile() { return file; }
|
||
public void setFile(String file) { this.file = file; }
|
||
public String getRemotePath() { return remotePath; }
|
||
public void setRemotePath(String remotePath) { this.remotePath = remotePath; }
|
||
public void setSuccess(boolean success) { this.success = success; }
|
||
public String getError() { return error; }
|
||
public void setError(String error) { this.error = error; }
|
||
}
|
||
}
|