Merge branch 'dev' into feature/REQ-3045

This commit is contained in:
yanglin 2024-10-22 17:37:21 +08:00
commit 6aac1936a9
17 changed files with 435 additions and 13 deletions

View File

@ -71,6 +71,9 @@ public class PendingMessageBizConfig {
@Getter
private int getLatestByBizCodeMaxSize = 500;
@Getter
private int getTodoSimpleSize = 500;
/**
* 设置待办隐藏的隐藏时间
*/

View File

@ -0,0 +1,153 @@
package cn.axzo.msg.center.inside.notices.service.impl;
import cn.axzo.msg.center.dal.TingyunInterfaceListDao;
import cn.axzo.msg.center.dal.mapper.TingyunInterfaceListMapper;
import cn.axzo.msg.center.domain.entity.TingyunInterfaceList;
import cn.axzo.msg.center.inside.notices.utils.Queries;
import cn.axzo.msg.center.notices.manager.api.dto.request.plat.TingyunInterfaceListRequest;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.google.common.util.concurrent.Uninterruptibles;
import lombok.Data;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import okhttp3.FormBody;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import org.apache.commons.collections4.CollectionUtils;
import org.joda.time.DateTime;
import org.springframework.stereotype.Service;
import java.time.Duration;
import java.util.Date;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import static java.util.stream.Collectors.toSet;
/**
* @author yanglin
*/
@Slf4j
@Service
@RequiredArgsConstructor
public class TingyunService {
private final TingyunInterfaceListDao tingyunInterfaceListDao;
private final TingyunInterfaceListMapper tingyunInterfaceListMapper;
private final ExecutorService scheduler = Executors.newSingleThreadExecutor();
private volatile boolean isRunning = false;
private volatile boolean aborted = false;
public String interfaceList(TingyunInterfaceListRequest request) {
synchronized (this) {
if (isRunning)
return "前面一个操作正在执行中..";
isRunning = true;
}
scheduler.execute(() -> {
try {
interfaceListImpl(request);
} catch (Exception e) {
log.error("执行失败", e);
} finally {
synchronized (this) {
isRunning = false;
aborted = false;
}
}
});
return "scheduled...";
}
public synchronized void abort() {
aborted = true;
}
private void interfaceListImpl(TingyunInterfaceListRequest request) throws Exception {
log.info("开始执行, request={}", request);
Set<Long> applicationIds = request.getActions().stream()
.map(TingyunInterfaceListRequest.ActionInfo::getApplicationId)
.collect(toSet());
tingyunInterfaceListMapper.delete(Queries.query(TingyunInterfaceList.class)
.in(TingyunInterfaceList::getApplicationId, applicationIds));
OkHttpClient httpClient = httpClient();
DateTime startDate = new DateTime(request.determineStartDate());
DateTime endDate = new DateTime(request.determineEndDate());
outer:
for (TingyunInterfaceListRequest.ActionInfo action : request.getActions()) {
for (DateTime date = startDate;
date.isBefore(endDate) || date.isEqual(endDate);
date = date.plusDays(1)) {
if (aborted) {
log.info("中断执行, request={}", request);
break outer;
}
FormBody formBody = new FormBody.Builder()
.add("timePeriod", "1440")
.add("endTime", date.toString("yyyy-MM-dd 00:00"))
.add("bizSystemId", request.getBizSystemId() + "")
.add("sortField", "response")
.add("sortDirection", "DESC")
.add("actionName", action.getActionName())
.add("applicationId", action.getApplicationId() + "")
.build();
Request httpRequest = new Request.Builder()
.post(formBody)
.header("accept", "application/json, text/plain, */*")
.header("authorization", request.getAuth())
.url("https://wukong1.tingyun.com/server-api/webaction/list/interfaceList")
.build();
String responseBody = httpClient.newCall(httpRequest).execute().body().string();
JSONObject resp = JSON.parseObject(responseBody);
if (resp.getIntValue("code") != 200)
throw new RuntimeException("请求失败: " + responseBody);
List<TingyunInterfaceList> content = resp.getObject("data", InterfaceListData.class).getContent();
if (CollectionUtils.isNotEmpty(content)) {
Date d = date.toDate();
content.forEach(c -> c.setDate(d));
tingyunInterfaceListDao.saveBatch(content);
} else {
TingyunInterfaceList empty = TingyunInterfaceList.emptyResponse(
action.getApplicationId(), action.getActionName(), date.toDate());
tingyunInterfaceListMapper.insert(empty);
}
Uninterruptibles.sleepUninterruptibly(1, TimeUnit.SECONDS);
}
}
log.info("结束执行, request={}", request);
}
private OkHttpClient httpClient() {
return new OkHttpClient.Builder()
// 设置连接超时时间
.connectTimeout(Duration.ofSeconds(30))
// 设置读超时时间
.readTimeout(Duration.ofSeconds(60))
// 设置写超时时间
.writeTimeout(Duration.ofSeconds(60))
// 设置完整请求超时时间
.callTimeout(Duration.ofSeconds(120))
.build();
}
@Data
public static class TingyunResponse<T> {
private int code;
private T date;
}
@Data
public static class InterfaceListData {
private int totalElements;
private List<TingyunInterfaceList> content;
}
}

View File

@ -14,6 +14,7 @@ import cn.axzo.msg.center.service.pending.request.CompletePendingBySubCodeReques
import cn.axzo.msg.center.service.pending.request.CompletePendingMessageByIdRequest;
import cn.axzo.msg.center.service.pending.request.CompletePendingMessageRequest;
import cn.axzo.msg.center.service.pending.request.GetPendingTodosRequest;
import cn.axzo.msg.center.service.pending.request.GetTodoRequest;
import cn.axzo.msg.center.service.pending.request.PendingMessageByBizCodeRequest;
import cn.axzo.msg.center.service.pending.request.PendingMessageCountUncompletedRequest;
import cn.axzo.msg.center.service.pending.request.PendingMessageDetailRequestV3;
@ -259,6 +260,12 @@ public class PendingMessageNewController implements PendingMessageClient {
return CommonResponse.success(todoSimpleQueryService.getLatestTodosByBiz(param));
}
@Override
public CommonResponse<List<PendingMessageSimpleDTO>> getTodosSimple(GetTodoRequest request) {
log.info("getTodos, request={}", JSON.toJSONString(request));
return CommonResponse.success(todoSimpleQueryService.getTodosSimple(request));
}
@Override
public CommonResponse<Boolean> setHide(SetHideRequest req) {
log.info("setHide, request={}", JSON.toJSONString(req));

View File

@ -15,6 +15,7 @@ import cn.axzo.msg.center.api.request.v3.UpdateMnsChannelTemplateRequest;
import cn.axzo.msg.center.dal.MNSMessageTemplateDao;
import cn.axzo.msg.center.domain.entity.MNSMessageTemplate;
import cn.axzo.msg.center.im.service.IMService;
import cn.axzo.msg.center.inside.notices.service.impl.TingyunService;
import cn.axzo.msg.center.inside.notices.service.impl.TodoSearchService;
import cn.axzo.msg.center.inside.notices.service.impl.v3.MessageRecordServiceV3;
import cn.axzo.msg.center.message.domain.param.PendingMessagePushParam;
@ -25,6 +26,7 @@ import cn.axzo.msg.center.notices.manager.api.MessageChannelRouter;
import cn.axzo.msg.center.notices.manager.api.MessageTemplateManager;
import cn.axzo.msg.center.notices.manager.api.dto.request.plat.AddMnsAppRequest;
import cn.axzo.msg.center.notices.manager.api.dto.request.plat.CreateTemplateRequestDto;
import cn.axzo.msg.center.notices.manager.api.dto.request.plat.TingyunInterfaceListRequest;
import cn.axzo.msg.center.notices.service.api.PlatService;
import cn.axzo.msg.center.service.pending.request.RevokeByTemplateCodeRequest;
import cn.axzo.trade.web.annotation.EnableResponseAdvice;
@ -70,6 +72,7 @@ public class PrivateMessageController {
private final MNSNoticesApi mnsNoticesApi;
private final MessageChannelRouter messageChannelRouter;
private final MNSMessageTemplateDao mnsMessageTemplateDao;
private final TingyunService tingyunService;
@PostMapping("/sendPendingMessage")
@EnableResponseAdvice(enable = false)
@ -167,7 +170,7 @@ public class PrivateMessageController {
@PostMapping("/createMnsTemplate")
@EnableResponseAdvice(enable = false)
public String createMnsTemplate(@RequestBody @Valid CreateTemplateRequestDto request){
public String createMnsTemplate(@RequestBody CreateTemplateRequestDto request){
platService.createTemplate(request);
return "created";
}
@ -201,7 +204,22 @@ public class PrivateMessageController {
@PostMapping("/listMnsApps")
@EnableResponseAdvice(enable = false)
public Object listMnsApps(){
public Object listMnsApps() {
return platService.listAllApps();
}
@PostMapping("/tingyun/interfaceList")
@EnableResponseAdvice(enable = false)
public Object tingyunInterfaceList(@RequestBody @Valid TingyunInterfaceListRequest request) {
return tingyunService.interfaceList(request);
}
@PostMapping("/tingyun/abort")
@EnableResponseAdvice(enable = false)
public Object tingyunAbort() {
tingyunService.abort();
return "ok";
}
}

View File

@ -189,8 +189,8 @@ public class TodoRangeQueryService {
// executable
.and(request.determineToDoType() == TodoType.EXECUTABLE, nested -> nested
.eq(request.getMsgState() != null, Todo::getState, request.getMsgState())
.eq(request.getWorkspaceId() != null, Todo::getOrgId, request.getWorkspaceId())
.last(nested.isEmptyOfWhere(), "1 = 1"))
.eq(request.getWorkspaceId() != null, Todo::getOrgId, request.getWorkspaceId())
// 这个条件放在最后, 因为templateCodes一般比较多
// 如果放到前面, 格式化后会不方便查看其它的查询条件
// 用于查询的模版会通过一个单独的字段打印出来, 用于排查问题

View File

@ -1,5 +1,7 @@
package cn.axzo.msg.center.message.service.todo;
import cn.axzo.maokai.api.util.Ref;
import cn.axzo.basics.common.BeanMapper;
import cn.axzo.msg.center.dal.SampleTodos;
import cn.axzo.msg.center.dal.TodoBusinessDao;
import cn.axzo.msg.center.dal.TodoBusinesses;
@ -27,6 +29,7 @@ import cn.axzo.msg.center.utils.JSONObjectUtil;
import cn.hutool.core.date.DateUtil;
import com.alibaba.fastjson.JSONObject;
import com.google.common.collect.ImmutableMap;
import cn.axzo.msg.center.service.pending.response.PendingMessageSimpleDTO;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
@ -135,6 +138,14 @@ class TodoRespBuilder {
return adapters;
}
List<PendingMessageSimpleDTO> buildTodosSimple(List<Todo> todos) {
List<PendingRecordAdapter> adapters = buildTodoAdapters(todos);
return adapters.stream()
.map(MigrateUtils::convertAdapterToPending)
.map(p -> BeanMapper.copyBean(p, PendingMessageSimpleDTO.class))
.collect(toList());
}
List<PendingRecordAdapter> buildTodoAdapters(List<Todo> todos) {
if (CollectionUtils.isEmpty(todos))
return Collections.emptyList();

View File

@ -1,6 +1,5 @@
package cn.axzo.msg.center.message.service.todo;
import cn.axzo.basics.common.BeanMapper;
import cn.axzo.msg.center.common.enums.TableIsDeleteEnum;
import cn.axzo.msg.center.common.utils.BizAssertions;
import cn.axzo.msg.center.dal.TodoBusinessDao;
@ -12,6 +11,7 @@ import cn.axzo.msg.center.inside.notices.config.PendingMessageBizConfig;
import cn.axzo.msg.center.message.service.impl.v3.ModelV3ExtPopulator;
import cn.axzo.msg.center.service.enums.TodoQueryType;
import cn.axzo.msg.center.service.enums.TodoType;
import cn.axzo.msg.center.service.pending.request.GetTodoRequest;
import cn.axzo.msg.center.service.pending.request.PendingMessageByBizCodeRequest;
import cn.axzo.msg.center.service.pending.request.PendingMessageDetailRequestV3;
import cn.axzo.msg.center.service.pending.request.PendingMessageQueryRequest;
@ -121,11 +121,25 @@ public class TodoSimpleQueryService {
.max(comparing(Todo::getId))
.ifPresent(latestTodos::add);
}
List<PendingRecordAdapter> adapters = todoRespBuilder.buildTodoAdapters(latestTodos);
return adapters.stream()
.map(MigrateUtils::convertAdapterToPending)
.map(p -> BeanMapper.copyBean(p, PendingMessageSimpleDTO.class))
.collect(toList());
return todoRespBuilder.buildTodosSimple(latestTodos);
}
public List<PendingMessageSimpleDTO> getTodosSimple(GetTodoRequest request) {
List<Todo> todos = todoDao.lambdaQuery()
.in(CollectionUtils.isNotEmpty(request.getIdentityCodes()), Todo::getIdentityCode, request.getIdentityCodes())
.in(CollectionUtils.isNotEmpty(request.getTemplateCodes()), Todo::getTemplateCode, request.getTemplateCodes())
.in(CollectionUtils.isNotEmpty(request.getBizCodes()), Todo::getBizCode, request.getBizCodes())
.in(CollectionUtils.isNotEmpty(request.getStates()), Todo::getState, request.getStates())
.and(CollectionUtils.isNotEmpty(request.getExecutors()), wrapper -> {
for (GetTodoRequest.Executor executor : request.getExecutors()) {
wrapper.or()
.eq(executor.getOuId() != null, Todo::getOuId, executor.getOuId())
.eq(executor.getOuId() != null, Todo::getOrgId, executor.getWorkspaceId())
.in(CollectionUtils.isNotEmpty(executor.getPersonIds()), Todo::getExecutorPersonId, executor.getPersonIds());
}
})
.last("LIMIT " + cfg.getGetTodoSimpleSize())
.list();
return todoRespBuilder.buildTodosSimple(todos);
}
}

View File

@ -280,9 +280,10 @@ public class TodoManager {
*/
@Transactional(rollbackFor = Exception.class)
public boolean completeById(CompletePendingMessageByIdRequest request) {
BizAssertions.assertNotEmpty(request.determineIds(), "待办id不能为空");
TodoRequestContext ctx = TodoRequestContext.create("completeById", request);
StateAdvanceResult advanceResult = advanceState(ctx, execAdvanceBuilder()
.eq(Todo::getId, request.getId())
.in(Todo::getId, request.determineIds())
.set(Todo::getState, PendingMessageStateEnum.COMPLETED));
boolean businessUpdated = false;
if (advanceResult.getBusiness() != null && request.getBizExtParams() != null) {

View File

@ -17,4 +17,5 @@ public enum MnsChannel {
;
private final String code;
}

View File

@ -6,6 +6,7 @@ import cn.axzo.msg.center.service.pending.request.CompletePendingBySubCodeReques
import cn.axzo.msg.center.service.pending.request.CompletePendingMessageByIdRequest;
import cn.axzo.msg.center.service.pending.request.CompletePendingMessageRequest;
import cn.axzo.msg.center.service.pending.request.GetPendingTodosRequest;
import cn.axzo.msg.center.service.pending.request.GetTodoRequest;
import cn.axzo.msg.center.service.pending.request.PendingMessageByBizCodeRequest;
import cn.axzo.msg.center.service.pending.request.PendingMessageCountUncompletedRequest;
import cn.axzo.msg.center.service.pending.request.PendingMessageDetailRequestV3;
@ -327,4 +328,8 @@ public interface PendingMessageClient {
@PostMapping(value = "/pending-message/set-copied-to-me-read", produces = {MediaType.APPLICATION_JSON_VALUE})
CommonResponse<Boolean> setCopiedToMeRead(@RequestParam("personId") Long personId,
@RequestParam(value = "identityCode", defaultValue = "") String identityCode);
@PostMapping(value = "/pending-message/record/get-todos-simple", produces = {MediaType.APPLICATION_JSON_VALUE})
CommonResponse<List<PendingMessageSimpleDTO>> getTodosSimple(@RequestBody @Valid GetTodoRequest request);
}

View File

@ -6,7 +6,9 @@ import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import javax.validation.constraints.NotNull;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
* @author haiyangjin
@ -19,11 +21,15 @@ import javax.validation.constraints.NotNull;
@NoArgsConstructor
public class CompletePendingMessageByIdRequest {
/**
* 关联业务主键
* 待办id, id和ids二选一
*/
@NotNull(message = "消息ID不能为空")
private Long id;
/**
* 待办id, id和ids二选一
*/
private List<Long> ids;
/**
* 业务扩展参数,JSON字符串格式
*/
@ -34,4 +40,13 @@ public class CompletePendingMessageByIdRequest {
*/
private String templateCode;
public Set<Long> determineIds() {
HashSet<Long> todoIds = new HashSet<>();
if (id != null)
todoIds.add(id);
if (ids != null)
todoIds.addAll(ids);
return todoIds;
}
}

View File

@ -0,0 +1,35 @@
package cn.axzo.msg.center.service.pending.request;
import cn.axzo.msg.center.service.enums.PendingMessageStateEnum;
import com.alibaba.fastjson.JSON;
import lombok.Getter;
import lombok.Setter;
import java.util.List;
import java.util.Set;
/**
* @author yanglin
*/
@Setter
@Getter
public class GetTodoRequest {
private Set<String> identityCodes;
private Set<String> templateCodes;
private Set<String> bizCodes;
private Set<PendingMessageStateEnum> states;
private List<Executor> executors;
@Setter
@Getter
public static class Executor {
private Long ouId;
private Long workspaceId;
private List<Long> personIds;
}
@Override
public String toString() {
return JSON.toJSONString(this);
}
}

View File

@ -0,0 +1,15 @@
package cn.axzo.msg.center.dal;
import cn.axzo.msg.center.dal.mapper.TingyunInterfaceListMapper;
import cn.axzo.msg.center.domain.entity.TingyunInterfaceList;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
/**
* @author yanglin
*/
@Slf4j
@Component
public class TingyunInterfaceListDao extends ServiceImpl<TingyunInterfaceListMapper, TingyunInterfaceList> {
}

View File

@ -0,0 +1,10 @@
package cn.axzo.msg.center.dal.mapper;
import cn.axzo.msg.center.domain.entity.TingyunInterfaceList;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
/**
* @author yanglin
*/
public interface TingyunInterfaceListMapper extends BaseMapper<TingyunInterfaceList> {
}

View File

@ -0,0 +1,43 @@
package cn.axzo.msg.center.domain.entity;
import cn.axzo.msg.center.service.enums.YesOrNo;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Getter;
import lombok.Setter;
import java.math.BigDecimal;
import java.util.Date;
/**
* @author yanglin
*/
@Setter
@Getter
@TableName(value = "tingyun_interface_list", autoResultMap = true)
public class TingyunInterfaceList {
private Long id;
private Date date;
private Long applicationId;
private String applicationName;
private String actionName;
private String actionAlias;
private Integer response;
private BigDecimal throught;
private BigDecimal maxThrought;
private BigDecimal error;
private BigDecimal timePercentage;
private Integer errorCount;
private Integer slowCount;
private Integer totalResponse;
private Integer count;
private YesOrNo responseEmpty = YesOrNo.NO;
public static TingyunInterfaceList emptyResponse(Long applicationId, String action, Date date) {
TingyunInterfaceList list = new TingyunInterfaceList();
list.setResponseEmpty(YesOrNo.YES);
list.setApplicationId(applicationId);
list.setActionName(action);
list.setDate(date);
return list;
}
}

View File

@ -0,0 +1,55 @@
package cn.axzo.msg.center.notices.manager.api.dto.request.plat;
import com.alibaba.fastjson.JSON;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import org.joda.time.DateTime;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import java.util.Date;
import java.util.List;
/**
* @author yanglin
*/
@Data
public class TingyunInterfaceListRequest {
@NotBlank
private String auth;
@JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
private Date startDate;
@JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
private Date endDate;
private Long bizSystemId = 4449L;
@NotEmpty
private List<ActionInfo> actions;
public Date determineStartDate() {
if (startDate != null)
return startDate;
return new DateTime(determineEndDate()).minusDays(60).toDate();
}
public Date determineEndDate() {
if (endDate != null)
return endDate;
return new Date();
}
@Override
public String toString() {
return JSON.toJSONString(this);
}
@Data
public static class ActionInfo {
@NotNull
private Long applicationId;
@NotBlank
private String actionName;
}
}

View File

@ -0,0 +1,36 @@
package cn.axzo.msg.center.inside.notices.service.impl;
import cn.axzo.msg.center.MsgCenterApplication;
import cn.axzo.msg.center.notices.manager.api.dto.request.plat.TingyunInterfaceListRequest;
import lombok.RequiredArgsConstructor;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.Collections;
import java.util.Date;
import static org.junit.jupiter.api.Assertions.*;
/**
* @author yanglin
*/
@SpringBootTest(classes = MsgCenterApplication.class)
@RequiredArgsConstructor(onConstructor_ = @Autowired)
class TingyunServiceTest {
private final TingyunService tingyunService;
@Test
void foo() {
TingyunInterfaceListRequest request = new TingyunInterfaceListRequest();
request.setAuth("Bearer eyJhbGciOiJSUzI1NiJ9.eyJzdWIiOiIxOGM0M2IzZS0yZTdmLTQzOTMtODBiZS04MjVhZDJlYTlkNmYiLCJpZCI6ImYxYTQxNDdmLWNjNmEtNGNlMC04OGYyLWI4NmViMWU5ZWYzMiIsIm5hbWUiOiJkYmIwNThlMy1jZjhkLTQ0NjQtOTU4My02NjA5ZTBiY2VlMDEiLCJjb2RlIjoiNTNlYTE5ZTItMWI2NS00YTUyLTg5ZmUtN2NmNjAzZWE5NTg5IiwiYWdyZWVtZW50SWQiOiJjNDBlY2UzZS1jZTUzLTQ0NDgtODUzOC02N2NjOWU4OTkzOTMiLCJ0eXBlIjoiMWJmM2M0NWEtMTk3ZC00YzdiLTlhMWItOWEyNzlhOGYyMWM2IiwicmlnaHRSYW5nZSI6IjJlOTQxODQzLWYwZTQtNGIxZC1iNjQ1LTRlNjRhZTRjNWUyMyIsInJhbmRvbSI6ImZiMzczNjE5LTBkYWEtNDY4ZS1iOTM1LWUzYWZiYjMxNGRjMSJ9.OWTE9F0hBn1jJDukLoON3exQZuHVk16bGqo4dDnfQz5xytZiOWvnI9iUIopKjsv9nh4NkQC2h10sSTwFHpFpf31J-aNi112bc69nYvnTI-5NHs8gWywYJ3kSUB8tGobtQC4nWlMKHb1fdiS-0pO-FI8fYkI1c580ULdvP15IUESYW6rueNr-IcmV87XVVNWqrUm7jWP8vmpzI6_10MBUTp2uZD889e1mD_XHBErV_-h93GaNc_2Bluke1LizeNHGx5F1QU0ZGkzQWsst9trlGwvJWLYlJtsKlQPfBftGmkuDfPmp3QJPseWQPXtTcfA9xGvsxIaf3M9Rva7RRfCcFw");
request.setEndDate(new Date());
TingyunInterfaceListRequest.ActionInfo action = new TingyunInterfaceListRequest.ActionInfo();
action.setApplicationId(64254L);
action.setActionName("groupStatisticV2");
request.setActions(Collections.singletonList(action));
tingyunService.interfaceList(request);
}
}