diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/applydata/ContentValue.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/applydata/ContentValue.java index 92ec8a43e8..c75ef9292a 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/applydata/ContentValue.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/applydata/ContentValue.java @@ -6,6 +6,8 @@ import java.io.Serializable; import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.Arrays; import java.util.List; /** @@ -126,6 +128,74 @@ public static class NewTips implements Serializable { @SerializedName("tips_content") private List tipsContent; + /** + * Creates a simple Tips control with mixed plain text and clickable links. + * + * @param lang the language code (e.g., "zh_CN") + * @param textAndLinks array of objects where strings become plain text and Link objects become clickable links + * @return NewTips instance with the specified content + */ + public static NewTips of(String lang, Object... textAndLinks) { + NewTips tips = new NewTips(); + TipsContent content = new TipsContent(); + TipsContent.Text text = new TipsContent.Text(); + + List subTexts = new ArrayList<>(); + + for (Object item : textAndLinks) { + TipsContent.SubText subText = new TipsContent.SubText(); + TipsContent.SubText.Content subContent = new TipsContent.SubText.Content(); + + if (item instanceof String) { + // Plain text + TipsContent.SubText.Content.PlainText plainText = new TipsContent.SubText.Content.PlainText(); + plainText.setContent((String) item); + subContent.setPlainText(plainText); + subText.setType(1); + } else if (item instanceof TipsContent.SubText.Content.Link) { + // Link + subContent.setLink((TipsContent.SubText.Content.Link) item); + subText.setType(2); + } + + subText.setContent(subContent); + subTexts.add(subText); + } + + text.setSubText(subTexts); + content.setText(text); + content.setLang(lang); + tips.setTipsContent(Arrays.asList(content)); + + return tips; + } + + /** + * Creates a simple Tips control with only plain text. + * + * @param lang the language code (e.g., "zh_CN") + * @param textContent the plain text content + * @return NewTips instance with plain text content + */ + public static NewTips ofText(String lang, String textContent) { + return of(lang, textContent); + } + + /** + * Creates a Tips control with a single clickable link. + * + * @param lang the language code (e.g., "zh_CN") + * @param linkTitle the display text for the link + * @param linkUrl the URL to link to + * @return NewTips instance with a single clickable link + */ + public static NewTips ofLink(String lang, String linkTitle, String linkUrl) { + TipsContent.SubText.Content.Link link = new TipsContent.SubText.Content.Link(); + link.setTitle(linkTitle); + link.setUrl(linkUrl); + return of(lang, link); + } + /** * The type tips_content. */ diff --git a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/bean/oa/applydata/ContentValueTipsTest.java b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/bean/oa/applydata/ContentValueTipsTest.java new file mode 100644 index 0000000000..739596fd52 --- /dev/null +++ b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/bean/oa/applydata/ContentValueTipsTest.java @@ -0,0 +1,124 @@ +package me.chanjar.weixin.cp.bean.oa.applydata; + +import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; +import org.testng.annotations.Test; + +import java.util.Arrays; + +import static org.testng.Assert.*; + +public class ContentValueTipsTest { + + @Test + public void testTipsWithLinksManualCreation() { + System.out.println("Testing ContentValue.NewTips structure with Link (manual creation):"); + + // Create a Tips structure with both plain text and link + ContentValue.NewTips tips = new ContentValue.NewTips(); + ContentValue.NewTips.TipsContent tipsContent = new ContentValue.NewTips.TipsContent(); + ContentValue.NewTips.TipsContent.Text text = new ContentValue.NewTips.TipsContent.Text(); + + // Create plain text subtext + ContentValue.NewTips.TipsContent.SubText plainSubText = new ContentValue.NewTips.TipsContent.SubText(); + ContentValue.NewTips.TipsContent.SubText.Content plainContent = new ContentValue.NewTips.TipsContent.SubText.Content(); + ContentValue.NewTips.TipsContent.SubText.Content.PlainText plainTextContent = + new ContentValue.NewTips.TipsContent.SubText.Content.PlainText(); + plainTextContent.setContent("This is plain text. For more info, "); + plainContent.setPlainText(plainTextContent); + plainSubText.setType(1); // Type 1 for plain text + plainSubText.setContent(plainContent); + + // Create link subtext + ContentValue.NewTips.TipsContent.SubText linkSubText = new ContentValue.NewTips.TipsContent.SubText(); + ContentValue.NewTips.TipsContent.SubText.Content linkContent = new ContentValue.NewTips.TipsContent.SubText.Content(); + ContentValue.NewTips.TipsContent.SubText.Content.Link link = + new ContentValue.NewTips.TipsContent.SubText.Content.Link(); + link.setTitle("click here"); + link.setUrl("https://work.weixin.qq.com"); + linkContent.setLink(link); + linkSubText.setType(2); // Type 2 for link + linkSubText.setContent(linkContent); + + text.setSubText(Arrays.asList(plainSubText, linkSubText)); + tipsContent.setText(text); + tipsContent.setLang("zh_CN"); + + tips.setTipsContent(Arrays.asList(tipsContent)); + + // Convert to JSON + String json = WxCpGsonBuilder.create().toJson(tips); + System.out.println("Generated JSON:"); + System.out.println(json); + + // Try to parse it back + validateTipsStructure(tips, json); + } + + @Test + public void testTipsWithConvenienceMethods() { + System.out.println("Testing ContentValue.NewTips with convenience methods:"); + + // Test 1: Simple plain text + ContentValue.NewTips textOnly = ContentValue.NewTips.ofText("zh_CN", "This is a simple text tip."); + String textJson = WxCpGsonBuilder.create().toJson(textOnly); + System.out.println("Text-only JSON: " + textJson); + validateTipsStructure(textOnly, textJson); + + // Test 2: Single link + ContentValue.NewTips linkOnly = ContentValue.NewTips.ofLink("zh_CN", "Visit WeChat Work", "https://work.weixin.qq.com"); + String linkJson = WxCpGsonBuilder.create().toJson(linkOnly); + System.out.println("Link-only JSON: " + linkJson); + validateTipsStructure(linkOnly, linkJson); + + // Test 3: Mixed content using convenience method + ContentValue.NewTips.TipsContent.SubText.Content.Link link = + new ContentValue.NewTips.TipsContent.SubText.Content.Link(); + link.setTitle("click here"); + link.setUrl("https://work.weixin.qq.com"); + + ContentValue.NewTips mixed = ContentValue.NewTips.of("zh_CN", + "For more information, ", link, " or contact support."); + String mixedJson = WxCpGsonBuilder.create().toJson(mixed); + System.out.println("Mixed content JSON: " + mixedJson); + validateTipsStructure(mixed, mixedJson); + + System.out.println("All convenience method tests passed!"); + } + + private void validateTipsStructure(ContentValue.NewTips tips, String json) { + try { + ContentValue.NewTips parsedTips = WxCpGsonBuilder.create().fromJson(json, ContentValue.NewTips.class); + assertNotNull(parsedTips); + assertNotNull(parsedTips.getTipsContent()); + assertFalse(parsedTips.getTipsContent().isEmpty()); + + ContentValue.NewTips.TipsContent.Text parsedText = parsedTips.getTipsContent().get(0).getText(); + assertNotNull(parsedText); + assertNotNull(parsedText.getSubText()); + assertTrue(parsedText.getSubText().size() > 0); + + // Verify structure based on content + for (ContentValue.NewTips.TipsContent.SubText subText : parsedText.getSubText()) { + assertNotNull(subText.getType()); + assertNotNull(subText.getContent()); + + if (subText.getType() == 1) { + // Plain text + assertNotNull(subText.getContent().getPlainText()); + assertNotNull(subText.getContent().getPlainText().getContent()); + } else if (subText.getType() == 2) { + // Link + assertNotNull(subText.getContent().getLink()); + assertNotNull(subText.getContent().getLink().getTitle()); + assertNotNull(subText.getContent().getLink().getUrl()); + } + } + + System.out.println("✓ JSON parsing and validation successful"); + } catch (Exception e) { + System.out.println("✗ Error parsing: " + e.getMessage()); + e.printStackTrace(); + fail("Failed to parse JSON: " + e.getMessage()); + } + } +} \ No newline at end of file diff --git a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/bean/oa/applydata/TipsUsageExample.java b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/bean/oa/applydata/TipsUsageExample.java new file mode 100644 index 0000000000..27759a0e62 --- /dev/null +++ b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/bean/oa/applydata/TipsUsageExample.java @@ -0,0 +1,93 @@ +package me.chanjar.weixin.cp.bean.oa.applydata; + +import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; + +/** + * Usage examples for ContentValue.NewTips with clickable link support. + * + * This example demonstrates how to create Tips controls that can render URLs as clickable links, + * addressing the issue where "Tips控件无法将url渲染为可点击的链接". + * + * @author WxJava Community + */ +public class TipsUsageExample { + + public static void main(String[] args) { + demonstrateBasicUsage(); + demonstrateAdvancedUsage(); + } + + /** + * Basic usage examples for creating Tips with clickable links. + */ + public static void demonstrateBasicUsage() { + System.out.println("=== Basic Tips Usage Examples ===\n"); + + // Example 1: Simple plain text tip + ContentValue.NewTips textTip = ContentValue.NewTips.ofText("zh_CN", + "这是一个简单的文本提示。"); + System.out.println("1. Plain text tip JSON:"); + System.out.println(WxCpGsonBuilder.create().toJson(textTip)); + System.out.println(); + + // Example 2: Simple clickable link tip + ContentValue.NewTips linkTip = ContentValue.NewTips.ofLink("zh_CN", + "访问企业微信官网", "https://work.weixin.qq.com"); + System.out.println("2. Single link tip JSON:"); + System.out.println(WxCpGsonBuilder.create().toJson(linkTip)); + System.out.println(); + + // Example 3: Mixed content - text with clickable link + ContentValue.NewTips.TipsContent.SubText.Content.Link helpLink = + new ContentValue.NewTips.TipsContent.SubText.Content.Link(); + helpLink.setTitle("点击查看详情"); + helpLink.setUrl("https://work.weixin.qq.com/help"); + + ContentValue.NewTips mixedTip = ContentValue.NewTips.of("zh_CN", + "如需了解更多信息,请", helpLink, "。"); + System.out.println("3. Mixed content tip JSON:"); + System.out.println(WxCpGsonBuilder.create().toJson(mixedTip)); + System.out.println(); + } + + /** + * Advanced usage examples showing complex Tips with multiple links and text. + */ + public static void demonstrateAdvancedUsage() { + System.out.println("=== Advanced Tips Usage Examples ===\n"); + + // Example 4: Complex tip with multiple links + ContentValue.NewTips.TipsContent.SubText.Content.Link docsLink = + new ContentValue.NewTips.TipsContent.SubText.Content.Link(); + docsLink.setTitle("开发文档"); + docsLink.setUrl("https://developer.work.weixin.qq.com"); + + ContentValue.NewTips.TipsContent.SubText.Content.Link supportLink = + new ContentValue.NewTips.TipsContent.SubText.Content.Link(); + supportLink.setTitle("技术支持"); + supportLink.setUrl("https://work.weixin.qq.com/contact"); + + ContentValue.NewTips complexTip = ContentValue.NewTips.of("zh_CN", + "审批流程说明:\n1. 提交申请后系统将自动处理\n2. 如有疑问请查看", + docsLink, + "或联系", + supportLink); + System.out.println("4. Complex tip with multiple links JSON:"); + System.out.println(WxCpGsonBuilder.create().toJson(complexTip)); + System.out.println(); + + // Demonstrate that the structure supports proper type differentiation + System.out.println("=== Type Verification ==="); + ContentValue.NewTips parsed = WxCpGsonBuilder.create().fromJson( + WxCpGsonBuilder.create().toJson(complexTip), ContentValue.NewTips.class); + + parsed.getTipsContent().get(0).getText().getSubText().forEach(subText -> { + if (subText.getType() == 1) { + System.out.println("Plain text: \"" + subText.getContent().getPlainText().getContent() + "\""); + } else if (subText.getType() == 2) { + System.out.println("Link: \"" + subText.getContent().getLink().getTitle() + + "\" -> " + subText.getContent().getLink().getUrl()); + } + }); + } +} \ No newline at end of file