Java系统对接企业微信审批项目流程
若依做的一个系统需求需要对接企业微信的人员去审核订单 回款之类,以下是详细步骤.
1.首先登入企业微信管理后台:
企业微信
2.找到应用管理
3.自建一个应用
4.这些数据都可以拿到
5.配置可信Ip
6.进入有两种方法让你去配置 ,第一种用公司的域名,然后下面有个文件需要导入,必须导入,不然不会成功的。导入之后配置自己电脑的IP公网地址。
7.配置回调的url接口
踩雷:::框架用的若依,这个接口必须开放,返回的也是字符串格式不能是json格式。
这里需要写两个接口 一个是get 一个是post。
8.创建自己的模板
9.模板显示范围和自建的应用必须关联
10.开发前必读 - 文档 - 企业微信开发者中心进入这个里面查看具体的教程
11.附所有的代码
wechat:
# 企业ID
corpId: wwd
# 审批应用ID
agentId: 100
# 审批应用Secret
corpSecret: 5vE
# 回调校验的Token
sToken: 2a
# 回调校验的Key
encodingAesKey: 5JH4nvyvb1
# tokenURL
tokenUrl: https://qyapi.weixin.qq.com/cgi-bin/gettoken
# 审核申请URL
approvalUrl: https://qyapi.weixin.qq.com/cgi-bin/oa/applyevent
# 获取模板详情URL
templateUrl: https://qyapi.weixin.qq.com/cgi-bin/oa/gettemplatedetail
# 上传文件到企业微信URL
uploadFileUrl: https://qyapi.weixin.qq.com/cgi-bin/media/upload
# 获取用户信息url
getUserUrl: https://qyapi.weixin.qq.com/cgi-bin/user/get
# 添加实验区审批流模版ID
areaTemplateId: 3WMV
package com.ruoyi.bingo.config.wechat;
import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
/**
* @author yj
*/
@Data
@Component
public class WeChatConfig {
@Value("${wechat.corpId}")
private String corpId;
@Value("${wechat.agentId}")
private String agentId;
@Value("${wechat.corpSecret}")
private String corpSecret;
@Value("${wechat.sToken}")
private String sToken;
@Value("${wechat.encodingAesKey}")
private String encodingAesKey;
@Value("${wechat.tokenUrl}")
private String tokenUrl;
@Value("${wechat.templateUrl}")
private String templateUrl;
@Value("${wechat.approvalUrl}")
private String approvalUrl;
@Value("${wechat.uploadFileUrl}")
private String uploadFileUrl;
@Value("${wechat.getUserUrl}")
private String getUserUrl;
@Value("${wechat.areaTemplateId}")
private String areaTemplateId;
@Value("${wechat.schoolTemplateId}")
private String schoolTemplateId;
@Value("${wechat.clientTemplateId}")
private String clientTemplateId;
@Value("${wechat.createOrderTemplateId}")
private String createOrderTemplateId;
@Value("${wechat.costTemplateId}")
private String costTemplateId;
@Value("${wechat.returnedTemplateId}")
private String returnedTemplateId;
@Value("${wechat.contractTemplateId}")
private String contractTemplateId;
@Value("${wechat.finishOrderTemplateId}")
private String finishOrderTemplateId;
// 提供公开的 Getter 方法
public String getCorpId() {
return corpId;
}
public String getAgentId() {
return agentId;
}
public String getCorpSecret() {
return corpSecret;
}
public String getSToken() {
return sToken;
}
public String getEncodingAesKey() {
return encodingAesKey;
}
public String getTokenUrl() {
return tokenUrl;
}
public String getTemplateUrl() {
return templateUrl;
}
public String getApprovalUrl() {
return approvalUrl;
}
public String getUploadFileUrl() {
return uploadFileUrl;
}
public String getGetUserUrl() {
return getUserUrl;
}
public String getAreaTemplateId() {
return areaTemplateId;
}
public String getSchoolTemplateId() {
return schoolTemplateId;
}
public String getClientTemplateId() {
return clientTemplateId;
}
public String getCreateOrderTemplateId() {
return createOrderTemplateId;
}
public String getCostTemplateId() {
return costTemplateId;
}
public String getReturnedTemplateId() {
return returnedTemplateId;
}
public String getContractTemplateId() {
return contractTemplateId;
}
public String getFinishOrderTemplateId() {
return finishOrderTemplateId;
}
}
12.加密解密XML工具栏
package com.ruoyi.bingo.config.wechat.xml;
import com.ruoyi.common.utils.bean.BeanUtils;
import org.springframework.stereotype.Component;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.xml.sax.InputSource;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import java.util.ArrayList;
import java.util.List;
/**
* Description :
*
* @author YJ
* date 2024-12-15
* time 20:28
*/
@Component
public class XmlUtils {
/**
* 解析xmL
*
* @param xmlData
*/
public ApprovalBack analysis(String xmlData) {
try {
// 调用 fromXml 方法解析 XML 数据
ApprovalCallback callback = ApprovalCallback.fromXml(xmlData);
// 输出解析结果
System.out.println("ToUserName: " + callback.toUserName);
System.out.println("FromUserName: " + callback.fromUserName);
System.out.println("CreateTime: " + callback.createTime);
System.out.println("MsgType: " + callback.msgType);
System.out.println("Event: " + callback.event);
System.out.println("AgentID: " + callback.agentID);
System.out.println("SpNo: " + callback.spNo);
System.out.println("SpName: " + callback.spName);
System.out.println("SpStatus: " + callback.spStatus);
System.out.println("TemplateId: " + callback.templateId);
System.out.println("ApplyTime: " + callback.applyTime);
//System.out.println("Applyer UserId: " + callback.applyer.userId);
//System.out.println("Applyer Party: " + callback.applyer.party);
//System.out.println("SpRecord SpStatus: " + callback.spRecord.spStatus);
//System.out.println("StatuChangeEvent: " + callback.statuChangeEvent);
//
输出 ProcessList 中的 SubNodeList 信息
//if (callback.processList != null && callback.processList.nodeList != null) {
// for (ApprovalCallback.UserInfo userInfo : callback.processList.nodeList.subNodeList) {
// System.out.println("SubNode UserId: " + userInfo.userId);
// }
//}
System.out.println("解密内容call: " + callback);
ApprovalBack approvalBack = new ApprovalBack();
approvalBack.setSpNo(callback.spNo);
approvalBack.setSpStatus(callback.spStatus);
return approvalBack;
} catch (
Exception e) {
e.printStackTrace();
}
return null;
}
}
class ApprovalCallback {
String toUserName;
String fromUserName;
String createTime;
String msgType;
String event;
String agentID;
String spNo;
String spName;
String spStatus;
String templateId;
String applyTime;
Applyer applyer;
SpRecord spRecord;
String statuChangeEvent;
ProcessList processList;
public static ApprovalCallback fromXml(String xmlContent) throws Exception {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
InputSource inputSource = new InputSource(new java.io.StringReader(xmlContent));
Document doc = builder.parse(inputSource);
ApprovalCallback callback = new ApprovalCallback();
callback.toUserName = doc.getElementsByTagName("ToUserName").item(0).getTextContent();
callback.fromUserName = doc.getElementsByTagName("FromUserName").item(0).getTextContent();
callback.createTime = doc.getElementsByTagName("CreateTime").item(0).getTextContent();
callback.msgType = doc.getElementsByTagName("MsgType").item(0).getTextContent();
callback.event = doc.getElementsByTagName("Event").item(0).getTextContent();
callback.agentID = doc.getElementsByTagName("AgentID").item(0).getTextContent();
// 解析 ApprovalInfo
org.w3c.dom.NodeList approvalInfo = doc.getElementsByTagName("ApprovalInfo");
if (approvalInfo.getLength() > 0) {
Element approvalInfoElement = (Element) approvalInfo.item(0);
callback.spNo = approvalInfoElement.getElementsByTagName("SpNo").item(0).getTextContent();
callback.spName = approvalInfoElement.getElementsByTagName("SpName").item(0).getTextContent();
callback.spStatus = approvalInfoElement.getElementsByTagName("SpStatus").item(0).getTextContent();
callback.templateId = approvalInfoElement.getElementsByTagName("TemplateId").item(0).getTextContent();
callback.applyTime = approvalInfoElement.getElementsByTagName("ApplyTime").item(0).getTextContent();
// Applyer info
Element applyerElement = (Element) approvalInfoElement.getElementsByTagName("Applyer").item(0);
callback.applyer = new Applyer();
callback.applyer.userId = applyerElement.getElementsByTagName("UserId").item(0).getTextContent();
callback.applyer.party = applyerElement.getElementsByTagName("Party").item(0).getTextContent();
// SpRecord info
org.w3c.dom.NodeList spRecordList = approvalInfoElement.getElementsByTagName("SpRecord");
if (spRecordList.getLength() > 0) {
Element spRecordElement = (Element) spRecordList.item(0);
callback.spRecord = new SpRecord();
callback.spRecord.spStatus = spRecordElement.getElementsByTagName("SpStatus").item(0).getTextContent();
callback.spRecord.approverAttr = spRecordElement.getElementsByTagName("ApproverAttr").item(0).getTextContent();
// Details info
org.w3c.dom.NodeList detailsList = spRecordElement.getElementsByTagName("Details");
if (detailsList.getLength() > 0) {
Element detailsElement = (Element) detailsList.item(0);
callback.spRecord.details = new Details();
callback.spRecord.details.userId = detailsElement.getElementsByTagName("UserId").item(0).getTextContent();
callback.spRecord.details.speech = detailsElement.getElementsByTagName("Speech").item(0).getTextContent();
callback.spRecord.details.spStatus = detailsElement.getElementsByTagName("SpStatus").item(0).getTextContent();
}
}
callback.statuChangeEvent = approvalInfoElement.getElementsByTagName("StatuChangeEvent").item(0).getTextContent();
// ProcessList info
org.w3c.dom.NodeList processList = approvalInfoElement.getElementsByTagName("ProcessList");
if (processList.getLength() > 0) {
Element processListElement = (Element) processList.item(0);
callback.processList = new ProcessList();
org.w3c.dom.NodeList nodeList = processListElement.getElementsByTagName("NodeList");
if (nodeList.getLength() > 0) {
Element nodeListElement = (Element) nodeList.item(0);
org.w3c.dom.NodeList subNodeList = nodeListElement.getElementsByTagName("SubNodeList");
callback.processList.nodeList = new ApprovalCallback.NodeList();
callback.processList.nodeList.subNodeList = new ArrayList<>();
for (int i = 0; i < subNodeList.getLength(); i++) {
Element subNodeElement = (Element) subNodeList.item(i);
UserInfo userInfo = new UserInfo();
userInfo.userId = subNodeElement.getElementsByTagName("UserId").item(0).getTextContent();
callback.processList.nodeList.subNodeList.add(userInfo);
}
}
}
}
return callback;
}
public static class Applyer {
private String userId;
private String party;
}
public static class SpRecord {
private String spStatus;
private String approverAttr;
private ApprovalCallback.Details details;
}
public static class Details {
private String userId;
private String speech;
private String spStatus;
}
public static class ProcessList {
private NodeList nodeList;
}
public static class NodeList {
private List<UserInfo> subNodeList;
}
public static class UserInfo {
private String userId;
}
}
package com.ruoyi.bingo.config.wechat.xml;
import lombok.Data;
/**
* Description :
*
* @author YJ
* date 2024-12-15
* time 20:40
*/
@Data
public class ApprovalBack {
private String toUserName;
private String createTime;
private String msgType;
private String event;
private String agentID;
//审批编号
private String spNo;
private String spName;
private String spStatus;
private String templateId;
private String applyTime;
private ApprovalCallback.Applyer applyer;
private ApprovalCallback.SpRecord spRecord;
private String statuChangeEvent;
private ApprovalCallback.ProcessList processList;
}
13.controller层
@Anonymous
@Api(description = "对接企业微信")
@RestController
@RequestMapping("/enterprise/wechat")
public class EnterpriseWeChatController extends BaseController {
@Autowired
private EnterpriseWeChatService weChatService;
/**
* 获取Access Token
*
* @return
*/
@ApiOperation("获取Access Token")
@PreAuthorize("@ss.hasPermi('bingo:wechat:accessToken')")
@PostMapping("/accessToken")
public String getAccessToken() {
return weChatService.getAccessToken();
}
/**
* 验证回调 URL 的有效性
*
* @return
*/
@ApiOperation("验证回调 URL 的有效性")
@GetMapping("/approval/back")
public String approvalBack(@RequestParam("msg_signature") String msgSignature,
@RequestParam("timestamp") String timestamp,
@RequestParam("nonce") String nonce,
@RequestParam("echostr") String ech,
HttpServletResponse response) {
return weChatService.approvalBack(msgSignature, timestamp, nonce, ech, response);
}
/**
* 处理审批回调事件
*/
@PostMapping("/approval/back")
public void handleApprovalCallback(
@RequestParam("msg_signature") String msgSignature,
@RequestParam("timestamp") String timestamp,
@RequestParam("nonce") String nonce,
HttpServletRequest request,
HttpServletResponse response) {
weChatService.handleApprovalCallback(msgSignature, timestamp, nonce, request, response);
}
/**
* 获取企业微信用户信息
*
* @return
*/
@ApiOperation("获取企业微信用户信息")
@PreAuthorize("@ss.hasPermi('bingo:wechat:getUser')")
@PostMapping("/getUser")
public AjaxResult getUser(@RequestBody WechatUserDTO wechatUserDTO) {
return success(weChatService.getUser(wechatUserDTO));
}
/**
* 上传文件到企业微信
*
* @return
*/
@ApiOperation("上传文件到企业微信")
@PreAuthorize("@ss.hasPermi('bingo:wechat:upload')")
@PostMapping("/upload")
public AjaxResult upload(MultipartFile file) {
return success(weChatService.uploadWeChat(file));
}
}
14.server层
/**
* 验证回调 URL 的有效性
*
* @param msgSignature
* @param timestamp
* @param nonce
* @param echostr
* @param response
* @return
*/
@Override
public String approvalBack(String msgSignature, String timestamp, String nonce, String
echostr, HttpServletResponse response) {
try {
log.info("验证回调 URL 的有效性");
return wxBizMsgCrypt.VerifyURL(msgSignature, timestamp, nonce, echostr);
} catch (Exception e) {
e.printStackTrace();
log.error("验证回调 URL 的有效性");
}
return null;
}
/**
* 处理审批回调事件
*
* @param msgSignature
* @param timestamp
* @param nonce
* @param request
* @param response
*/
@Override
public void handleApprovalCallback(String msgSignature, String timestamp, String nonce, HttpServletRequest
request, HttpServletResponse response) {
log.info("处理审批回调事件----------");
try {
// 读取回调数据
StringBuilder sb = new StringBuilder();
BufferedReader reader = new BufferedReader(new InputStreamReader(request.getInputStream()));
String line;
while ((line = reader.readLine()) != null) {
sb.append(line);
}
String callbackXml = sb.toString();
String decryptedMessage = wxBizMsgCrypt.DecryptMsg(msgSignature, timestamp, nonce, callbackXml);
ApprovalBack analysis = xmlUtils.analysis(decryptedMessage);
System.out.println("解密内容analysis: " + analysis);
auditInfoMapper.updateAuditStatus(analysis);
AuditInfo audit = auditInfoMapper.selectApprovalInfo(analysis.getSpNo());
//实验区
if (audit.getAuditType().equals(FileTypeConstants.FILE_AREA_TYPE)) {
ExperimentArea experimentArea = new ExperimentArea();
experimentArea.setId(audit.getId());
experimentArea.setApprovalStatus(Integer.valueOf(analysis.getSpStatus()));
areaMapper.updateById(experimentArea);
}
//实验校
if (audit.getAuditType().equals(FileTypeConstants.FILE_SCHOOL_TYPE)) {
ExperimentSchool experimentSchool = new ExperimentSchool();
experimentSchool.setId(audit.getId());
experimentSchool.setApprovalStatus(Integer.valueOf(analysis.getSpStatus()));
schoolMapper.updateById(experimentSchool);
}
//客户
if (audit.getAuditType().equals(FileTypeConstants.FILE_CLIENT_TYPE)) {
Client client = new Client();
client.setId(audit.getId());
client.setApprovalStatus(Integer.valueOf(analysis.getSpStatus()));
clientMapper.updateById(client);
}
//创建订单
if (audit.getAuditType().equals(FileTypeConstants.FILE_CREATE_ORDER_TYPE)) {
Orders orders = new Orders();
orders.setId(audit.getId());
orders.setApprovalCreateStatus(Integer.valueOf(analysis.getSpStatus()));
ordersMapper.updateById(orders);
}
//费用
if (audit.getAuditType().equals(FileTypeConstants.FILE_COST_TYPE)) {
Cost cost = new Cost();
cost.setId(audit.getId());
cost.setApprovalStatus(Integer.valueOf(analysis.getSpStatus()));
costMapper.updateById(cost);
}
//合同
if (audit.getAuditType().equals(FileTypeConstants.FILE_CONTRACT_TYPE)) {
Contract contract = new Contract();
contract.setId(audit.getId());
contract.setApprovalStatus(Integer.valueOf(analysis.getSpStatus()));
contractMapper.updateById(contract);
}
//完成订单
if (audit.getAuditType().equals(FileTypeConstants.FILE_FINISH_ORDER_TYPE)) {
Orders orders = new Orders();
orders.setId(audit.getId());
orders.setApprovalFinishStatus(Integer.valueOf(analysis.getSpStatus()));
ordersMapper.updateById(orders);
}
//回款
if (audit.getAuditType().equals(FileTypeConstants.FILE_RETURNED_TYPE)) {
Returned returned = new Returned();
returned.setId(audit.getId());
returned.setApprovalStatus(Integer.valueOf(analysis.getSpStatus()));
returnedMapper.updateById(returned);
}
// 返回成功响应
} catch (
Exception e) {
// 处理异常,例如返回错误信息
throw new ServiceException(e.getMessage());
}
callbackXml就是回调的xml,然后进行解密解析。 analysis 属于明文
ApprovalCallback callback = ApprovalCallback.fromXml(xmlData);
这个属于解密后所有的明文数据
15.提交审批的代码
/**
* 创建订单申请
*
* @param approvalOrderDTO
* @return
*/
@Override
public String approvalOrder(ApprovalOrderDTO approvalOrderDTO) {
ApprovalTemplateDTO approvalTemplateDTO = new ApprovalTemplateDTO();
approvalTemplateDTO.setTemplate_id(weChatConfig.getCreateOrderTemplateId());
try {
String url = weChatConfig.getTemplateUrl() + "?access_token=" + getAccessToken();
// 获取模板信息
String jsonBody = new ObjectMapper().writeValueAsString(approvalTemplateDTO);
String response = HttpUtils.sendPost(url, jsonBody);
System.out.println("获取创建订单模板:" + response);
JSONObject jsonResponse = new JSONObject(response);
if (jsonResponse.getInt("errcode") != 0) {
return null;
}
// 获取模板控件内容
JSONObject templateContent = jsonResponse.getJSONObject("template_content");
JSONArray controls = templateContent.getJSONArray("controls");
// 构造字段内容
JSONArray formArray = new JSONArray();
//构造内容
setArrayOrder(approvalOrderDTO, controls, formArray);
String businessName = ordersMapper.selectOrderBusinessName(approvalOrderDTO.getBusinessType());
approvalOrderDTO.setBusinessName(businessName);
JSONObject applyData = new JSONObject();
for (int i = 0; i < controls.length(); i++) {
JSONObject control = controls.getJSONObject(i).getJSONObject("property");
String controlType = control.getString("control");
String controlId = control.getString("id");
String title = control.getJSONArray("title").getJSONObject(0).getString("text");
JSONObject field = new JSONObject();
field.put("id", controlId);
field.put("control", controlType);
JSONObject fieldValue = new JSONObject();
if ("Text".equals(controlType)) {
switch (title) {
case "客户名称":
fieldValue.put("text", approvalOrderDTO.getClientName());
break;
case "业务类型":
fieldValue.put("text", approvalOrderDTO.getBusinessName());
break;
case "合同总额":
fieldValue.put("text", approvalOrderDTO.getOrderMoney());
break;
default:
fieldValue.put("text", "无");
break;
}
} else if ("File".equals(controlType)) {
if (approvalOrderDTO.getMediaIdList() != null) {
JSONArray filesArray = new JSONArray();
for (String mediaId : approvalAreaDTO.getMediaIdList()) {
JSONObject fileObject = new JSONObject();
fileObject.put("file_id", mediaId);
filesArray.put(fileObject);
}
fieldValue.put("files", filesArray);
}
} else if ("Textarea".equals(controlType)) {
if (!StringUtils.isNull(approvalOrderDTO.getRemark())) {
fieldValue.put("text", approvalOrderDTO.getRemark());
}
}
field.put("value", fieldValue);
formArray.put(field);
}
//处理子控件
for (int i = 0; i < controls.length(); i++) {
JSONObject control = controls.getJSONObject(i).getJSONObject("property");
String controlType = control.getString("control");
String controlId = control.getString("id");
JSONObject resultObject = new JSONObject();
resultObject.put("id", controlId);
resultObject.put("control", controlType);
if ("Table".equals(controlType)) {
// 获取表格控件的配置信息
JSONObject config = controls.getJSONObject(i).getJSONObject("config");
JSONArray children = config.getJSONObject("table").getJSONArray("children");
// 获取订单项目列表
List<OrdersProjectListDTO> orderDetails = approvalOrderDTO.getOrdersProjectList();
JSONArray jsonArray = new JSONArray(); // 用于存放所有表格行数据
if (orderDetails != null && !orderDetails.isEmpty()) {
for (OrdersProjectListDTO detail : orderDetails) {
// 用于存放当前行的所有列数据
JSONArray rowArray = new JSONArray();
// 遍历表格模板的 children 数组,按模板字段顺序填充数据
for (int n = 0; n < children.length(); n++) {
JSONObject property = children.getJSONObject(n).getJSONObject("property");
// 提取控件 ID、类型和标题
String id = property.getString("id");
String controlTypeInTable = property.getString("control");
String tit = property.getJSONArray("title").getJSONObject(0).getString("text");
// 填充对应字段值
JSONObject value = new JSONObject();
switch (tit) {
case "合作项目":
value.put("text", detail.getProjectName()); // 填充项目名称
break;
case "单价":
value.put("text", detail.getUnitPrice()); // 填充单价
break;
case "数量":
value.put("text", detail.getQuantity()); // 填充数量
break;
case "小计":
value.put("text", detail.getSubtotal()); // 填充小计
break;
default:
value.put("text", "未知字段"); // 其他字段使用默认值
break;
}
// 构造当前列的数据结构
JSONObject columnData = new JSONObject();
columnData.put("id", id);
columnData.put("control", controlTypeInTable);
columnData.put("title", new JSONArray().put(new JSONObject().put("text", tit))); // 包含字段标题
columnData.put("value", value);
rowArray.put(columnData);
}
// 构造当前行数据
JSONObject rowData = new JSONObject();
rowData.put("list", rowArray);
jsonArray.put(rowData); // 添加当前行到表格数据
}
// 遍历 JSON 数组
for (int j = 0; j < formArray.length(); j++) {
JSONObject obj = formArray.getJSONObject(j);
// 判断是否是 Table 控件
if ("Table".equals(obj.getString("control"))) {
JSONObject value = obj.getJSONObject("value");
value.put("children", jsonArray);
}
}
}
}
}
// 构造最终的审批请求
applyData.put("contents", formArray);
JSONObject auditRequest = new JSONObject();
auditRequest.put("template_id", approvalTemplateDTO.getTemplate_id());
auditRequest.put("creator_userid", approvalOrderDTO.getWechatUser());
auditRequest.put("use_template_approver", 1);
auditRequest.put("apply_data", applyData);
System.out.println("构造最终的审批请求:" + auditRequest);
// 调用企业微信审核接口
String auditApiUrl = weChatConfig.getApprovalUrl() + "?access_token=" + getAccessToken();
String res = HttpUtils.sendPost(auditApiUrl, auditRequest.toString());
System.out.println("return:" + res);
JSONObject jsonObject = new JSONObject(res);
return jsonObject.getString("sp_no");
} catch (Exception e) {
e.printStackTrace();
throw new ServiceException(ServiceConstants.USER_APPROVAL);
}
}
控件不一样,类型不一样 具体参照文档
给的例子全是value里面的参数
附带订单上传的json 格式
{
"template_id": "C4ZULJy5ZkE7UuXWFu5rBfe1Xt7d91wbgpL2Rwrj1",
"creator_userid": "laoyou",
"use_template_approver": 1,
"apply_data": {
"contents": [
{
"id": "Text-1734055236654",
"control": "Text",
"value": {
"text": "老友测试客户"
}
},
{
"id": "Text-1734055244339",
"control": "Text",
"value": {
"text": "产品销售类"
}
},
{
"id": "Table-1734417961974",
"control": "Table",
"value": {
"children": [
{
"list": [
{
"id": "Text-1734417976091",
"control": "Text",
"title": [
{
"text": "合作项目"
}
],
"value": {
"text": "A项目"
}
},
{
"id": "Text-1734417982652",
"control": "Text",
"title": [
{
"text": "单价"
}
],
"value": {
"text": 1
}
},
{
"id": "Text-1734417988404",
"control": "Text",
"title": [
{
"text": "数量"
}
],
"value": {
"text": 1
}
},
{
"id": "Text-1734417994187",
"control": "Text",
"title": [
{
"text": "小计"
}
],
"value": {
"text": 1
}
}
]
},
{
"list": [
{
"id": "Text-1734417976091",
"control": "Text",
"title": [
{
"text": "合作项目"
}
],
"value": {
"text": "B项目"
}
},
{
"id": "Text-1734417982652",
"control": "Text",
"title": [
{
"text": "单价"
}
],
"value": {
"text": 2
}
},
{
"id": "Text-1734417988404",
"control": "Text",
"title": [
{
"text": "数量"
}
],
"value": {
"text": 2
}
},
{
"id": "Text-1734417994187",
"control": "Text",
"title": [
{
"text": "小计"
}
],
"value": {
"text": 4
}
}
]
}
]
}
},
{
"id": "Text-1734055362163",
"control": "Text",
"value": {
"text": 5
}
},
{
"id": "Textarea-1734055383107",
"control": "Textarea",
"value": {
"text": "这是一个订单"
}
},
{
"id": "File-1734055390371",
"control": "File",
"value": {
"files": [
{
"file_id": "39EZFpTASgYAlxDf2S09ClA0TKaQcGPyQZPEQ9HbEwQN0AWwl4BLXy8MHyB5CG6yG"
}
]
}
}
]
}
}
加密解密下载链接
登录 - 企业微信开发者中心