Commit 6fa4d339 by zhangshaowu

init

0 parents
Showing 107 changed files with 5362 additions and 0 deletions
target/
*.log
### STS ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
### IntelliJ IDEA ###
.idea
*.iws
*.iml
*.ipr
### NetBeans ###
nbproject/private/
build/
nbbuild/
dist/
nbdist/
.nb-gradle/
\ No newline at end of file \ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.pipi.test</groupId>
<artifactId>pipi-helper</artifactId>
<version>1.0</version>
<packaging>jar</packaging>
<properties>
<java.version>1.8</java.version>
</properties>
<!-- Inherit defaults from Spring Boot -->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.5.1</version>
</parent>
<dependencies>
<!--Spring Boot依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.commons/commons-pool2 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
<version>2.11.1</version>
</dependency>
<!--常用库依赖-->
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.12.0</version>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>23.0</version>
</dependency>
<!--MySQL JDBC驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<!--MyBatis 及 插件依赖-->
<!--引入 mybatis-spring-boot-starter 的依赖-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.2.2</version>
</dependency>
<dependency>
<groupId>tk.mybatis</groupId>
<artifactId>mapper</artifactId>
<version>4.1.5</version>
</dependency>
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>1.3.0</version>
</dependency>
<!--阿里 FastJson依赖-->
<!-- https://mvnrepository.com/artifact/com.alibaba/fastjson -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.83</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.jayway.jsonpath/json-path -->
<dependency>
<groupId>com.jayway.jsonpath</groupId>
<artifactId>json-path</artifactId>
<version>2.7.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.squareup.okhttp3/okhttp -->
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>4.9.3</version>
</dependency>
<!--阿里 Druid Spring Boot Starter依赖-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.10</version>
</dependency>
<!--代码生成器依赖-->
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-core</artifactId>
<version>1.3.5</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.22</version>
</dependency>
<!--云效sdk-->
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>devops20210625</artifactId>
<version>1.1.15</version>
</dependency>
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>tea-openapi</artifactId>
<version>0.2.4</version>
</dependency>
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>tea-console</artifactId>
<version>0.0.1</version>
</dependency>
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>tea-util</artifactId>
<version>0.2.13</version>
</dependency>
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>tea</artifactId>
<version>1.1.15</version>
</dependency>
<!--腾讯云-->
<!-- https://mvnrepository.com/artifact/com.tencentcloudapi/tencentcloud-sdk-java -->
<dependency>
<groupId>com.tencentcloudapi</groupId>
<artifactId>tencentcloud-sdk-java</artifactId>
<version>4.0.11</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.dom4j/dom4j -->
<dependency>
<groupId>org.dom4j</groupId>
<artifactId>dom4j</artifactId>
<version>2.1.3</version>
</dependency>
<!--缓存-->
<!-- https://mvnrepository.com/artifact/com.github.ben-manes.caffeine/caffeine -->
<dependency>
<groupId>com.github.ben-manes.caffeine</groupId>
<artifactId>caffeine</artifactId>
<version>2.9.3</version>
</dependency>
<!--hutool-->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-core</artifactId>
<version>5.8.2</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
</dependency>
<!-- <dependency>-->
<!-- <groupId>org.testng</groupId>-->
<!-- <artifactId>testng</artifactId>-->
<!-- <version>RELEASE</version>-->
<!-- <scope>compile</scope>-->
<!-- </dependency>-->
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
</plugins>
</build>
<repositories>
<repository>
<id>aliyun-repos</id>
<url>http://maven.aliyun.com/nexus/content/groups/public/</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>aliyun-plugin</id>
<url>http://maven.aliyun.com/nexus/content/groups/public/</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</pluginRepository>
</pluginRepositories>
</project>
\ No newline at end of file \ No newline at end of file
package com.pipihelper.project;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.annotation.EnableScheduling;
@SpringBootApplication
@EnableAsync
@EnableScheduling
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
package com.pipihelper.project.configurer;
import org.springframework.context.annotation.Configuration;
/**
* Mybatis & Mapper & PageHelper 配置
*/
@Configuration
public class MybatisConfigurer {
// @Bean
// public SqlSessionFactory sqlSessionFactoryBean(DataSource dataSource) throws Exception {
// SqlSessionFactoryBean factory = new SqlSessionFactoryBean();
// factory.setDataSource(dataSource);
// factory.setTypeAliasesPackage(MODEL_PACKAGE);
//
//// //配置分页插件,详情请查阅官方文档
//// PageHelper pageHelper = new PageHelper();
//// Properties properties = new Properties();
//// properties.setProperty("pageSizeZero", "true");//分页尺寸为0时查询所有纪录不再执行分页
//// properties.setProperty("reasonable", "true");//页码<=0 查询第一页,页码>=总页数查询最后一页
//// properties.setProperty("supportMethodsArguments", "true");//支持通过 Mapper 接口参数来传递分页参数
//// pageHelper.setProperties(properties);
////
//// //添加插件
//// factory.setPlugins(new Interceptor[]{pageHelper});
//
// //添加XML目录
// ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
// factory.setMapperLocations(resolver.getResources("classpath:mapper/*.xml"));
// return factory.getObject();
// }
}
package com.pipihelper.project.configurer;
import com.pipihelper.project.tx.dto.TxConfig;
import com.tencentcloudapi.common.Credential;
import com.tencentcloudapi.common.profile.ClientProfile;
import com.tencentcloudapi.common.profile.HttpProfile;
import com.tencentcloudapi.nlp.v20190408.NlpClient;
import com.tencentcloudapi.tbp.v20190627.TbpClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.task.TaskExecutor;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import java.util.concurrent.ThreadPoolExecutor;
/**
* @Description: TODO
* @author: charles
* @date: 2022年04月16日 10:28
*/
@Configuration
public class PersonalConfigurer {
@Autowired
private TxConfig txConfig;
@Bean
public TaskExecutor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(10); // 设置核心线程数
executor.setMaxPoolSize(20); // 设置最大线程数
executor.setQueueCapacity(20); // 设置队列容量
executor.setKeepAliveSeconds(60); // 设置线程活跃时间(秒)
executor.setThreadNamePrefix("user-rpt-"); // 设置默认线程名称
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); // 设置拒绝策略
executor.setWaitForTasksToCompleteOnShutdown(true); // 等待所有任务结束后再关闭线程池
return executor;
}
@Bean
public NlpClient createTxNlpApiClient(){
// 实例化一个认证对象,入参需要传入腾讯云账户secretId,secretKey,此处还需注意密钥对的保密
// 密钥可前往https://console.cloud.tencent.com/cam/capi网站进行获取
Credential cred = new Credential(txConfig.getSecretId(), txConfig.getSecretKey());
// 实例化一个http选项,可选的,没有特殊需求可以跳过
HttpProfile httpProfile = new HttpProfile();
httpProfile.setEndpoint("nlp.tencentcloudapi.com");
// 实例化一个client选项,可选的,没有特殊需求可以跳过
ClientProfile clientProfile = new ClientProfile();
clientProfile.setHttpProfile(httpProfile);
clientProfile.setDebug(true);
// 实例化要请求产品的client对象,clientProfile是可选的
return new NlpClient(cred, "ap-guangzhou", clientProfile);
}
@Bean
public TbpClient createTxTbpApiClient(){
// 实例化一个认证对象,入参需要传入腾讯云账户secretId,secretKey,此处还需注意密钥对的保密
// 密钥可前往https://console.cloud.tencent.com/cam/capi网站进行获取
Credential cred = new Credential(txConfig.getSecretId(), txConfig.getSecretKey());
// 实例化一个http选项,可选的,没有特殊需求可以跳过
HttpProfile httpProfile = new HttpProfile();
httpProfile.setEndpoint("tbp.tencentcloudapi.com");
// 实例化一个client选项,可选的,没有特殊需求可以跳过
ClientProfile clientProfile = new ClientProfile();
clientProfile.setHttpProfile(httpProfile);
clientProfile.setDebug(true);
// 实例化要请求产品的client对象,clientProfile是可选的
return new TbpClient(cred, "", clientProfile);
}
}
package com.pipihelper.project.configurer;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.alibaba.fastjson.support.config.FastJsonConfig;
import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter;
import com.pipihelper.project.core.ResultCode;
import com.pipihelper.project.core.ServiceException;
import com.pipihelper.project.core.Result;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.NoHandlerFoundException;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
/**
* Spring MVC 配置
*/
@Configuration
public class WebMvcConfigurer extends WebMvcConfigurerAdapter {
private final Logger logger = LoggerFactory.getLogger(WebMvcConfigurer.class);
@Value("${spring.profiles.active}")
private String env;//当前激活的配置文件
//使用阿里 FastJson 作为JSON MessageConverter
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
FastJsonHttpMessageConverter converter = new FastJsonHttpMessageConverter();
FastJsonConfig config = new FastJsonConfig();
config.setSerializerFeatures(SerializerFeature.WriteMapNullValue);//保留空的字段
//SerializerFeature.WriteNullStringAsEmpty,//String null -> ""
//SerializerFeature.WriteNullNumberAsZero//Number null -> 0
// 按需配置,更多参考FastJson文档哈
converter.setFastJsonConfig(config);
converter.setDefaultCharset(Charset.forName("UTF-8"));
converter.setSupportedMediaTypes(Arrays.asList(MediaType.APPLICATION_JSON_UTF8));
converters.add(converter);
}
//统一异常处理
@Override
public void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> exceptionResolvers) {
exceptionResolvers.add(new HandlerExceptionResolver() {
public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception e) {
Result result = new Result();
if (e instanceof ServiceException) {//业务失败的异常,如“账号或密码错误”
result.setCode(ResultCode.FAIL).setMessage(e.getMessage());
logger.info(e.getMessage());
} else if (e instanceof NoHandlerFoundException) {
result.setCode(ResultCode.NOT_FOUND).setMessage("接口 [" + request.getRequestURI() + "] 不存在");
} else if (e instanceof ServletException) {
result.setCode(ResultCode.FAIL).setMessage(e.getMessage());
} else {
result.setCode(ResultCode.INTERNAL_SERVER_ERROR).setMessage("接口 [" + request.getRequestURI() + "] 内部错误,请联系管理员");
String message;
if (handler instanceof HandlerMethod) {
HandlerMethod handlerMethod = (HandlerMethod) handler;
message = String.format("接口 [%s] 出现异常,方法:%s.%s,异常摘要:%s",
request.getRequestURI(),
handlerMethod.getBean().getClass().getName(),
handlerMethod.getMethod().getName(),
e.getMessage());
} else {
message = e.getMessage();
}
logger.error(message, e);
}
responseResult(response, result);
return new ModelAndView();
}
});
}
//解决跨域问题
@Override
public void addCorsMappings(CorsRegistry registry) {
//registry.addMapping("/**");
}
//添加拦截器
@Override
public void addInterceptors(InterceptorRegistry registry) {
//接口签名认证拦截器,该签名认证比较简单,实际项目中可以使用Json Web Token或其他更好的方式替代。
// if (!"dev".equals(env)) { //开发环境忽略签名认证
// registry.addInterceptor(new HandlerInterceptorAdapter() {
// @Override
// public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// //验证签名
// boolean pass = validateSign(request);
// if (pass) {
// return true;
// } else {
// logger.warn("签名认证失败,请求接口:{},请求IP:{},请求参数:{}",
// request.getRequestURI(), getIpAddress(request), JSON.toJSONString(request.getParameterMap()));
//
// Result result = new Result();
// result.setCode(ResultCode.UNAUTHORIZED).setMessage("签名认证失败");
// responseResult(response, result);
// return false;
// }
// }
// });
// }
}
private void responseResult(HttpServletResponse response, Result result) {
response.setCharacterEncoding("UTF-8");
response.setHeader("Content-type", "application/json;charset=UTF-8");
response.setStatus(200);
try {
response.getWriter().write(JSON.toJSONString(result));
} catch (IOException ex) {
logger.error(ex.getMessage());
}
}
/**
* 一个简单的签名认证,规则:
* 1. 将请求参数按ascii码排序
* 2. 拼接为a=value&b=value...这样的字符串(不包含sign)
* 3. 混合密钥(secret)进行md5获得签名,与请求的签名进行比较
*/
private boolean validateSign(HttpServletRequest request) {
String requestSign = request.getParameter("sign");//获得请求签名,如sign=19e907700db7ad91318424a97c54ed57
if (StringUtils.isEmpty(requestSign)) {
return false;
}
List<String> keys = new ArrayList<String>(request.getParameterMap().keySet());
keys.remove("sign");//排除sign参数
Collections.sort(keys);//排序
StringBuilder sb = new StringBuilder();
for (String key : keys) {
sb.append(key).append("=").append(request.getParameter(key)).append("&");//拼接字符串
}
String linkString = sb.toString();
linkString = StringUtils.substring(linkString, 0, linkString.length() - 1);//去除最后一个'&'
String secret = "Potato";//密钥,自己修改
String sign = DigestUtils.md5Hex(linkString + secret);//混合密钥md5
return StringUtils.equals(sign, requestSign);//比较
}
private String getIpAddress(HttpServletRequest request) {
String ip = request.getHeader("x-forwarded-for");
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("Proxy-Client-IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("WL-Proxy-Client-IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("HTTP_CLIENT_IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("HTTP_X_FORWARDED_FOR");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getRemoteAddr();
}
// 如果是多级代理,那么取第一个ip为客户端ip
if (ip != null && ip.indexOf(",") != -1) {
ip = ip.substring(0, ip.indexOf(",")).trim();
}
return ip;
}
}
package com.pipihelper.project.controller;
import com.pipihelper.project.core.Result;
import com.pipihelper.project.core.ResultGenerator;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @Description: TODO
* @author: charles
* @date: 2022年06月29日 14:54
*/
@RestController
@RequestMapping("/pipitest")
public class HealthCheckController {
@PostMapping("/health-check")
public Result healthCheck(){
return ResultGenerator.genSuccessResult();
}
}
package com.pipihelper.project.controller;
import com.alibaba.fastjson.JSONObject;
import com.pipihelper.project.core.Result;
import com.pipihelper.project.core.ResultGenerator;
import com.pipihelper.project.feishu.dto.FeiShuConfig;
import com.pipihelper.project.feishu.enums.SendMsgBusinessType;
import com.pipihelper.project.feishu.service.FeiShuApiService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @Description: TODO
* @author: charles
* @date: 2022年07月20日 14:50
*/
@RestController
@RequestMapping("/pipitest")
public class SendMsgUseFeiShu {
@Autowired
private FeiShuApiService feiShuApiService;
@Autowired
private FeiShuConfig feiShuConfig;
@PostMapping("/send-msg")
public Result sendMsg(String receiveId, String msg, String receiveIdType){
JSONObject sendMsg = new JSONObject();
sendMsg.put("receive_id", receiveId);
sendMsg.put("msg_type", "text");
JSONObject content = new JSONObject();
content.put("text", msg);
sendMsg.put("content", content.toString());
feiShuApiService.sendMsg(SendMsgBusinessType.TALK.getBusinessType(), receiveIdType, sendMsg, feiShuConfig.getTestHelperApp());
return ResultGenerator.genSuccessResult();
}
}
package com.pipihelper.project.core;
/**
* 项目常量
*/
public final class ProjectConstant {
public static final String BASE_PACKAGE = "com.pipitest.project";
public static final String MODEL_PACKAGE = BASE_PACKAGE + ".model";//Model所在包
public static final String MAPPER_PACKAGE = BASE_PACKAGE + ".mapper";//Mapper所在包
public static final String SERVICE_PACKAGE = BASE_PACKAGE + ".service";//Service所在包
public static final String SERVICE_IMPL_PACKAGE = SERVICE_PACKAGE + ".impl";//ServiceImpl所在包
public static final String CONTROLLER_PACKAGE = BASE_PACKAGE + ".controller";//Controller所在包
public static final String MAPPER_INTERFACE_REFERENCE = BASE_PACKAGE + ".core.Mapper";//Mapper插件基础接口的完全限定名
}
package com.pipihelper.project.core;
import com.alibaba.fastjson.JSON;
/**
* 统一API响应结果封装
*/
public class Result<T> {
private int code;
private String message;
private T data;
public Result setCode(ResultCode resultCode) {
this.code = resultCode.code();
return this;
}
public int getCode() {
return code;
}
public String getMessage() {
return message;
}
public Result setMessage(String message) {
this.message = message;
return this;
}
public T getData() {
return data;
}
public Result setData(T data) {
this.data = data;
return this;
}
@Override
public String toString() {
return JSON.toJSONString(this);
}
}
package com.pipihelper.project.core;
/**
* 响应码枚举,参考HTTP状态码的语义
*/
public enum ResultCode {
SUCCESS(200),//成功
FAIL(400),//失败
UNAUTHORIZED(401),//未认证(签名错误)
NOT_FOUND(404),//接口不存在
INTERNAL_SERVER_ERROR(500);//服务器内部错误
private final int code;
ResultCode(int code) {
this.code = code;
}
public int code() {
return code;
}
}
package com.pipihelper.project.core;
/**
* 响应结果生成工具
*/
public class ResultGenerator {
private static final String DEFAULT_SUCCESS_MESSAGE = "SUCCESS";
public static Result genSuccessNoResult() {
return new Result();
}
public static Result genSuccessResult() {
return new Result()
.setCode(ResultCode.SUCCESS)
.setMessage(DEFAULT_SUCCESS_MESSAGE);
}
public static <T> Result<T> genSuccessResult(T data) {
return new Result()
.setCode(ResultCode.SUCCESS)
.setMessage(DEFAULT_SUCCESS_MESSAGE)
.setData(data);
}
public static Result genFailResult(String message) {
return new Result()
.setCode(ResultCode.FAIL)
.setMessage(message);
}
}
package com.pipihelper.project.core;
/**
* 服务(业务)异常如“ 账号或密码错误 ”,该异常只做INFO级别的日志记录 @see WebMvcConfigurer
*/
public class ServiceException extends RuntimeException {
public ServiceException() {
}
public ServiceException(String message) {
super(message);
}
public ServiceException(String message, Throwable cause) {
super(message, cause);
}
}
package com.pipihelper.project.feishu.controller;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.pipihelper.project.feishu.dto.FeiShuConfig;
import com.pipihelper.project.feishu.dto.FeiShuEventDTO;
import com.pipihelper.project.feishu.service.FeiShuEventService;
import com.pipihelper.project.feishu.utils.FeiShuEventDataDecrypter;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
/**
* @Description: TODO
* @author: charles
* @date: 2022年03月30日 17:39
*/
@RestController
@RequestMapping("/pipitest/feishu")
public class FeiShuEventController {
@Resource
private FeiShuConfig feiShuConfig;
@Resource
private FeiShuEventService feiShuEventService;
@PostMapping("/event")
public JSONObject event(@RequestBody String s) throws Exception {
JSONObject reqJsonObject = JSON.parseObject(s);
System.out.println(s);
FeiShuEventDataDecrypter d = new FeiShuEventDataDecrypter(feiShuConfig.getAppConfigMap().get(feiShuConfig.getTestHelperApp()).getEncryptKey());
JSONObject encryptJsonObject = JSON.parseObject(d.decrypt(reqJsonObject.getString("encrypt")));
System.out.println(encryptJsonObject);
if (encryptJsonObject.containsKey("challenge")) {
return encryptJsonObject;
}
FeiShuEventDTO feiShuEventDTO = encryptJsonObject.toJavaObject(FeiShuEventDTO.class);
if (!feiShuEventDTO.getHeader().getToken().equalsIgnoreCase(feiShuConfig.getAppConfigMap().get(feiShuConfig.getTestHelperApp()).getVerificationToken())) {
return null;
}
if ("im.message.receive_v1".equalsIgnoreCase(feiShuEventDTO.getHeader().getEvent_type())) {
// feiShuEventService.imMessageReceiveV1(feiShuEventDTO);
} else if ("im.message.message_read_v1".equalsIgnoreCase(feiShuEventDTO.getHeader().getEvent_type())) {
// feiShuEventService.imMessageMessageReadV1(feiShuEventDTO);
}
return null;
}
// @PostMapping("msg_card")
// public JSONObject msgCardEvent(@RequestBody String s) throws Exception {
// JSONObject reqJsonObject = JSON.parseObject(s);
// System.out.println(s);
// if (reqJsonObject.containsKey("challenge")) {
// return reqJsonObject;
// }
// FeiShuMsgCardEventDTO feiShuMsgCardEventDTO = reqJsonObject.toJavaObject(FeiShuMsgCardEventDTO.class);
// String actionType = feiShuMsgCardEventDTO.getAction().getValue().getKey().split("\\.")[0];
// switch (actionType) {
// case "TEST_DATA":
//
//// default:
//// }
// }
//
// }
}
package com.pipihelper.project.feishu.controller;
import com.pipihelper.project.core.Result;
import com.pipihelper.project.core.ResultGenerator;
import com.pipihelper.project.feishu.service.doc.DocHelperService;
import com.pipihelper.project.feishu.vo.CreateTaskVo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @author xiongjian
* @date 2022/7/27
*/
@RestController
@RequestMapping("/pipitest")
@Slf4j
public class FeishuTaskController {
@Autowired
private DocHelperService docHelperService;
@RequestMapping("/creatTask")
public Result creatTask(CreateTaskVo createTaskVo){
log.info("创建任务接口:{}", createTaskVo);
docHelperService.createTask(createTaskVo);
return ResultGenerator.genSuccessResult();
}
}
package com.pipihelper.project.feishu.dto;
import lombok.Data;
import lombok.NoArgsConstructor;
@NoArgsConstructor
@Data
public class AppConfig {
private String encryptKey;
private String verificationToken;
private String appId;
private String appSecret;
private String fkChatId;
private String fkChatIdTest;
private String tableId;
private String peopleTableId;
private String taskTableId;
}
package com.pipihelper.project.feishu.dto;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import java.util.Map;
/**
* @Description: TODO
* @author: charles
* @date: 2022年04月11日 18:32
*/
@Component
@ConfigurationProperties(prefix = "feishu")
@Data
@NoArgsConstructor
@AllArgsConstructor
public class FeiShuConfig {
private String feiShuOpenApiHost;
private String testHelperApp;
private String anniversaryApp;
private Map<String, AppConfig> appConfigMap;
}
package com.pipihelper.project.feishu.dto;
import com.alibaba.fastjson.annotation.JSONField;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
/**
* @description: some desc
* @author: charles
* @date: 2022/6/5 20:17
*/
@NoArgsConstructor
@Data
public class FeiShuCreateTaskDTO {
@JSONField(name = "summary")
private String summary;
@JSONField(name = "description")
private String description;
@JSONField(name = "origin")
private OriginDTO origin;
@JSONField(name = "can_edit")
private Boolean canEdit;
@JSONField(name = "due")
private Due due;
@JSONField(name = "follower_ids")
private List<String> followerIds;
@JSONField(name = "collaborator_ids")
private List<String> collaboratorIds;
@NoArgsConstructor
@Data
public static class OriginDTO {
@JSONField(name = "platform_i18n_name")
private String platformI18nName;
@JSONField(name = "href")
private HrefDTO href;
@NoArgsConstructor
@Data
public static class HrefDTO {
@JSONField(name = "url")
private String url;
@JSONField(name = "title")
private String title;
}
}
@NoArgsConstructor
@Data
public static class Due{
@JSONField(name = "time")
private String time;
}
}
package com.pipihelper.project.feishu.dto;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.pipihelper.project.feishu.dto.department.FeiShuDepartmentDTO;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
@NoArgsConstructor
@Data
public class FeiShuDataDTO<T> {
@JsonProperty("items")
private List<T> items;
@JsonProperty("page_token")
private String pageToken;
@JsonProperty("has_more")
private Boolean hasMore;
@JsonProperty("department")
private FeiShuDepartmentDTO department;
}
package com.pipihelper.project.feishu.dto;
import com.alibaba.fastjson.JSONObject;
import lombok.Getter;
import lombok.Setter;
/**
* @Description: TODO
* @author: charles
* @date: 2022年04月12日 9:52
*/
@Getter
@Setter
public class FeiShuEventDTO {
private String schema;
private FeiShuEventHeaderDTO header;
private JSONObject event;
}
package com.pipihelper.project.feishu.dto;
import lombok.Getter;
import lombok.Setter;
/**
* @Description: TODO
* @author: charles
* @date: 2022年04月12日 9:54
*/
@Getter
@Setter
public class FeiShuEventHeaderDTO {
private String event_id;
private String event_type;
private String create_time;
private String token;
private String app_id;
private String tenant_key;
}
package com.pipihelper.project.feishu.dto;
import lombok.Data;
import java.util.List;
/**
* @Description: TODO
* @author: charles
* @date: 2022年05月26日 11:18
*/
@Data
public class FeiShuEventMessageReadDTO{
private ReaderBean reader;
private List<String> message_id_list;
@Data
public static class ReaderBean{
private ReaderIdBean reader_id;
private String read_time;
private String tenant_key;
@Data
public static class ReaderIdBean{
private String union_id;
private String user_id;
private String open_id;
}
}
}
package com.pipihelper.project.feishu.dto;
import com.alibaba.fastjson.JSONObject;
import lombok.Data;
import lombok.Getter;
import lombok.Setter;
import java.util.List;
/**
* @Description: TODO
* @author: charles
* @date: 2022年04月20日 13:27
*/
@Getter
@Setter
public class FeiShuEventReceiveMessageContentDTO {
@Getter
@Setter
public static class Text {
private String text;
}
@Getter
@Setter
public static class Post {
private String title;
private List<List<JSONObject>> content;
}
@Getter
@Setter
public static class Image {
private String image_key;
}
@Getter
@Setter
public static class File {
private String file_key;
private String file_name;
}
@Getter
@Setter
public static class Audio {
private String file_key;
private String duration;
}
@Getter
@Setter
public static class Media {
private String file_key;
private String image_key;
private String file_name;
private String duration;
}
@Getter
@Setter
public static class Sticker {
private String file_key;
}
@Getter
@Setter
public static class Interactive {
private String title;
private List<List<JSONObject>> elements;
}
}
package com.pipihelper.project.feishu.dto;
import lombok.Getter;
import lombok.Setter;
import java.util.List;
/**
* @Description: TODO
* @author: charles
* @date: 2022年04月12日 9:58
*/
@Getter
@Setter
public class FeiShuEventReceiveMessageDTO {
private Sender sender;
private Message message;
@Getter
@Setter
public static class Sender {
private Sender_id sender_id;
private String sender_type;
private String tenant_key;
@Getter
@Setter
public static class Sender_id {
private String union_id;
private String user_id;
private String open_id;
}
}
@Getter
@Setter
public static class Message {
private String message_id;
private String root_id;
private String parent_id;
private String create_time;
private String chat_id;
private String chat_type;
private String message_type;
private String content;
private List<Mentions> mentions;
@Getter
@Setter
public static class Mentions {
private String key;
private Id id;
private String name;
private String tenant_key;
@Getter
@Setter
public static class Id {
private String union_id;
private String user_id;
private String open_id;
}
}
}
}
package com.pipihelper.project.feishu.dto;
import com.alibaba.fastjson.annotation.JSONField;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
/**
* @description: some desc
* @author: charles
* @date: 2022/5/24 9:46 下午
*/
@Data
public class FeiShuMsgCardDTO {
@JSONField(name = "config")
private ConfigDTO config;
@JSONField(name = "header")
private HeaderDTO header;
@JSONField(name = "elements")
private List<ElementsDTO> elements;
@Data
public static class ConfigDTO {
@JSONField(name = "wide_screen_mode")
private Boolean wideScreenMode;
}
@Data
public static class HeaderDTO {
@JSONField(name = "title")
private TitleDTO title;
@JSONField(name = "template")
private String template;
@Data
public static class TitleDTO {
@JSONField(name = "tag")
private String tag;
@JSONField(name = "content")
private String content;
}
}
@Data
public static class ElementsDTO {
@JSONField(name = "tag")
private String tag;
@JSONField(name = "content")
private String content;
@JSONField(name = "actions")
private List<ActionsDTO> actions;
@Data
public static class ActionsDTO {
@JSONField(name = "tag")
private String tag;
@JSONField(name = "placeholder")
private PlaceholderDTO placeholder;
@JSONField(name = "options")
private List<OptionsDTO> options;
@Data
public static class PlaceholderDTO {
@JSONField(name = "tag")
private String tag;
@JSONField(name = "content")
private String content;
}
@Data
public static class OptionsDTO {
@JSONField(name = "text")
private TextDTO text;
@JSONField(name = "value")
private String value;
@Data
public static class TextDTO {
@JSONField(name = "tag")
private String tag;
@JSONField(name = "content")
private String content;
}
}
}
}
}
package com.pipihelper.project.feishu.dto;
import lombok.Data;
import java.io.Serializable;
/**
* @Description: TODO
* @author: charles
* @date: 2022年05月31日 19:16
*/
@Data
public class FeiShuMsgCardEventDTO implements Serializable {
/**
* open_message_id : om_68847045627958c15f8393cd65f7bf2c
* tenant_key : 1120c5cd9d915758
* open_id : ou_d75bfe889720de803aea8099c7f2f837
* user_id : gf613cf5
* action : {"tag":"select_static","value":{"key":"level"},"option":"高"}
* token : c-ee8d54e0dbef055760d6f9fd171576c3930ee7cd
*/
private String open_message_id;
private String tenant_key;
private String open_id;
private String user_id;
private ActionBean action;
private String token;
@Data
public static class ActionBean{
/**
* tag : select_static
* value : {"key":"level"}
* option : 高
*/
private String tag;
private ValueBean value;
private String option;
private String timezone;
@Data
public static class ValueBean{
/**
* key : level
*/
private String key;
}
}
}
package com.pipihelper.project.feishu.dto;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import lombok.NoArgsConstructor;
@NoArgsConstructor
@Data
public class FeiShuResultDTO<T> {
@JsonProperty("code")
private Integer code;
@JsonProperty("msg")
private String msg;
@JsonProperty("data")
private FeiShuDataDTO<T> data;
}
package com.pipihelper.project.feishu.dto.chat;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import lombok.NoArgsConstructor;
@NoArgsConstructor
@Data
public class FeiShuChatDTO {
@JsonProperty("chat_id")
private String chatId;
@JsonProperty("avatar")
private String avatar;
@JsonProperty("name")
private String name;
@JsonProperty("description")
private String description;
@JsonProperty("owner_id")
private String ownerId;
@JsonProperty("owner_id_type")
private String ownerIdType;
@JsonProperty("external")
private Boolean external;
@JsonProperty("tenant_key")
private String tenantKey;
}
package com.pipihelper.project.feishu.dto.department;
import com.alibaba.fastjson.annotation.JSONField;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
@NoArgsConstructor
@Data
public class FeiShuDepartmentDTO {
@JSONField(name = "name")
private String name;
@JSONField(name = "i18n_name")
private I18nNameDTO i18nName;
@JSONField(name = "parent_department_id")
private String parentDepartmentId;
@JSONField(name = "department_id")
private String departmentId;
@JSONField(name = "open_department_id")
private String openDepartmentId;
@JSONField(name = "leader_user_id")
private String leaderUserId;
@JSONField(name = "chat_id")
private String chatId;
@JSONField(name = "order")
private String order;
@JSONField(name = "unit_ids")
private List<String> unitIds;
@JSONField(name = "member_count")
private Integer memberCount;
@JSONField(name = "status")
private StatusDTO status;
}
package com.pipihelper.project.feishu.dto.department;
import com.alibaba.fastjson.annotation.JSONField;
import lombok.Data;
import lombok.NoArgsConstructor;
@NoArgsConstructor
@Data
public class I18nNameDTO {
@JSONField(name = "zh_cn")
private String zhCn;
@JSONField(name = "ja_jp")
private String jaJp;
@JSONField(name = "en_us")
private String enUs;
}
package com.pipihelper.project.feishu.dto.department;
import com.alibaba.fastjson.annotation.JSONField;
import lombok.Data;
import lombok.NoArgsConstructor;
@NoArgsConstructor
@Data
public class StatusDTO {
@JSONField(name = "is_deleted")
private Boolean isDeleted;
}
package com.pipihelper.project.feishu.dto.doc;
import com.alibaba.fastjson.annotation.JSONField;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @author xiongjian
* @date 2022/7/4
*/
@NoArgsConstructor
@Data
public class DocDTO <T>{
@JSONField(name = "id")
private String id;
@JSONField(name = "record_id")
private String recordId;
@JSONField(name = "fields")
private T fields;
}
package com.pipihelper.project.feishu.dto.doc;
import com.alibaba.fastjson.annotation.JSONField;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
/**
* @author xiongjian
* @date 2022/7/4
*/
@NoArgsConstructor
@Data
public class FieldsDTO {
@JSONField(name = "邮箱")
private String mail;
@JSONField(name = "姓名")
private String name;
@JSONField(name = "人员")
private List<PeopleDTO> people;
@JSONField(name = "手机")
private String phone;
@JSONField(name = "userId")
private String userId;
@JSONField(name = "更新时间")
private Long updateTime;
@JSONField(name = "token")
private String token;
@JSONField(name = "任务名")
private String taskName;
@JSONField(name = "任务描述")
private String taskDescription;
@JSONField(name = "截止时间")
private Long due;
@JSONField(name = "任务来源")
private String origin;
@JSONField(name = "执行人")
private String collaborators;
@JSONField(name = "关注人")
private String followers;
@JSONField(name = "消息")
private String msg;
@JSONField(name = "消息人")
private String receiverIds;
@JSONField(name = "状态")
private String statues;
@JSONField(name = "备注")
private String note;
}
package com.pipihelper.project.feishu.dto.doc;
import com.alibaba.fastjson.annotation.JSONField;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @author xiongjian
* @date 2022/7/4
*/
@NoArgsConstructor
@Data
public class PeopleDTO {
@JSONField(name = "id")
private String id;
@JSONField(name = "email")
private String email;
@JSONField(name = "name")
private String name;
@JSONField(name = "en_name")
private String enName;
public PeopleDTO(String id) {
this.id = id;
}
}
package com.pipihelper.project.feishu.dto.employee;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import lombok.NoArgsConstructor;
@NoArgsConstructor
@Data
public class CertOfMeritDTO {
@JsonProperty("id")
private String id;
@JsonProperty("mime_type")
private String mimeType;
@JsonProperty("name")
private String name;
@JsonProperty("size")
private Integer size;
}
package com.pipihelper.project.feishu.dto.employee;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import lombok.NoArgsConstructor;
@NoArgsConstructor
@Data
public class ContractCompanyDTO {
@JsonProperty("id")
private Long id;
@JsonProperty("name")
private String name;
}
package com.pipihelper.project.feishu.dto.employee;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import lombok.NoArgsConstructor;
@NoArgsConstructor
@Data
public class CustomFieldsDTO {
@JsonProperty("key")
private String key;
@JsonProperty("label")
private String label;
@JsonProperty("type")
private String type;
@JsonProperty("value")
private String value;
}
package com.pipihelper.project.feishu.dto.employee;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import lombok.NoArgsConstructor;
@NoArgsConstructor
@Data
public class DiplomaPhotoDTO {
@JsonProperty("id")
private String id;
@JsonProperty("mime_type")
private String mimeType;
@JsonProperty("name")
private String name;
@JsonProperty("size")
private Integer size;
}
package com.pipihelper.project.feishu.dto.employee;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import lombok.NoArgsConstructor;
@NoArgsConstructor
@Data
public class EducationDTO {
@JsonProperty("level")
private Integer level;
@JsonProperty("school")
private String school;
@JsonProperty("major")
private String major;
@JsonProperty("degree")
private Integer degree;
@JsonProperty("start")
private String start;
@JsonProperty("end")
private String end;
}
package com.pipihelper.project.feishu.dto.employee;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import lombok.NoArgsConstructor;
@NoArgsConstructor
@Data
public class EmergencyContactDTO {
@JsonProperty("name")
private String name;
@JsonProperty("relationship")
private Integer relationship;
@JsonProperty("mobile")
private String mobile;
}
package com.pipihelper.project.feishu.dto.employee;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
@NoArgsConstructor
@Data
public class FeiShuEmployeeDTO {
@JsonProperty("user_id")
private String userId;
@JsonProperty("system_fields")
private SystemFieldsDTO systemFields;
@JsonProperty("custom_fields")
private List<CustomFieldsDTO> customFields;
}
package com.pipihelper.project.feishu.dto.employee;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import lombok.NoArgsConstructor;
@NoArgsConstructor
@Data
public class FormerWorkExpDTO {
@JsonProperty("company")
private String company;
@JsonProperty("department")
private String department;
@JsonProperty("job")
private String job;
@JsonProperty("start")
private String start;
@JsonProperty("end")
private String end;
@JsonProperty("description")
private String description;
}
package com.pipihelper.project.feishu.dto.employee;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import lombok.NoArgsConstructor;
@NoArgsConstructor
@Data
public class GraduationCertDTO {
@JsonProperty("id")
private String id;
@JsonProperty("mime_type")
private String mimeType;
@JsonProperty("name")
private String name;
@JsonProperty("size")
private Integer size;
}
package com.pipihelper.project.feishu.dto.employee;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import lombok.NoArgsConstructor;
@NoArgsConstructor
@Data
public class HighestLevelOfEduDTO {
@JsonProperty("level")
private Integer level;
@JsonProperty("school")
private String school;
@JsonProperty("major")
private String major;
@JsonProperty("degree")
private Integer degree;
@JsonProperty("start")
private String start;
@JsonProperty("end")
private String end;
}
package com.pipihelper.project.feishu.dto.employee;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import lombok.NoArgsConstructor;
@NoArgsConstructor
@Data
public class IdPhotoDTO {
@JsonProperty("id")
private String id;
@JsonProperty("mime_type")
private String mimeType;
@JsonProperty("name")
private String name;
@JsonProperty("size")
private Integer size;
}
package com.pipihelper.project.feishu.dto.employee;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import lombok.NoArgsConstructor;
@NoArgsConstructor
@Data
public class IdPhotoEmSideDTO {
@JsonProperty("id")
private String id;
@JsonProperty("mime_type")
private String mimeType;
@JsonProperty("name")
private String name;
@JsonProperty("size")
private Integer size;
}
package com.pipihelper.project.feishu.dto.employee;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import lombok.NoArgsConstructor;
@NoArgsConstructor
@Data
public class IdPhotoPoSideDTO {
@JsonProperty("id")
private String id;
@JsonProperty("mime_type")
private String mimeType;
@JsonProperty("name")
private String name;
@JsonProperty("size")
private Integer size;
}
package com.pipihelper.project.feishu.dto.employee;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import lombok.NoArgsConstructor;
@NoArgsConstructor
@Data
public class JobDTO {
@JsonProperty("id")
private Long id;
@JsonProperty("name")
private String name;
}
package com.pipihelper.project.feishu.dto.employee;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import lombok.NoArgsConstructor;
@NoArgsConstructor
@Data
public class JobLevelDTO {
@JsonProperty("id")
private Long id;
@JsonProperty("name")
private String name;
}
package com.pipihelper.project.feishu.dto.employee;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import lombok.NoArgsConstructor;
@NoArgsConstructor
@Data
public class ManagerDTO {
@JsonProperty("user_id")
private String userId;
@JsonProperty("name")
private String name;
@JsonProperty("en_name")
private String enName;
}
package com.pipihelper.project.feishu.dto.employee;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import lombok.NoArgsConstructor;
@NoArgsConstructor
@Data
public class NativeRegionDTO {
@JsonProperty("iso_code")
private String isoCode;
@JsonProperty("name")
private String name;
}
package com.pipihelper.project.feishu.dto.employee;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import lombok.NoArgsConstructor;
@NoArgsConstructor
@Data
public class OffboardingFileDTO {
@JsonProperty("id")
private String id;
@JsonProperty("mime_type")
private String mimeType;
@JsonProperty("name")
private String name;
@JsonProperty("size")
private Integer size;
}
package com.pipihelper.project.feishu.dto.employee;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import lombok.NoArgsConstructor;
@NoArgsConstructor
@Data
public class PrimaryEmergencyContactDTO {
@JsonProperty("name")
private String name;
@JsonProperty("relationship")
private Integer relationship;
@JsonProperty("mobile")
private String mobile;
}
package com.pipihelper.project.feishu.dto.employee;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
@NoArgsConstructor
@Data
public class SystemFieldsDTO {
@JsonProperty("name")
private String name;
@JsonProperty("en_name")
private String enName;
@JsonProperty("email")
private String email;
@JsonProperty("mobile")
private String mobile;
@JsonProperty("department_id")
private String departmentId;
@JsonProperty("manager")
private ManagerDTO manager;
@JsonProperty("job")
private JobDTO job;
@JsonProperty("job_level")
private JobLevelDTO jobLevel;
@JsonProperty("work_location")
private WorkLocationDTO workLocation;
@JsonProperty("gender")
private Integer gender;
@JsonProperty("birthday")
private String birthday;
@JsonProperty("native_region")
private NativeRegionDTO nativeRegion;
@JsonProperty("ethnicity")
private Integer ethnicity;
@JsonProperty("marital_status")
private Integer maritalStatus;
@JsonProperty("political_status")
private Integer politicalStatus;
@JsonProperty("entered_workforce_date")
private String enteredWorkforceDate;
@JsonProperty("id_type")
private Integer idType;
@JsonProperty("id_number")
private String idNumber;
@JsonProperty("hukou_type")
private Integer hukouType;
@JsonProperty("hukou_location")
private String hukouLocation;
@JsonProperty("bank_account_number")
private String bankAccountNumber;
@JsonProperty("bank_name")
private String bankName;
@JsonProperty("social_security_account")
private String socialSecurityAccount;
@JsonProperty("provident_fund_account")
private String providentFundAccount;
@JsonProperty("employee_no")
private String employeeNo;
@JsonProperty("employee_type")
private Integer employeeType;
@JsonProperty("status")
private Integer status;
@JsonProperty("hire_date")
private String hireDate;
@JsonProperty("probation_months")
private Integer probationMonths;
@JsonProperty("conversion_date")
private String conversionDate;
@JsonProperty("application")
private Integer application;
@JsonProperty("application_status")
private Integer applicationStatus;
@JsonProperty("last_day")
private String lastDay;
@JsonProperty("departure_type")
private Integer departureType;
@JsonProperty("departure_reason")
private Integer departureReason;
@JsonProperty("departure_notes")
private String departureNotes;
@JsonProperty("contract_company")
private ContractCompanyDTO contractCompany;
@JsonProperty("contract_type")
private Integer contractType;
@JsonProperty("contract_start_date")
private String contractStartDate;
@JsonProperty("contract_expiration_date")
private String contractExpirationDate;
@JsonProperty("contract_sign_times")
private Integer contractSignTimes;
@JsonProperty("personal_email")
private String personalEmail;
@JsonProperty("family_address")
private String familyAddress;
@JsonProperty("primary_emergency_contact")
private PrimaryEmergencyContactDTO primaryEmergencyContact;
@JsonProperty("emergency_contact")
private List<EmergencyContactDTO> emergencyContact;
@JsonProperty("highest_level_of_edu")
private HighestLevelOfEduDTO highestLevelOfEdu;
@JsonProperty("education")
private List<EducationDTO> education;
@JsonProperty("former_work_exp")
private FormerWorkExpDTO formerWorkExp;
@JsonProperty("work_exp")
private List<WorkExpDTO> workExp;
@JsonProperty("id_photo_po_side")
private List<IdPhotoPoSideDTO> idPhotoPoSide;
@JsonProperty("id_photo_em_side")
private List<IdPhotoEmSideDTO> idPhotoEmSide;
@JsonProperty("id_photo")
private List<IdPhotoDTO> idPhoto;
@JsonProperty("diploma_photo")
private List<DiplomaPhotoDTO> diplomaPhoto;
@JsonProperty("graduation_cert")
private List<GraduationCertDTO> graduationCert;
@JsonProperty("cert_of_merit")
private List<CertOfMeritDTO> certOfMerit;
@JsonProperty("offboarding_file")
private List<OffboardingFileDTO> offboardingFile;
@JsonProperty("cancel_onboarding_reason")
private Integer cancelOnboardingReason;
@JsonProperty("cancel_onboarding_notes")
private String cancelOnboardingNotes;
@JsonProperty("employee_form_status")
private Integer employeeFormStatus;
@JsonProperty("create_time")
private Long createTime;
@JsonProperty("update_time")
private Long updateTime;
}
package com.pipihelper.project.feishu.dto.employee;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import lombok.NoArgsConstructor;
@NoArgsConstructor
@Data
public class WorkExpDTO {
@JsonProperty("company")
private String company;
@JsonProperty("department")
private String department;
@JsonProperty("job")
private String job;
@JsonProperty("start")
private String start;
@JsonProperty("end")
private String end;
@JsonProperty("description")
private String description;
}
package com.pipihelper.project.feishu.dto.employee;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import lombok.NoArgsConstructor;
@NoArgsConstructor
@Data
public class WorkLocationDTO {
@JsonProperty("id")
private Long id;
@JsonProperty("name")
private String name;
}
package com.pipihelper.project.feishu.dto.msg;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.Map;
@NoArgsConstructor
@Data
public class ContentDTO {
@JsonProperty("text")
private String text;
public ContentDTO(String text) {
this.text = text;
}
}
package com.pipihelper.project.feishu.dto.msg;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
import java.util.Map;
@NoArgsConstructor
@Data
public class FeiShuBatchMsgDTO {
@JsonProperty("department_ids")
private List<String> departmentIds;
@JsonProperty("open_ids")
private List<String> openIds;
@JsonProperty("user_ids")
private List<String> userIds;
@JsonProperty("union_ids")
private List<String> unionIds;
@JsonProperty("msg_type")
private String msgType;
@JsonProperty("content")
private ContentDTO content;
@JsonProperty("card")
private Map card;
}
package com.pipihelper.project.feishu.dto.msg;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import lombok.NoArgsConstructor;
@NoArgsConstructor
@Data
public class FeiShuMsgDTO {
@JsonProperty("receive_id")
private String receiveId;
@JsonProperty("content")
private String content;
@JsonProperty("msg_type")
private String msgType;
}
package com.pipihelper.project.feishu.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.ArrayList;
import java.util.List;
@Getter
@AllArgsConstructor
public enum CardElements {
APP_TYPE("appType","select_static","APP类型"),
MODULE("module","select_static","业务场景"),
LEVEL("level","select_static","优先级"),
ASSIGN("assign","select_person","指派处理人"),
VERIFY("verify","select_person","转发给其他值班人"),
HANDLE("handle","button","我来"),
FINISH_TIME("finishTime","date_picker","预计完成时间"),
FINISH("finish","button","完成"),
FILE_FINISH("fileFinish","overflow","完成|完成并归档");
// FILED("button","","","");
private String action;
private String tag;
private String msg;
}
package com.pipihelper.project.feishu.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
@Getter
@AllArgsConstructor
public enum ModuleType {
FRIENDS(1,"娱乐"),
PLAY(2,"游戏"),
MIDDLEWARE(3,"中台"),
OTHER(4,"其他"),
NONE(0,"未指定");
private Integer type;
private String msg;
public static Integer getTypeByMsg(String msg) {
for (ModuleType moduleType : ModuleType.values()) {
if (moduleType.getMsg().equals(msg)) {
return moduleType.getType();
}
}
return null;
}
public static String getMsgByType(Integer type) {
for (ModuleType moduleType : ModuleType.values()) {
if (moduleType.getType().equals(type)) {
return moduleType.getMsg();
}
}
return null;
}
}
package com.pipihelper.project.feishu.enums;
import com.pipihelper.project.core.ServiceException;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@Getter
@AllArgsConstructor
public enum MsgCard {
//CardElements.APP_TYPE, CardElements.MODULE, CardElements.LEVEL,CardElements.ASSIGN,CardElements.VERIFY, CardElements.HANDLE, CardElements.FINISH_TIME, CardElements.FINISH
TESTER_CARD_01("tester_0", Arrays.asList(CardElements.VERIFY, CardElements.HANDLE), "待值班确认", 1),
TESTER_CARD_02("tester_1", Arrays.asList(CardElements.LEVEL, CardElements.ASSIGN), "待值班指派", 1),
TESTER_CARD_03("tester_2", Arrays.asList(CardElements.FILE_FINISH), "负责人已完成待值班已确认", 1),//问题已完成,负责人未点完成,不允许测试点完成
TESTER_CARD_04("tester_3", Arrays.asList(CardElements.FINISH_TIME,CardElements.FILE_FINISH), "值班负责任务",4),//负责人是自己的时候,可以点击填写完成时间
TESTER_CARD_05("tester_4", new ArrayList<>(), "负责人已确认待解决",4),//开发确认后,测试侧卡片
CODER_CARD_01("coder_0", Arrays.asList(CardElements.ASSIGN, CardElements.HANDLE), "待负责人确认",1),
CODER_CARD_02("coder_1", Arrays.asList(CardElements.FINISH_TIME,CardElements.FINISH), "负责人已确认待解决",4),//负责人只能完成,不能归档
FILE("file", new ArrayList<>(), "卡片固化",null);
private String type;
private List<CardElements> elements;
private String msg;
private Integer interval;
public static List<CardElements> getElementsByType(String type) {
for (MsgCard msgCard : MsgCard.values()) {
if (msgCard.getType().equals(type)) {
return msgCard.getElements();
}
}
throw new ServiceException("类型不匹配");
}
public static Integer getIntervalByType(String type) {
for (MsgCard msgCard : MsgCard.values()) {
if (msgCard.getType().equals(type)) {
return msgCard.getInterval();
}
}
throw new ServiceException("类型不匹配");
}
}
package com.pipihelper.project.feishu.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* @Description: TODO
* @author: charles
* @date: 2022年06月08日 14:21
*/
@Getter
@AllArgsConstructor
public enum NoticeType {
NONE(0,"未发送提醒"),
URGENT_APP(1,"app内加急"),
URGENT_SMS(2,"短信加急"),
URGENT_PHONE(3,"电话加急"),
URGENT_APP_24(4,"app内加急"),
URGENT_SMS_24(5,"短信加急"),
URGENT_PHONE_24(6,"电话加急");
private Integer type;
private String msg;
}
package com.pipihelper.project.feishu.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
@Getter
@AllArgsConstructor
public enum SendMsgBusinessType {
/***
* 机器人发送消息的消息类型
*/
FEEDBACKS(0,"反馈群反馈问题"),
FEEDBACKS_DATA(1,""),
TALK(2,"一般性对话"),
DUTY_NOTICE(3, "值班提醒"),
HAR(4,"解析har文件"),
SYNC_DICT(5,"同步字典"),
TEST_DATA(6,"处理测试数据"),
DEFAULT(-1,"");
private Integer businessType;
private String description;
}
package com.pipihelper.project.feishu.service;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import com.pipihelper.project.core.ServiceException;
import com.pipihelper.project.feishu.dto.FeiShuConfig;
import com.pipihelper.project.feishu.dto.FeiShuCreateTaskDTO;
import com.pipihelper.project.feishu.dto.FeiShuResultDTO;
import com.pipihelper.project.feishu.dto.chat.FeiShuChatDTO;
import com.pipihelper.project.feishu.dto.department.FeiShuDepartmentDTO;
import com.pipihelper.project.feishu.dto.doc.DocDTO;
import com.pipihelper.project.feishu.dto.doc.FieldsDTO;
import com.pipihelper.project.feishu.dto.employee.FeiShuEmployeeDTO;
import com.pipihelper.project.feishu.dto.msg.FeiShuBatchMsgDTO;
import com.pipihelper.project.feishu.dto.msg.FeiShuMsgDTO;
//import org.springframework.http.MediaType;
import lombok.extern.slf4j.Slf4j;
import okhttp3.*;
import org.apache.commons.lang3.StringUtils;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;
import javax.annotation.Resource;
import java.lang.reflect.Type;
import java.time.Duration;
import java.util.ArrayList;
import java.util.List;
/**
* @Description: TODO
* @author: charles
* @date: 2022年05月19日 15:40
*/
@Service
@Transactional
@Slf4j
public class FeiShuApiService {
@Resource
private FeiShuConfig feiShuConfig;
private static ObjectMapper objectMapper = new ObjectMapper();
private final Cache<Object, String> FEISHU_CACHE = Caffeine.newBuilder()
.maximumSize(100)
.expireAfterWrite(Duration.ofHours(2))
.build();
private final Cache<String, List<FeiShuEmployeeDTO>> EMPLOYEE_LIST_CACHE = Caffeine.newBuilder()
.maximumSize(100)
.expireAfterWrite(Duration.ofHours(2))
.build();
public String getTenantToken(String appName) {
return FEISHU_CACHE.get("tenantAccessToken" + appName, key -> setTenantAccessToken(appName));
}
public String setTenantAccessToken(String appName) {
String api = "/auth/v3/tenant_access_token/internal";
RestTemplate restTemplate = new RestTemplate();
JSONObject params = new JSONObject();
params.put("app_id", feiShuConfig.getAppConfigMap().get(appName).getAppId());
params.put("app_secret", feiShuConfig.getAppConfigMap().get(appName).getAppSecret());
ResponseEntity<JSONObject> responseEntity = restTemplate.postForEntity(feiShuConfig.getFeiShuOpenApiHost() + api, params, JSONObject.class);
System.out.println(responseEntity.getBody().get("tenant_access_token").toString());
return responseEntity.getBody().get("tenant_access_token").toString();
}
/**
* @param /https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/im-v1/message/create
* @param sendMsg
* @return
*/
public JSONObject sendMsg(Integer businessType, String receive_id_type, JSONObject sendMsg, String appName) {
System.out.println(sendMsg.toString());
//发送消息
String api = "/im/v1/messages?receive_id_type={receive_id_type}";
RestTemplate restTemplate = new RestTemplate();
HttpHeaders headers = new HttpHeaders();
headers.set("Authorization", "Bearer " + getTenantToken(appName));
headers.set("Content-Type", "application/json; charset=utf-8");
HttpEntity<String> requestEntity = new HttpEntity<>(sendMsg.toString(), headers);
String url = feiShuConfig.getFeiShuOpenApiHost() + api;
try {
ResponseEntity<JSONObject> responseEntity = restTemplate.postForEntity(url, requestEntity, JSONObject.class, receive_id_type);
if (responseEntity.getBody().getJSONObject("data") == null) {
return null;
}
log.info("业务类型:{} ,消息发送成功,接收人: {}", businessType, sendMsg.getString("receive_id"));
return responseEntity.getBody();
} catch (Exception e) {
log.error("飞书:" + api + "接口调用失败" + "\n" + e);
throw new ServiceException("飞书:" + api + "接口调用失败" + "\n" + e);
}
}
@Async
public JSONObject replyMsg(String message_id, JSONObject replyMsg, String appName) {
System.out.println(replyMsg.toString());
String api = "/im/v1/messages/{message_id}/reply";
RestTemplate restTemplate = new RestTemplate();
HttpHeaders headers = new HttpHeaders();
headers.set("Authorization", "Bearer " + getTenantToken(appName));
headers.set("Content-Type", "application/json; charset=utf-8");
HttpEntity<String> requestEntity = new HttpEntity<>(replyMsg.toString(), headers);
String url = feiShuConfig.getFeiShuOpenApiHost() + api;
try {
ResponseEntity<JSONObject> responseEntity = restTemplate.postForEntity(url, requestEntity, JSONObject.class, message_id);
log.info("飞书接口 {} 调用成功,返回值:{}", api, responseEntity.getBody());
return responseEntity.getBody();
} catch (Exception e) {
throw new ServiceException("飞书:" + api + "接口调用失败" + "\n" + e);
}
}
@Async
public JSONObject patchMsg(String message_id, JSONObject content, String appName) {
String api = "/im/v1/messages/";
OkHttpClient client = new OkHttpClient();
String url = feiShuConfig.getFeiShuOpenApiHost() + api;
RequestBody requestBody = RequestBody.create(content.toString(), MediaType.parse("application/json"));
Request request = new Request.Builder()
.patch(requestBody)
.url(url + message_id)
.addHeader("Authorization", "Bearer " + getTenantToken(appName))
.addHeader("Content-Type", "application/json; charset=utf-8")
.build();
try {
Response response = client.newCall(request).execute();
String responseString = response.body().string();
log.info("飞书接口 {} 调用成功,返回值:{}", api, responseString);
return JSONObject.parseObject(responseString);
} catch (Exception e) {
throw new ServiceException("飞书:" + api + "接口调用失败" + "\n" + e);
}
}
@Async
public JSONObject patchUrgentApp(String message_id, JSONObject content, String appName) {
String api = "/im/v1/messages/" + message_id + "/urgent_sms?user_id_type=open_id";
OkHttpClient client = new OkHttpClient();
String url = feiShuConfig.getFeiShuOpenApiHost() + api;
RequestBody requestBody = RequestBody.create(content.toString(), MediaType.parse("application/json"));
Request request = new Request.Builder()
.patch(requestBody)
.url(url)
.addHeader("Authorization", "Bearer " + getTenantToken(appName))
.addHeader("Content-Type", "application/json; charset=utf-8")
.build();
try {
Response response = client.newCall(request).execute();
String responseString = response.body().string();
System.out.println(responseString);
return JSONObject.parseObject(responseString);
} catch (Exception e) {
System.out.println("飞书:" + api + "接口调用失败" + "\n" + e);
return null;
}
}
@Async
public JSONObject patchUrgentSms(String message_id, JSONObject content, String appName) {
String api = "/im/v1/messages/" + message_id + "/urgent_app?user_id_type=open_id";
OkHttpClient client = new OkHttpClient();
String url = feiShuConfig.getFeiShuOpenApiHost() + api;
RequestBody requestBody = RequestBody.create(content.toString(), MediaType.parse("application/json"));
Request request = new Request.Builder()
.patch(requestBody)
.url(url)
.addHeader("Authorization", "Bearer " + getTenantToken(appName))
.addHeader("Content-Type", "application/json; charset=utf-8")
.build();
try {
Response response = client.newCall(request).execute();
String responseString = response.body().string();
System.out.println(responseString);
return JSONObject.parseObject(responseString);
} catch (Exception e) {
System.out.println("飞书:" + api + "接口调用失败" + "\n" + e);
return null;
}
}
// @Async
public String createTask(FeiShuCreateTaskDTO taskMsg, String openId, String appName) {
log.info("调用创建任务接口,task: {}", taskMsg.toString());
String api = "/task/v1/tasks";
RestTemplate restTemplate = new RestTemplate();
HttpHeaders headers = new HttpHeaders();
headers.set("Authorization", "Bearer " + getTenantToken(appName));
// headers.set("Authorization", "Bearer " + "t-d3db00058d854b3c004ffdf6453fd3e169c82043");
headers.set("Content-Type", "application/json; charset=utf-8");
HttpEntity<String> requestEntity = new HttpEntity<>(JSON.toJSONString(taskMsg), headers);
String url = feiShuConfig.getFeiShuOpenApiHost() + api;
// String url = "https://open.feishu.cn/open-apis" + api;
try {
ResponseEntity<JSONObject> responseEntity = restTemplate.postForEntity(url, requestEntity, JSONObject.class);
String taskId = responseEntity.getBody().getJSONObject("data").getJSONObject("task").getString("id");
collaborators(taskId, openId, appName);
return taskId;
} catch (Exception e) {
throw new ServiceException("飞书:" + api + "接口调用失败" + "\n" + e);
}
}
public JSONObject collaborators(String taskId, String openId, String appName) {
if (StringUtils.isBlank(openId)) {
return null;
}
String api = "/task/v1/tasks/{taskId}/collaborators";
RestTemplate restTemplate = new RestTemplate();
JSONObject param = new JSONObject();
param.put("id", openId);
HttpHeaders headers = new HttpHeaders();
headers.set("Authorization", "Bearer " + getTenantToken(appName));
headers.set("Content-Type", "application/json; charset=utf-8");
HttpEntity<String> requestEntity = new HttpEntity<>(param.toString(), headers);
String url = feiShuConfig.getFeiShuOpenApiHost() + api;
try {
ResponseEntity<JSONObject> responseEntity = restTemplate.postForEntity(url, requestEntity, JSONObject.class, taskId);
return responseEntity.getBody();
} catch (Exception e) {
throw new ServiceException("飞书:" + api + "接口调用失败" + "\n" + e);
}
}
public byte[] getMessageFile(String messageId, String fileKey, String type, String appName) {
String api = "/im/v1/messages/{messageId}/resources/{fileKey}/?type={type}";
RestTemplate restTemplate = new RestTemplate();
HttpHeaders headers = new HttpHeaders();
headers.set("Authorization", "Bearer " + getTenantToken(appName));
HttpEntity<MultiValueMap<String, Object>> requestEntity = new HttpEntity<>(headers);
String url = feiShuConfig.getFeiShuOpenApiHost() + api;
try {
ResponseEntity<byte[]> responseEntity = restTemplate.exchange(url, HttpMethod.GET, requestEntity, byte[].class, messageId, fileKey, type);
return responseEntity.getBody();
} catch (Exception e) {
throw new ServiceException("飞书:" + api + "接口调用失败");
}
}
//获取指定部门下的员工信息
public JSONObject getUser(String department_id, String appName) {
String api = "/contact/v3/users/find_by_department?department_id={department_id}";
RestTemplate restTemplate = new RestTemplate();
HttpHeaders headers = new HttpHeaders();
headers.set("Authorization", "Bearer " + getTenantToken(appName));
headers.set("Content-Type", "application/json; charset=utf-8");
HttpEntity<String> requestEntity = new HttpEntity<>(headers);
String url = feiShuConfig.getFeiShuOpenApiHost() + api;
try {
ResponseEntity<JSONObject> responseEntity = restTemplate.exchange(url, HttpMethod.GET, requestEntity, JSONObject.class, department_id);
return responseEntity.getBody();
} catch (Exception e) {
throw new ServiceException("飞书:" + api + "接口调用失败" + "\n" + e);
}
}
//下载文件
public byte[] downloadFile(String filePath, String appName) {
String api = "/im/v1/files/{filePath}";
RestTemplate restTemplate = new RestTemplate();
HttpHeaders headers = new HttpHeaders();
headers.set("Authorization", "Bearer " + getTenantToken(appName));
HttpEntity<String> requestEntity = new HttpEntity<>(headers);
String url = feiShuConfig.getFeiShuOpenApiHost() + api;
try {
ResponseEntity<byte[]> responseEntity = restTemplate.exchange(url, HttpMethod.GET, requestEntity, byte[].class, filePath);
return responseEntity.getBody();
} catch (Exception e) {
throw new ServiceException("飞书:" + api + "接口调用失败" + "\n" + e);
}
}
//上传文件
public String uploadFile(String file_type, String file_name, Integer duration, byte[] file, String appName) {
String api = "/im/v1/files";
RestTemplate restTemplate = new RestTemplate();
HttpHeaders headers = new HttpHeaders();
headers.set("Authorization", "Bearer " + getTenantToken(appName));
headers.setContentType(org.springframework.http.MediaType.MULTIPART_FORM_DATA);
MultiValueMap<String, Object> param = new LinkedMultiValueMap<>();
param.add("file_type", file_type);
param.add("file_name", file_name);
param.add("duration", duration);
param.add("file", file);
HttpEntity<MultiValueMap<String, Object>> requestEntity = new HttpEntity<>(param, headers);
String url = feiShuConfig.getFeiShuOpenApiHost() + api;
try {
ResponseEntity<JSONObject> responseEntity = restTemplate.postForEntity(url, requestEntity, JSONObject.class);
return responseEntity.getBody().getJSONObject("data").getString("file_key");
} catch (Exception e) {
throw new ServiceException("飞书:" + api + "接口调用失败" + "\n" + e);
}
}
//获取应用有权限的所有部门
public JSONObject getDepartment(String appName) {
String api = "/contact/v3/departments?fetch_child=true";
RestTemplate restTemplate = new RestTemplate();
HttpHeaders headers = new HttpHeaders();
headers.set("Authorization", "Bearer " + getTenantToken(appName));
headers.set("Content-Type", "application/json; charset=utf-8");
HttpEntity<String> requestEntity = new HttpEntity<>(headers);
String url = feiShuConfig.getFeiShuOpenApiHost() + api;
try {
ResponseEntity<JSONObject> responseEntity = restTemplate.exchange(url, HttpMethod.GET, requestEntity, JSONObject.class);
return responseEntity.getBody();
} catch (Exception e) {
throw new ServiceException("飞书:" + api + "接口调用失败" + "\n" + e);
}
}
/**
* 获取飞书人事花名单信息
* 文档:https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/ehr/ehr-v1/employee/list
*
* @return
*/
public List<FeiShuEmployeeDTO> getEmployeeList(String appName) {
return EMPLOYEE_LIST_CACHE.get("employeeList", s -> setEmployeeList(appName));
}
private List<FeiShuEmployeeDTO> setEmployeeList(String appName) {
String api = "/ehr/v1/employees?view=full&status=2&status=4&page_size=100";
RestTemplate restTemplate = new RestTemplate();
HttpHeaders headers = new HttpHeaders();
headers.set("Authorization", "Bearer " + getTenantToken(appName));
headers.set("Content-Type", "application/json; charset=utf-8");
HttpEntity<String> requestEntity = new HttpEntity<>(headers);
String url = feiShuConfig.getFeiShuOpenApiHost() + api;
try {
ResponseEntity<String> responseEntity = restTemplate.exchange(url, HttpMethod.GET, requestEntity, String.class);
Type type = new TypeReference<FeiShuResultDTO<FeiShuEmployeeDTO>>() {
}.getType();
FeiShuResultDTO feiShuResultDTO = JSONObject.parseObject(responseEntity.getBody(), type);
List<FeiShuEmployeeDTO> feiShuEmployeeDTOList = new ArrayList<>();
feiShuEmployeeDTOList.addAll(feiShuResultDTO.getData().getItems());
while (feiShuResultDTO.getData().getItems().size() >= 100) {
ResponseEntity<String> next = restTemplate.exchange(url + "&page_token=" + feiShuResultDTO.getData().getPageToken(), HttpMethod.GET, requestEntity, String.class);
feiShuResultDTO = JSONObject.parseObject(next.getBody(), type);
feiShuEmployeeDTOList.addAll(feiShuResultDTO.getData().getItems());
}
log.info("花名册获取完成,总计 {} 人", feiShuEmployeeDTOList.size());
return feiShuEmployeeDTOList;
} catch (Exception e) {
throw new ServiceException("飞书:" + api + "接口调用失败" + "\n" + e);
}
}
/**
* 获取机器人所在群信息
* https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/im-v1/chat/list
*
* @return
*/
public List<FeiShuChatDTO> getChatList(String appName) {
String api = "/im/v1/chats";
RestTemplate restTemplate = new RestTemplate();
HttpHeaders headers = new HttpHeaders();
headers.set("Authorization", "Bearer " + getTenantToken(appName));
headers.set("Content-Type", "application/json; charset=utf-8");
HttpEntity<String> requestEntity = new HttpEntity<>(headers);
String url = feiShuConfig.getFeiShuOpenApiHost() + api;
try {
ResponseEntity<String> responseEntity = restTemplate.exchange(url, HttpMethod.GET, requestEntity, String.class);
Type type = new TypeReference<FeiShuResultDTO<FeiShuChatDTO>>() {
}.getType();
System.out.println(responseEntity.getBody());
FeiShuResultDTO feiShuResultDTO = JSONObject.parseObject(responseEntity.getBody(), type);
return feiShuResultDTO.getData().getItems();
} catch (Exception e) {
throw new ServiceException("飞书:" + api + "接口调用失败" + "\n" + e);
}
}
/**
* 群发消息
* https://open.feishu.cn/document/ukTMukTMukTM/ucDO1EjL3gTNx4yN4UTM
*/
public void sendBatchMsg(FeiShuBatchMsgDTO feiShuBatchMsgDTO, String appName) {
String api = "/message/v4/batch_send/";
RestTemplate restTemplate = new RestTemplate();
try {
HttpHeaders headers = new HttpHeaders();
headers.set("Authorization", "Bearer " + getTenantToken(appName));
headers.set("Content-Type", "application/json; charset=utf-8");
HttpEntity<String> requestEntity = new HttpEntity<>(objectMapper.writeValueAsString(feiShuBatchMsgDTO), headers);
String url = feiShuConfig.getFeiShuOpenApiHost() + api;
ResponseEntity<String> responseEntity = restTemplate.exchange(url, HttpMethod.POST, requestEntity, String.class);
log.info("批量发送消息返回:{}", responseEntity);
} catch (Exception e) {
throw new ServiceException("飞书:" + api + "接口调用失败" + "\n" + e);
}
}
/**
* 单发消息
* https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/im-v1/message/create
*
* @param feiShuMsgDTO
*/
public void sendMsg(FeiShuMsgDTO feiShuMsgDTO, String receiveIdType, String appName) {
String api = "/im/v1/messages?receive_id_type={receiveIdType}";
RestTemplate restTemplate = new RestTemplate();
try {
HttpHeaders headers = new HttpHeaders();
headers.set("Authorization", "Bearer " + getTenantToken(appName));
headers.set("Content-Type", "application/json; charset=utf-8");
HttpEntity<String> requestEntity = new HttpEntity<>(objectMapper.writeValueAsString(feiShuMsgDTO), headers);
String url = feiShuConfig.getFeiShuOpenApiHost() + api;
ResponseEntity<String> responseEntity = restTemplate.exchange(url, HttpMethod.POST, requestEntity, String.class, receiveIdType);
log.info("单发消息成功, req:{}, resp: {}", feiShuMsgDTO, responseEntity);
} catch (Exception e) {
log.error("单发消息失败", e);
}
}
/**
* 根据部门id查询部门信息
* https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/contact-v3/department/get
*
* @param departmentId
* @return
*/
public FeiShuDepartmentDTO getDepartment(String departmentId, String appName) {
String api = "/contact/v3/departments/{departmentId}";
RestTemplate restTemplate = new RestTemplate();
HttpHeaders headers = new HttpHeaders();
headers.set("Authorization", "Bearer " + getTenantToken(appName));
headers.set("Content-Type", "application/json; charset=utf-8");
HttpEntity<String> requestEntity = new HttpEntity<>(headers);
String url = feiShuConfig.getFeiShuOpenApiHost() + api;
try {
ResponseEntity<String> responseEntity = restTemplate.exchange(url, HttpMethod.GET, requestEntity, String.class, departmentId);
Type type = new TypeReference<FeiShuResultDTO>() {
}.getType();
FeiShuResultDTO feiShuResultDTO = JSONObject.parseObject(responseEntity.getBody(), type);
return feiShuResultDTO.getData().getDepartment();
} catch (Exception e) {
throw new ServiceException("飞书:" + api + "接口调用失败" + "\n" + e);
}
}
/**
* 列出文档记录
* https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/bitable-v1/app-table-record/list
*
* @param appToken
* @param tableId
* @param appName
* @return
*/
public List getTableRecords(String appToken, String tableId, String appName) {
String api = "/bitable/v1/apps/{app_token}/tables/{table_id}/records";
RestTemplate restTemplate = new RestTemplate();
HttpHeaders headers = new HttpHeaders();
headers.set("Authorization", "Bearer " + getTenantToken(appName));
headers.set("Content-Type", "application/json; charset=utf-8");
HttpEntity<String> requestEntity = new HttpEntity<>(headers);
String url = feiShuConfig.getFeiShuOpenApiHost() + api;
List list = new ArrayList();
try {
ResponseEntity<String> responseEntity = restTemplate.exchange(url, HttpMethod.GET, requestEntity, String.class, appToken, tableId);
Type type = new TypeReference<FeiShuResultDTO<DocDTO<FieldsDTO>>>() {
}.getType();
FeiShuResultDTO feiShuResultDTO = JSONObject.parseObject(responseEntity.getBody(), type);
list = feiShuResultDTO.getData().getItems();
while (feiShuResultDTO.getData().getItems().size() >= 100) {
ResponseEntity<String> next = restTemplate.exchange(url + "&page_token=" + feiShuResultDTO.getData().getPageToken(), HttpMethod.GET, requestEntity, String.class, appToken, tableId);
feiShuResultDTO = JSONObject.parseObject(next.getBody(), type);
list.addAll(feiShuResultDTO.getData().getItems());
}
return list;
} catch (Exception e) {
throw new ServiceException("飞书:" + api + "接口调用失败" + "\n" + e);
}
}
/**
* 更新记录
*
* @param appToken
* @param tableId
* @param recordId
* @param appName
* @param docDTO
* @return
*/
public FeiShuResultDTO updateTableRecords(String appToken, String tableId, String recordId, String appName, DocDTO docDTO) {
String api = "/bitable/v1/apps/{app_token}/tables/{table_id}/records/{record_id}";
RestTemplate restTemplate = new RestTemplate();
HttpHeaders headers = new HttpHeaders();
headers.set("Authorization", "Bearer " + getTenantToken(appName));
headers.set("Content-Type", "application/json; charset=utf-8");
HttpEntity<String> requestEntity = new HttpEntity<>(JSONObject.toJSONString(docDTO), headers);
String url = feiShuConfig.getFeiShuOpenApiHost() + api;
try {
ResponseEntity<String> responseEntity = restTemplate.exchange(url, HttpMethod.PUT, requestEntity, String.class, appToken, tableId, recordId);
FeiShuResultDTO feiShuResultDTO = JSONObject.parseObject(responseEntity.getBody(), FeiShuResultDTO.class);
return feiShuResultDTO;
} catch (Exception e) {
throw new ServiceException("飞书:" + api + "接口调用失败" + "\n" + e);
}
}
}
package com.pipihelper.project.feishu.service;
import com.pipihelper.project.feishu.dto.FeiShuConfig;
import com.pipihelper.project.feishu.dto.FeiShuEventDTO;
import com.pipihelper.project.feishu.dto.FeiShuEventHeaderDTO;
import com.pipihelper.project.feishu.dto.FeiShuEventReceiveMessageDTO;
import com.pipihelper.project.tx.dto.TxConfig;
import com.pipihelper.project.tx.service.TxApiService;
import com.pipitest.project.feishu.dto.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
/**
* @Description: TODO
* @author: charles
* @date: 2022年04月11日 17:01
*/
@Service
public class FeiShuEventService {
@Resource
private FeiShuConfig feiShuConfig;
@Resource
private FeiShuApiService feiShuApiService;
@Autowired
private TxApiService txApiService;
@Autowired
private TxConfig txConfig;
@Async
public void imMessageReceiveV1(FeiShuEventDTO feiShuEventDTO){
FeiShuEventHeaderDTO feiShuEventHeaderDTO = feiShuEventDTO.getHeader();
FeiShuEventReceiveMessageDTO feiShuEventReceiveMessageDTO = feiShuEventDTO.getEvent().toJavaObject(FeiShuEventReceiveMessageDTO.class);
FeiShuEventReceiveMessageDTO.Sender sender= feiShuEventReceiveMessageDTO.getSender();
FeiShuEventReceiveMessageDTO.Message message= feiShuEventReceiveMessageDTO.getMessage();
//单独处理群消息
if (message.getChat_id().equals(feiShuConfig.getAppConfigMap().get(feiShuConfig.getTestHelperApp()).getFkChatId())
|| message.getChat_id().equals(feiShuConfig.getAppConfigMap().get(feiShuConfig.getTestHelperApp()).getFkChatIdTest())) { //判断是否为指定群消息
return;
}
else {
//如果是单聊
if(message.getChat_type().equals("p2p")) {
// robotTalk(feiShuEventDTO);
}
}
}
}
package com.pipihelper.project.feishu.service.anniversary;
import com.pipihelper.project.feishu.dto.FeiShuConfig;
import com.pipihelper.project.feishu.dto.department.FeiShuDepartmentDTO;
import com.pipihelper.project.feishu.dto.employee.FeiShuEmployeeDTO;
import com.pipihelper.project.feishu.dto.msg.FeiShuMsgDTO;
import com.pipihelper.project.feishu.service.FeiShuApiService;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@Service
public abstract class AbstractNoticeService {
@Resource
private FeiShuConfig feiShuConfig;
@Autowired
private FeiShuApiService feiShuApiService;
/**
* 发送通知
*/
public abstract void notice();
/**
* 生成员工消息卡片
* @param feiShuEmployeeDTO
* @return
*/
public abstract FeiShuMsgDTO generateEmployeeMsgCard(FeiShuEmployeeDTO feiShuEmployeeDTO);
/**
* 生产领导消息卡片
* @param feiShuEmployeeDTO
* @return
*/
public abstract FeiShuMsgDTO generateLeaderMsgCard(FeiShuEmployeeDTO feiShuEmployeeDTO);
/**
* 获取领导OpenId
*
* @param departmentId
* @return
*/
public String getLeaderOpenId(String departmentId) {
if (StringUtils.isBlank(departmentId)) {
return "";
}
if ("0".equals(departmentId)) {
// 若递归无上级负责人,则获取企业创建人
return feiShuApiService.getUser("0", feiShuConfig.getAnniversaryApp()).getJSONObject("data").getJSONArray("items").getJSONObject(0).getString("open_id");
}
FeiShuDepartmentDTO feiShuDepartmentDTO = feiShuApiService.getDepartment(departmentId, feiShuConfig.getAnniversaryApp());
return StringUtils.isNotBlank(feiShuDepartmentDTO.getLeaderUserId()) ? feiShuDepartmentDTO.getLeaderUserId() : getLeaderOpenId(feiShuDepartmentDTO.getParentDepartmentId());
}
/**
* 给部门领导发送消息,若领导是自己,则找父部门领导
*
* @param feiShuMsgDTO
* @param feiShuEmployeeDTO
*/
public void sendMsgToLeader(FeiShuMsgDTO feiShuMsgDTO, FeiShuEmployeeDTO feiShuEmployeeDTO) {
String leaderOpenId = getLeaderOpenId(feiShuEmployeeDTO.getSystemFields().getDepartmentId());
// 领导是自己,找父部门领导
if (leaderOpenId.equals(feiShuEmployeeDTO.getUserId()) && !"0".equals(feiShuEmployeeDTO.getSystemFields().getDepartmentId())) {
leaderOpenId = getLeaderOpenId(feiShuApiService.getDepartment(feiShuEmployeeDTO.getSystemFields().getDepartmentId(), feiShuConfig.getAnniversaryApp()).getParentDepartmentId());
}
if (!leaderOpenId.equals(feiShuEmployeeDTO.getUserId()) && StringUtils.isNotBlank(leaderOpenId)) {
// 给领导发送消息
feiShuMsgDTO.setReceiveId(leaderOpenId);
feiShuApiService.sendMsg(feiShuMsgDTO, "open_id", feiShuConfig.getAnniversaryApp());
}
}
/**
* 读取模板文件
* @param file
* @return
*/
public String getInteractiveCardStr(String file){
try {
InputStream in = this.getClass().getResourceAsStream(file);
InputStreamReader inputStreamReader = new InputStreamReader(in);
Stream<String> streamOfString= new BufferedReader(inputStreamReader).lines();
String streamToString = streamOfString.collect(Collectors.joining());
return streamToString;
} catch (Exception e) {
e.printStackTrace();
}
return "";
}
}
package com.pipihelper.project.feishu.service.anniversary;
import com.pipihelper.project.feishu.dto.FeiShuConfig;
import com.pipihelper.project.feishu.dto.employee.FeiShuEmployeeDTO;
import com.pipihelper.project.feishu.dto.msg.FeiShuMsgDTO;
import com.pipihelper.project.feishu.service.FeiShuApiService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.time.LocalDate;
import java.util.List;
import java.util.stream.Collectors;
/**
* 飞书入职周年提醒
*/
@Service
@Slf4j
public class AnniversaryNoticeService extends AbstractNoticeService{
@Autowired
private FeiShuApiService feiShuApiService;
@Resource
private FeiShuConfig feiShuConfig;
@Override
public void notice() {
// 1. 获取当天生日人
List<FeiShuEmployeeDTO> feiShuEmployeeDTOList = feiShuApiService.getEmployeeList(feiShuConfig.getAnniversaryApp());
List<FeiShuEmployeeDTO> hireDayEmployees = feiShuEmployeeDTOList.stream()
.filter(feiShuEmployeeDTO -> StringUtils.isNotEmpty(feiShuEmployeeDTO.getSystemFields().getHireDate()))
.filter(feiShuEmployeeDTO -> {
LocalDate hireDay = LocalDate.parse(feiShuEmployeeDTO.getSystemFields().getHireDate());
// 使用plusYears可以避免2月29日的特殊情况.当年若无2月29日,生日则为2月28日
long years = LocalDate.now().getYear() - hireDay.getYear();
// 1,2,3,5周年时发送提醒
return hireDay.plusYears(years).equals(LocalDate.now()) && (years == 1 || years == 2 || years == 3 || years == 5);
})
.peek(feiShuEmployeeDTO -> {
// 给员工自己发消息
feiShuApiService.sendMsg(generateEmployeeMsgCard(feiShuEmployeeDTO), "open_id",feiShuConfig.getAnniversaryApp());
// 给领导发送消息
sendMsgToLeader(generateLeaderMsgCard(feiShuEmployeeDTO),feiShuEmployeeDTO);
}).collect(Collectors.toList());
log.info("{} 当日周年人: {}", LocalDate.now(), hireDayEmployees);
}
@Override
public FeiShuMsgDTO generateEmployeeMsgCard(FeiShuEmployeeDTO feiShuEmployeeDTO) {
LocalDate hireDay = LocalDate.parse(feiShuEmployeeDTO.getSystemFields().getHireDate());
long years = LocalDate.now().getYear() - hireDay.getYear();
String fileName = String.format("/templates/anniversary-employee-%s.json", years);
FeiShuMsgDTO feiShuMsgDTO = new FeiShuMsgDTO();
feiShuMsgDTO.setMsgType("interactive");
feiShuMsgDTO.setContent(String.format(getInteractiveCardStr(fileName), feiShuEmployeeDTO.getSystemFields().getName()));
feiShuMsgDTO.setReceiveId(feiShuEmployeeDTO.getUserId());
return feiShuMsgDTO;
}
@Override
public FeiShuMsgDTO generateLeaderMsgCard(FeiShuEmployeeDTO feiShuEmployeeDTO) {
LocalDate hireDay = LocalDate.parse(feiShuEmployeeDTO.getSystemFields().getHireDate());
long years = LocalDate.now().getYear() - hireDay.getYear();
String fileName = String.format("/templates/anniversary-leader-%s.json", String.valueOf(years));
FeiShuMsgDTO feiShuMsgDTO = new FeiShuMsgDTO();
feiShuMsgDTO.setMsgType("interactive");
feiShuMsgDTO.setContent(String.format(getInteractiveCardStr(fileName), feiShuEmployeeDTO.getSystemFields().getName()));
return feiShuMsgDTO;
}
}
package com.pipihelper.project.feishu.service.anniversary;
import com.pipihelper.project.feishu.dto.FeiShuConfig;
import com.pipihelper.project.feishu.dto.msg.FeiShuMsgDTO;
import com.pipihelper.project.feishu.dto.employee.FeiShuEmployeeDTO;
import com.pipihelper.project.feishu.service.FeiShuApiService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.time.LocalDate;
import java.util.List;
import java.util.stream.Collectors;
/**
* 飞书生日提醒
*/
@Service
@Slf4j
public class BirthdayNoticeService extends AbstractNoticeService {
@Autowired
private FeiShuApiService feiShuApiService;
@Resource
private FeiShuConfig feiShuConfig;
@Override
public void notice() {
// 1. 获取当天生日人
List<FeiShuEmployeeDTO> feiShuEmployeeDTOList = feiShuApiService.getEmployeeList(feiShuConfig.getAnniversaryApp());
List<FeiShuEmployeeDTO> birthdayEmployees = feiShuEmployeeDTOList.stream()
.filter(feiShuEmployeeDTO -> StringUtils.isNotEmpty(feiShuEmployeeDTO.getSystemFields().getBirthday()))
.filter(feiShuEmployeeDTO -> {
LocalDate birthday = LocalDate.parse(feiShuEmployeeDTO.getSystemFields().getBirthday());
// 使用plusYears可以避免2月29日的特殊情况.当年若无2月29日,生日则为2月28日
long years = LocalDate.now().getYear() - birthday.getYear();
return birthday.plusYears(years).equals(LocalDate.now());
})
.map(feiShuEmployeeDTO -> {
// 给员工自己发消息
feiShuApiService.sendMsg(generateEmployeeMsgCard(feiShuEmployeeDTO), "open_id", feiShuConfig.getAnniversaryApp());
// 给领导发送消息
sendMsgToLeader(generateLeaderMsgCard(feiShuEmployeeDTO), feiShuEmployeeDTO);
return feiShuEmployeeDTO;
}).collect(Collectors.toList());
log.info("{} 当日生日人: {}", LocalDate.now(), birthdayEmployees);
}
@Override
public FeiShuMsgDTO generateEmployeeMsgCard(FeiShuEmployeeDTO feiShuEmployeeDTO) {
FeiShuMsgDTO feiShuMsgDTO = new FeiShuMsgDTO();
feiShuMsgDTO.setMsgType("interactive");
feiShuMsgDTO.setContent(String.format(getInteractiveCardStr("/templates/birthday-employee.json"), feiShuEmployeeDTO.getSystemFields().getName()));
feiShuMsgDTO.setReceiveId(feiShuEmployeeDTO.getUserId());
return feiShuMsgDTO;
}
@Override
public FeiShuMsgDTO generateLeaderMsgCard(FeiShuEmployeeDTO feiShuEmployeeDTO) {
FeiShuMsgDTO feiShuMsgDTO = new FeiShuMsgDTO();
feiShuMsgDTO.setMsgType("interactive");
feiShuMsgDTO.setContent(String.format(getInteractiveCardStr("/templates/birthday-leader.json"), feiShuEmployeeDTO.getSystemFields().getName()));
return feiShuMsgDTO;
}
}
package com.pipihelper.project.feishu.service.anniversary;
import com.pipihelper.project.feishu.dto.FeiShuConfig;
import com.pipihelper.project.feishu.dto.msg.FeiShuMsgDTO;
import com.pipihelper.project.feishu.dto.employee.FeiShuEmployeeDTO;
import com.pipihelper.project.feishu.service.FeiShuApiService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.time.LocalDate;
import java.util.List;
import java.util.stream.Collectors;
/**
* 飞书转正日提醒
*/
@Service
@Slf4j
public class ConversionNoticeService extends AbstractNoticeService{
@Autowired
private FeiShuApiService feiShuApiService;
@Resource
private FeiShuConfig feiShuConfig;
/**
* 逻辑来自HR
* 转正日为入职日过6个月
* 一般是入职是X月Y日,那么6个月是到X+6月Y-1日到期,第二天的X+6月Y日是转正的第一天了
* 特殊情况就是有一些月份的30日 31日入职,那么转正月份因为当月最后1天不是30号或者31号,那么我们就按照当月的最后1天作为转正日
* 最典型的就是8月31日入职,转正日期可能是2月28日,也可能是2月29日
* 或者12月31日入职,那么转正日期是6月30日(因为6月没有31号);但是1月31日入职,转正日期就是7月31日(因为7月最后1天就是31号)
*/
@Override
public void notice() {
// 1. 获取当天生日人
List<FeiShuEmployeeDTO> feiShuEmployeeDTOList = feiShuApiService.getEmployeeList(feiShuConfig.getAnniversaryApp());
List<FeiShuEmployeeDTO> hireDayEmployees = feiShuEmployeeDTOList.stream()
.filter(feiShuEmployeeDTO -> StringUtils.isNotEmpty(feiShuEmployeeDTO.getSystemFields().getHireDate()))
.filter(feiShuEmployeeDTO -> {
LocalDate hireDay = LocalDate.parse(feiShuEmployeeDTO.getSystemFields().getHireDate());
// 使用plusMonths正好满足需求
return hireDay.plusMonths(6L).equals(LocalDate.now());
})
.map(feiShuEmployeeDTO -> {
// 给员工自己发消息
feiShuApiService.sendMsg(generateEmployeeMsgCard(feiShuEmployeeDTO), "open_id",feiShuConfig.getAnniversaryApp());
// 给领导发送消息
sendMsgToLeader(generateLeaderMsgCard(feiShuEmployeeDTO),feiShuEmployeeDTO);
return feiShuEmployeeDTO;
}).collect(Collectors.toList());
log.info("{} 当日转正人: {}", LocalDate.now(), hireDayEmployees);
}
@Override
public FeiShuMsgDTO generateEmployeeMsgCard(FeiShuEmployeeDTO feiShuEmployeeDTO){
String name = feiShuEmployeeDTO.getSystemFields().getName();
FeiShuMsgDTO feiShuMsgDTO = new FeiShuMsgDTO();
feiShuMsgDTO.setMsgType("interactive");
feiShuMsgDTO.setContent(String.format(getInteractiveCardStr("/templates/conversion-employee.json"), name, name));
feiShuMsgDTO.setReceiveId(feiShuEmployeeDTO.getUserId());
return feiShuMsgDTO;
}
@Override
public FeiShuMsgDTO generateLeaderMsgCard(FeiShuEmployeeDTO feiShuEmployeeDTO) {
FeiShuMsgDTO feiShuMsgDTO = new FeiShuMsgDTO();
feiShuMsgDTO.setMsgType("interactive");
feiShuMsgDTO.setContent(String.format(getInteractiveCardStr("/templates/conversion-leader.json"), feiShuEmployeeDTO.getSystemFields().getName()));
return feiShuMsgDTO;
}
}
package com.pipihelper.project.feishu.service.doc;
import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import com.pipihelper.project.feishu.dto.AppConfig;
import com.pipihelper.project.feishu.dto.FeiShuConfig;
import com.pipihelper.project.feishu.dto.FeiShuCreateTaskDTO;
import com.pipihelper.project.feishu.dto.doc.DocDTO;
import com.pipihelper.project.feishu.dto.doc.FieldsDTO;
import com.pipihelper.project.feishu.dto.doc.PeopleDTO;
import com.pipihelper.project.feishu.dto.employee.FeiShuEmployeeDTO;
import com.pipihelper.project.feishu.dto.msg.ContentDTO;
import com.pipihelper.project.feishu.dto.msg.FeiShuBatchMsgDTO;
import com.pipihelper.project.feishu.service.FeiShuApiService;
import com.pipihelper.project.feishu.vo.CreateTaskVo;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import javax.annotation.Resource;
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.util.*;
/**
* @author xiongjian
* @date 2022/7/4
*/
@Service
@Slf4j
public class DocHelperService {
@Autowired
private FeiShuApiService feiShuApiService;
@Resource
private FeiShuConfig feiShuConfig;
private final Cache<String, Map<String, String>> NAME_CACHE = Caffeine.newBuilder()
.maximumSize(5)
.expireAfterWrite(Duration.ofHours(2))
.build();
private final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm");
private Map<String, String> getNameMap() {
return NAME_CACHE.get("nameMap", map -> {
List<FeiShuEmployeeDTO> feiShuEmployeeDTOList = feiShuApiService.getEmployeeList(feiShuConfig.getAnniversaryApp());
HashMap<String, String> nameMap = new HashMap<>(feiShuEmployeeDTOList.size());
for (FeiShuEmployeeDTO feiShuEmployeeDTO : feiShuEmployeeDTOList) {
String key = feiShuEmployeeDTO.getSystemFields().getName();
nameMap.put(key, feiShuEmployeeDTO.getUserId());
}
log.info("重建姓名-Id缓存, size: {}", nameMap.size());
return nameMap;
});
}
/**
* 人员转换器
*/
public void transferPeople() {
String app = feiShuConfig.getAnniversaryApp();
AppConfig appConfig = feiShuConfig.getAppConfigMap().get(app);
List<DocDTO> docList = feiShuApiService.getTableRecords(appConfig.getTableId(), appConfig.getPeopleTableId(), app);
for (DocDTO<FieldsDTO> docDTO : docList) {
if (Objects.isNull(docDTO.getFields().getName())) {
continue;
}
if (getNameMap().containsKey(docDTO.getFields().getName()) && CollectionUtils.isEmpty(docDTO.getFields().getPeople())) {
List<PeopleDTO> list = new ArrayList<>();
list.add(new PeopleDTO(getNameMap().get(docDTO.getFields().getName())));
docDTO.getFields().setPeople(list);
docDTO.getFields().setUserId(getNameMap().get(docDTO.getFields().getName()));
docDTO.getFields().setUpdateTime(LocalDateTime.now().atZone(ZoneId.systemDefault()).toInstant().toEpochMilli());
docDTO.getFields().setToken("Bearer " + feiShuApiService.getTenantToken(app));
feiShuApiService.updateTableRecords(appConfig.getTableId(), appConfig.getPeopleTableId(), docDTO.getRecordId(), app, docDTO);
}
}
}
/**
* 通过表格创建任务
*/
public void createTaskByTable() {
String app = feiShuConfig.getAnniversaryApp();
AppConfig appConfig = feiShuConfig.getAppConfigMap().get(app);
List<DocDTO> docList = feiShuApiService.getTableRecords(appConfig.getTableId(), appConfig.getTaskTableId(), app);
for (DocDTO<FieldsDTO> docDTO : docList) {
if (docDTO.getFields() == null) {
continue;
}
if (StringUtils.isBlank(docDTO.getFields().getTaskName())) {
continue;
}
if (!"待创建".equals(docDTO.getFields().getStatues())) {
continue;
}
if (StringUtils.isBlank(docDTO.getFields().getCollaborators())) {
continue;
}
// 创建任务
FeiShuCreateTaskDTO feiShuCreateTaskDTO = new FeiShuCreateTaskDTO();
feiShuCreateTaskDTO.setSummary(docDTO.getFields().getTaskName());
feiShuCreateTaskDTO.setDescription(docDTO.getFields().getTaskDescription());
FeiShuCreateTaskDTO.Due due = new FeiShuCreateTaskDTO.Due();
if (docDTO.getFields().getDue() != null) {
due.setTime(String.valueOf(docDTO.getFields().getDue()).substring(0, 10));
}
feiShuCreateTaskDTO.setDue(due);
FeiShuCreateTaskDTO.OriginDTO originDTO = new FeiShuCreateTaskDTO.OriginDTO();
originDTO.setPlatformI18nName(String.format("{\"zh_cn\": \"%s\"}", docDTO.getFields().getOrigin()));
feiShuCreateTaskDTO.setOrigin(originDTO);
// 执行者
String[] collaborators = docDTO.getFields().getCollaborators().split(",|,");
List<String> collaboratorIds = new ArrayList<>();
for (String collaborator : collaborators) {
if (getNameMap().containsKey(collaborator)) {
collaboratorIds.add(getNameMap().get(collaborator));
}
}
feiShuCreateTaskDTO.setCollaboratorIds(collaboratorIds);
// 关注者
if (docDTO.getFields().getFollowers() != null) {
String[] followers = docDTO.getFields().getFollowers().split(",|,");
List<String> followerIds = new ArrayList<>();
for (String follower : followers) {
if (getNameMap().containsKey(follower)) {
followerIds.add(getNameMap().get(follower));
}
}
feiShuCreateTaskDTO.setFollowerIds(followerIds);
}
try {
// 创建任务
feiShuApiService.createTask(feiShuCreateTaskDTO, null, app);
// 发送消息
sendMsg(docDTO);
docDTO.getFields().setNote("成功");
} catch (Exception e) {
docDTO.getFields().setNote(e.getMessage());
} finally {
docDTO.getFields().setStatues("已创建");
docDTO.getFields().setUpdateTime(LocalDateTime.now().atZone(ZoneId.systemDefault()).toInstant().toEpochMilli());
// 更新数据表
feiShuApiService.updateTableRecords(appConfig.getTableId(), appConfig.getTaskTableId(), docDTO.getRecordId(), app, docDTO);
}
}
}
public void createTask(CreateTaskVo createTaskVo) {
String app = feiShuConfig.getAnniversaryApp();
// 创建任务
FeiShuCreateTaskDTO feiShuCreateTaskDTO = new FeiShuCreateTaskDTO();
feiShuCreateTaskDTO.setSummary(createTaskVo.getSummary());
feiShuCreateTaskDTO.setDescription(createTaskVo.getDescription());
FeiShuCreateTaskDTO.Due due = new FeiShuCreateTaskDTO.Due();
ZoneId zone = ZoneId.systemDefault();
if (StringUtils.isNotBlank(createTaskVo.getDueTime())) {
// 截止时间有值按填值截止
Long time = LocalDateTime.parse(createTaskVo.getDueTime(), DATE_TIME_FORMATTER).atZone(zone).toInstant().getEpochSecond();
due.setTime(String.valueOf(time));
} else {
// 截止时间无值,按当天18点+偏移量截止
LocalDateTime dateTime = LocalDateTime.of(LocalDate.now(), LocalTime.of(18, 0)).plusHours(createTaskVo.getDueTimeOffset());
Long time = dateTime.atZone(zone).toInstant().getEpochSecond();
due.setTime(String.valueOf(time));
}
feiShuCreateTaskDTO.setDue(due);
FeiShuCreateTaskDTO.OriginDTO originDTO = new FeiShuCreateTaskDTO.OriginDTO();
originDTO.setPlatformI18nName(String.format("{\"zh_cn\": \"%s\"}", app));
feiShuCreateTaskDTO.setOrigin(originDTO);
// 执行者
if (StringUtils.isNotBlank(createTaskVo.getCollaboratorIds())){
List<String> collaboratorIds = new ArrayList<>();
for (String name : getNameMap().keySet()) {
if (createTaskVo.getCollaboratorIds().contains(name)){
collaboratorIds.add(getNameMap().get(name));
}
}
feiShuCreateTaskDTO.setCollaboratorIds(collaboratorIds);
}
// 关注者
if (StringUtils.isNotBlank(createTaskVo.getFollowerIds())){
List<String> followersIds = new ArrayList<>();
for (String name : getNameMap().keySet()) {
if (createTaskVo.getFollowerIds().contains(name)){
followersIds.add(getNameMap().get(name));
}
}
feiShuCreateTaskDTO.setFollowerIds(followersIds);
}
// 创建任务
feiShuApiService.createTask(feiShuCreateTaskDTO, null, app);
}
public void sendMsg(DocDTO<FieldsDTO> docDTO) {
if (StringUtils.isBlank(docDTO.getFields().getMsg())) {
return;
}
if (StringUtils.isBlank(docDTO.getFields().getReceiverIds())) {
return;
}
FeiShuBatchMsgDTO feiShuBatchMsgDTO = new FeiShuBatchMsgDTO();
feiShuBatchMsgDTO.setMsgType("text");
feiShuBatchMsgDTO.setContent(new ContentDTO(docDTO.getFields().getMsg()));
String[] receivers = docDTO.getFields().getReceiverIds().split(",|,");
List<String> receiverIds = new ArrayList<>();
for (String receiver : receivers) {
if (getNameMap().containsKey(receiver)) {
receiverIds.add(getNameMap().get(receiver));
}
}
feiShuBatchMsgDTO.setOpenIds(receiverIds);
// 发送消息
feiShuApiService.sendBatchMsg(feiShuBatchMsgDTO, feiShuConfig.getAnniversaryApp());
}
}
package com.pipihelper.project.feishu.utils;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;
/**
* @Description: TODO
* @author: charles
* @date: 2022年03月31日 10:03
*/
public class FeiShuEventDataDecrypter {
private byte[] keyBs;
public FeiShuEventDataDecrypter(String key) {
MessageDigest digest = null;
try {
digest = MessageDigest.getInstance("SHA-256");
} catch (NoSuchAlgorithmException e) {
// won't happen
}
keyBs = digest.digest(key.getBytes(StandardCharsets.UTF_8));
}
public String decrypt(String base64) throws Exception {
byte[] decode = Base64.getDecoder().decode(base64);
Cipher cipher = Cipher.getInstance("AES/CBC/NOPADDING");
byte[] iv = new byte[16];
System.arraycopy(decode, 0, iv, 0, 16);
byte[] data = new byte[decode.length - 16];
System.arraycopy(decode, 16, data, 0, data.length);
cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(keyBs, "AES"), new IvParameterSpec(iv));
byte[] r = cipher.doFinal(data);
if (r.length > 0) {
int p = r.length - 1;
for (; p >= 0 && r[p] <= 16; p--) {
}
if (p != r.length - 1) {
byte[] rr = new byte[p + 1];
System.arraycopy(r, 0, rr, 0, p + 1);
r = rr;
}
}
return new String(r, StandardCharsets.UTF_8);
}
public static void main(String[] args) throws Exception {
FeiShuEventDataDecrypter d = new FeiShuEventDataDecrypter("kudryavka");
System.out.println(d.decrypt("FIAfJPGRmFZWkaxPQ1XrJZVbv2JwdjfLk4jx0k/U1deAqYK3AXOZ5zcHt/cC4ZNTqYwWUW/EoL+b2hW/C4zoAQQ5CeMtbxX2zHjm+E4nX/Aww+FHUL6iuIMaeL2KLxqdtbHRC50vgC2YI7xohnb3KuCNBMUzLiPeNIpVdnYaeteCmSaESb+AZpJB9PExzTpRDzCRv+T6o5vlzaE8UgIneC1sYu85BnPBEMTSuj1ZZzfdQi7ZW992Z4dmJxn9e8FL2VArNm99f5Io3c2O4AcNsQENNKtfAAxVjCqc3mg5jF0nKabA+u/5vrUD76flX1UOF5fzJ0sApG2OEn9wfyPDRBsApn9o+fceF9hNrYBGsdtZrZYyGG387CGOtKsuj8e2E8SNp+Pn4E9oYejOTR+ZNLNi+twxaXVlJhr6l+RXYwEiMGQE9zGFBD6h2dOhKh3W84p1GEYnSRIz1+9/Hp66arjC7RCrhuW5OjCj4QFEQJiwgL45XryxHtiZ7JdAlPmjVsL03CxxFZarzxzffryrWUG3VkRdHRHbTsC34+ScoL5MTDU1QAWdqUC1T7xT0lCvQELaIhBTXAYrznJl6PlA83oqlMxpHh0gZBB1jFbfoUr7OQbBs1xqzpYK6Yjux6diwpQB1zlZErYJUfCqK7G/zI9yK/60b4HW0k3M+AvzMcw=")); //hello world
}
}
package com.pipihelper.project.feishu.vo;
import com.alibaba.fastjson.annotation.JSONField;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
/**
* @author xiongjian
* @date 2022/7/27
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class CreateTaskVo {
private String summary;
private String description;
private String dueTime;
private Integer dueTimeOffset;
private String followerIds;
private String collaboratorIds;
}
package com.pipihelper.project.rostering.model;
import java.util.Date;
/**
* @description:
* @author: zsw
* @create: 2022-10-14 16:52
**/
public class RosteringModel {
/**
* 名字
*/
private String name;
/**
* 班次
*/
private String shift;
/**
* 时间
*/
private Date date;
}
package com.pipihelper.project.rostering.service;
/**
* @description:
* @author: zsw
* @create: 2022-10-14 16:51
**/
public interface RosteringService {
}
package com.pipihelper.project.rostering.service.impl;
import com.pipihelper.project.rostering.service.RosteringService;
import org.springframework.stereotype.Service;
/**
* @description:
* @author: zsw
* @create: 2022-10-14 16:51
**/
@Service
public class RosteringServiceImpl implements RosteringService {
}
package com.pipihelper.project.tx.dto;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/**
* @Description: TODO
* @author: charles
* @date: 2022年06月06日 13:20
*/
@Component
@ConfigurationProperties(prefix = "tx")
@Data
@NoArgsConstructor
@AllArgsConstructor
public class TxConfig {
private String secretId;
private String secretKey;
private String robotId;
private String robotEnv;
private String dictId;
}
package com.pipihelper.project.tx.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* @description: some desc
* @author: charles
* @date: 2022/6/11 15:57
*/
@Getter
@AllArgsConstructor
public enum BotWant {
/**
* 意图
*/
}
package com.pipihelper.project.tx.service;
import com.alibaba.fastjson.JSONObject;
import com.pipihelper.project.tx.dto.TxConfig;
import com.tencentcloudapi.common.exception.TencentCloudSDKException;
import com.tencentcloudapi.nlp.v20190408.NlpClient;
import com.tencentcloudapi.nlp.v20190408.models.*;
import com.tencentcloudapi.tbp.v20190627.TbpClient;
import com.tencentcloudapi.tbp.v20190627.models.TextProcessRequest;
import com.tencentcloudapi.tbp.v20190627.models.TextProcessResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
/**
* @Description: TODO
* @author: charles
* @date: 2022年06月06日 13:31
*/
@Service
public class TxApiService {
@Autowired
private NlpClient nlpClient;
@Autowired
private TbpClient tbpClient;
@Autowired
private TxConfig txConfig;
public JSONObject txKeywordsExtraction(String msgText){
KeywordsExtractionRequest req = new KeywordsExtractionRequest();
req.setText(msgText);
req.setNum(5L);
try {
// 返回的resp是一个KeywordsExtractionResponse的实例,与请求对象对应
KeywordsExtractionResponse resp = nlpClient.KeywordsExtraction(req);
// 输出json格式的字符串回包
return JSONObject.parseObject(KeywordsExtractionResponse.toJsonString(resp));
}catch (TencentCloudSDKException e) {
System.out.println(e.toString());
return null;
}
}
public TextProcessResponse txTextProcessRequest(String terminalId, String inputText){
try{
// 实例化一个请求对象,每个接口都会对应一个request对象
TextProcessRequest req = new TextProcessRequest();
req.setBotId(txConfig.getRobotId());
req.setBotEnv(txConfig.getRobotEnv());
req.setTerminalId(terminalId);
req.setInputText(inputText);
// 返回的resp是一个TextProcessResponse的实例,与请求对象对应
return tbpClient.TextProcess(req);
} catch (TencentCloudSDKException e) {
System.out.println(e.toString());
}
return null;
}
//新增自定义词库词条
@Async
public CreateWordItemsResponse createWordItemsResponse(String dictId, String[] texts){
try {
CreateWordItemsRequest req = new CreateWordItemsRequest();
req.setDictId(dictId);
WordItem[] wordItems = new WordItem[texts.length];
for(int i=0;i<texts.length;i++){
WordItem wordItem1 = new WordItem();
wordItem1.setText(texts[i]);
wordItems[i] = wordItem1;
}
req.setWordItems(wordItems);
return nlpClient.CreateWordItems(req);
} catch (TencentCloudSDKException e) {
e.printStackTrace();
}
return null;
}
public List<String> lexicalAnalysisResponse(String dictId, String texts){
List<String> keywords =new ArrayList<>();
try {
LexicalAnalysisRequest req = new LexicalAnalysisRequest();
req.setDictId(dictId);
req.setText(texts);
// 返回的resp是一个LexicalAnalysisResponse的实例,与请求对象对应
LexicalAnalysisResponse resp = nlpClient.LexicalAnalysis(req);
PosToken[] PosTokens = resp.getPosTokens();
for(PosToken posToken: PosTokens){
if(posToken.getPos().equals("n")){
if(posToken.getWord().length()>1) {
if (!keywords.contains(posToken.getWord())) {
keywords.add(posToken.getWord());
}
}
}
}
} catch (TencentCloudSDKException e) {
e.printStackTrace();
}
return keywords;
}
}
package com.pipihelper.project.utils;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.Random;
/**
* @Description: TODO,生成身份证号,网上抄的
* @author: charles
* @date: 2022年08月04日 15:12
*/
public class CardNoUtil {
// 18位身份证号码各位的含义:
// 1-2位省、自治区、直辖市代码;
// 3-4位地级市、盟、自治州代码;
// 5-6位县、县级市、区代码;
// 7-14位出生年月日,比如19670401代表1967年4月1日;
// 15-17位为顺序号,其中17位(倒数第二位)男为单数,女为双数;
// 18位为校验码,0-9和X。
// 作为尾号的校验码,是由把前十七位数字带入统一的公式计算出来的,
// 计算的结果是0-10,如果某人的尾号是0-9,都不会出现X,但如果尾号是10,那么就得用X来代替,
// 因为如果用10做尾号,那么此人的身份证就变成了19位。X是罗马数字的10,用X来代替10
public static String getRandomID() {
String id = "";
// 随机生成省、自治区、直辖市代码 1-2
String[] provinces = { "11", "12", "13", "14", "15", "21", "22", "23",
"31", "32", "33", "34", "35", "36", "37", "41", "42", "43",
"44", "45", "46", "50", "51", "52", "53", "54", "61", "62",
"63", "64", "65", "71", "81", "82" };
String province = provinces[new Random().nextInt(provinces.length - 1)];
// 随机生成地级市、盟、自治州代码 3-4
String citys[] = { "01", "02", "03", "04", "05", "06", "07", "08",
"09", "10", "21", "22", "23", "24", "25", "26", "27", "28" };
String city = citys[new Random().nextInt(citys.length - 1)];
// 随机生成县、县级市、区代码 5-6
String countys[] = { "01", "02", "03", "04", "05", "06", "07", "08",
"09", "10", "21", "22", "23", "24", "25", "26", "27", "28",
"29", "30", "31", "32", "33", "34", "35", "36", "37", "38" };
String county = countys[new Random().nextInt(countys.length - 1)];
// 随机生成出生年月 7-14
SimpleDateFormat dft = new SimpleDateFormat("yyyyMMdd");
Date beginDate = new Date();
Calendar date = Calendar.getInstance();
date.setTime(beginDate);
date.set(Calendar.DATE,
date.get(Calendar.DATE) - new Random().nextInt(365 * 100));
String birth = dft.format(date.getTime());
// 随机生成顺序号 15-17
String no = new Random().nextInt(999) + "";
// 随机生成校验码 18
String checks[] = { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9",
"X" };
String check = checks[new Random().nextInt(checks.length - 1)];
// 拼接身份证号码
id = province + city + county + birth + no + check;
return id;
}
}
\ No newline at end of file \ No newline at end of file
# 开发环境配置
# 数据源配置,请修改为你项目的实际配置
spring:
datasource:
url: jdbc:mysql://124.71.186.146:3316/t_test_dev?useSSL=false
username: weishuangshuang
password: weishuangshuang2020PipiTest
driver-class-name: com.mysql.cj.jdbc.Driver
# 飞书相关配置参数
feishu:
feiShuOpenApiHost: https://open.feishu.cn/open-apis
testHelperApp: testHelperApp
anniversaryApp: anniversaryApp
# 测试小助手配置
appConfigMap[testHelperApp]:
encryptKey: aGTqmJcfXluKWfFWHGw5SdzIg6QIxPsp
verificationToken: ChUEDdWQbyHpHUV6H5fVeL5fOP3HfBE6
appId: cli_a2cff17bd3f8d013
appSecret: E5vXXmRipOD6vyolib186b25XXLbdYfE
fkChatId: oc_5124ee21dbdecf5d802f9e9e33dab722
# 腾讯云配置参数
tx:
secretId: AKIDDF1XnlSu5IFwNXQ1LPCixvb091bw0nJX
secretKey: yf6G8k58uPdaHhqjLjwsY2HGzThfOr34
robotId: d10169df-89bd-4545-bc82-9adf61aa8910
robotEnv: dev
dictId: udf-8aeabde380
spring:
profiles:
active: dev
# 日志
logging:
file:
name: pipi-test.log
////////////////////////////////////////////////////////////////////
// _ooOoo_ //
// o8888888o //
// 88" . "88 //
// (| ^_^ |) //
// O\ = /O //
// ____/`---'\____ //
// .' \\| |// `. //
// / \\||| : |||// \ //
// / _||||| -:- |||||- \ //
// | | \\\ - /// | | //
// | \_| ''\---/'' | | //
// \ .-\__ `-` ___/-. / //
// ___`. .' /--.--\ `. . ___ //
// ."" '< `.___\_<|>_/___.' >'"". //
// | | : `- \`.;`\ _ /`;.`/ - ` : | | //
// \ \ `-. \_ __\ /__ _/ .-` / / //
// ========`-.____`-.___\_____/___.-`____.-'======== //
// `=---=' //
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //
// 佛祖保佑 永不宕机 永无BUG //
////////////////////////////////////////////////////////////////////
\ No newline at end of file \ No newline at end of file
{
"config": {
"wide_screen_mode": true
},
"elements": [
{
"alt": {
"content": "",
"tag": "plain_text"
},
"img_key": "img_v2_fb35ed68-018e-4f10-b88d-b7f8c81ea50g",
"tag": "img"
},
{
"tag": "div",
"text": {
"content": "**嗨,%s,今天是你入职皮皮的1周年,祝你周年快乐!**",
"tag": "lark_md"
}
},
{
"tag": "hr"
},
{
"tag": "markdown",
"content": "一年前的今天,你选择并加入了**皮皮**💕\n带着你的满腔热血与不懈的努力,一点一点收获成长,突破自我👍\n一年后的今天,你在**皮皮**也留下更多的回忆,皮皮也看到了不断成长的你✨\n**祝愿你初心不改,乘风破浪,朝着更好的自己出发!**",
"href": {
"urlVal": {
"url": "https://www.feishu.com",
"android_url": "https://developer.android.com/",
"ios_url": "lark://msgcard/unsupported_action",
"pc_url": "https://www.feishu.com"
}
}
},
{
"tag": "hr"
}
],
"header": {
"template": "orange",
"title": {
"content": "🎉🎉祝入职【1】周年快乐!",
"tag": "plain_text"
}
}
}
\ No newline at end of file \ No newline at end of file
{
"config": {
"wide_screen_mode": true
},
"elements": [
{
"alt": {
"content": "",
"tag": "plain_text"
},
"img_key": "img_v2_5273001f-8a08-4830-bc9e-3038f8d65f0g",
"tag": "img"
},
{
"tag": "div",
"text": {
"content": "**嗨,%s,今天是你入职皮皮的2周年,祝你周年快乐!**",
"tag": "lark_md"
}
},
{
"tag": "hr"
},
{
"tag": "markdown",
"content": "两年的时光,一晃而过💕\n感谢你的全力奔跑和付出的努力,也期待你在未来的日子闪闪发光,熠熠生辉✨\n**愿你初心如磐,奋楫笃行,复年更胜今年~~**",
"href": {
"urlVal": {
"url": "https://www.feishu.com",
"android_url": "https://developer.android.com/",
"ios_url": "lark://msgcard/unsupported_action",
"pc_url": "https://www.feishu.com"
}
}
},
{
"tag": "hr"
}
],
"header": {
"template": "orange",
"title": {
"content": "🎉🎉祝入职【2】周年快乐!",
"tag": "plain_text"
}
}
}
\ No newline at end of file \ No newline at end of file
{
"config": {
"wide_screen_mode": true
},
"elements": [
{
"alt": {
"content": "",
"tag": "plain_text"
},
"img_key": "img_v2_8ad7651c-8d71-4c0b-bf3e-5b17b65f63ag",
"tag": "img"
},
{
"tag": "div",
"text": {
"content": "**嗨,%s,今天是你入职皮皮的3周年,祝你周年快乐!**",
"tag": "lark_md"
}
},
{
"tag": "hr"
},
{
"tag": "markdown",
"content": "又到了这个特别的日子✨\n这是你跟皮皮相逢的纪念日,也是成为皮皮中一员的重要一天💕\n从第一个365天的不断尝试和试错💨\n到接下来两个365天的更加坚定和相互信任👏\n感谢你一如既往的坚持,在披荆斩棘的路上不畏艰难,闪闪发光💫\n**愿你永远炽热、真诚、坚定地奔赴你想要的未来~**",
"href": {
"urlVal": {
"url": "https://www.feishu.com",
"android_url": "https://developer.android.com/",
"ios_url": "lark://msgcard/unsupported_action",
"pc_url": "https://www.feishu.com"
}
}
},
{
"tag": "hr"
}
],
"header": {
"template": "orange",
"title": {
"content": "🎉🎉祝入职【3】周年快乐!",
"tag": "plain_text"
}
}
}
\ No newline at end of file \ No newline at end of file
{
"config": {
"wide_screen_mode": true
},
"elements": [
{
"alt": {
"content": "",
"tag": "plain_text"
},
"img_key": "img_v2_26f282b8-ced6-47e0-9c6a-a07608c73dag",
"tag": "img"
},
{
"tag": "div",
"text": {
"content": "**嗨,%s,今天是你入职皮皮的5周年,祝你周年快乐!**",
"tag": "lark_md"
}
},
{
"tag": "hr"
},
{
"tag": "markdown",
"content": "伴随着时光的脚步,走过了五个春夏秋冬🧭\n五年的时间,栉风沐雨🌥\n五年的时间,砥砺前行💨\n感恩你的坚持和付出,为皮皮增光添彩👏\n我们铭记你的付出,也见证了你的成长❤\n**未来的日子里愿你满腔热血拥抱无限可能~**",
"href": {
"urlVal": {
"url": "https://www.feishu.com",
"android_url": "https://developer.android.com/",
"ios_url": "lark://msgcard/unsupported_action",
"pc_url": "https://www.feishu.com"
}
}
},
{
"tag": "hr"
}
],
"header": {
"template": "orange",
"title": {
"content": "🎉🎉祝入职【5】周年快乐!",
"tag": "plain_text"
}
}
}
\ No newline at end of file \ No newline at end of file
{
"config": {
"wide_screen_mode": true
},
"elements": [
{
"alt": {
"content": "",
"tag": "plain_text"
},
"img_key": "img_v2_f4de8df4-204a-48c7-9cd4-8abff2e6593g",
"tag": "img"
},
{
"tag": "hr"
},
{
"tag": "div",
"text": {
"content": "**哈喽!💕**\n**今天是%s入职一周年的日子,快去送上祝福和TA聊聊吧~**✨",
"tag": "lark_md"
}
}
],
"header": {
"template": "orange",
"title": {
"content": "🎉🎉团队小伙伴入职1周年啦!",
"tag": "plain_text"
}
}
}
\ No newline at end of file \ No newline at end of file
{
"config": {
"wide_screen_mode": true
},
"elements": [
{
"alt": {
"content": "",
"tag": "plain_text"
},
"img_key": "img_v2_592d3f6b-dedb-4fde-8f00-854edf5e06dg",
"tag": "img"
},
{
"tag": "hr"
},
{
"tag": "div",
"text": {
"content": "**哈喽!💕**\n**今天是%s入职两周年的日子,快去送上祝福和TA聊聊吧~**✨",
"tag": "lark_md"
}
}
],
"header": {
"template": "orange",
"title": {
"content": "🎉🎉团队小伙伴入职2周年啦!",
"tag": "plain_text"
}
}
}
\ No newline at end of file \ No newline at end of file
{
"config": {
"wide_screen_mode": true
},
"elements": [
{
"alt": {
"content": "",
"tag": "plain_text"
},
"img_key": "img_v2_53fbf63d-2bf2-49e5-b061-f89f04d8188g",
"tag": "img"
},
{
"tag": "hr"
},
{
"tag": "div",
"text": {
"content": "**哈喽!💕**\n**今天是%s入职三周年的日子,快去送上祝福和TA聊聊吧~**✨",
"tag": "lark_md"
}
}
],
"header": {
"template": "orange",
"title": {
"content": "🎉🎉团队小伙伴入职3周年啦!",
"tag": "plain_text"
}
}
}
\ No newline at end of file \ No newline at end of file
{
"config": {
"wide_screen_mode": true
},
"elements": [
{
"alt": {
"content": "",
"tag": "plain_text"
},
"img_key": "img_v2_f83c3a87-38fd-4690-992d-1e53b434bb5g",
"tag": "img"
},
{
"tag": "hr"
},
{
"tag": "div",
"text": {
"content": "**哈喽!💕**\n**今天是%s入职五周年的日子,快去送上祝福和TA聊聊吧~**✨",
"tag": "lark_md"
}
}
],
"header": {
"template": "orange",
"title": {
"content": "🎉🎉团队小伙伴入职5周年啦!",
"tag": "plain_text"
}
}
}
\ No newline at end of file \ No newline at end of file
{
"config": {
"wide_screen_mode": true
},
"header": {
"template": "red",
"title": {
"content": "🎂 亲爱的 %s,祝你生日快乐!",
"tag": "plain_text"
}
},
"elements": [
{
"alt": {
"content": "",
"tag": "plain_text"
},
"img_key": "img_v2_a557f866-917f-415a-bf5e-04379ea67b8g",
"tag": "img"
},
{
"tag": "div",
"text": {
"content": "**生活因你而更精彩,愿你此后每一天,眼里是阳光,笑里是坦荡!**",
"tag": "lark_md"
}
},
{
"tag": "hr"
},
{
"tag": "markdown",
"content": "\n**🎁领取专属生日礼物**\n地点:14楼前台\n时间:9:30—18:00</at>",
"href": {
"urlVal": {
"url": "https://www.feishu.com",
"android_url": "https://developer.android.com/",
"ios_url": "lark://msgcard/unsupported_action",
"pc_url": "https://www.feishu.com"
}
}
},
{
"tag": "hr"
}
]
}
\ No newline at end of file \ No newline at end of file
{
"config": {
"wide_screen_mode": true
},
"header": {
"template": "red",
"title": {
"content": "🎈请查收一份生日提醒!",
"tag": "plain_text"
}
},
"elements": [
{
"alt": {
"content": "",
"tag": "plain_text"
},
"img_key": "img_v2_a557f866-917f-415a-bf5e-04379ea67b8g",
"tag": "img"
},
{
"tag": "div",
"text": {
"content": "**今天你的团队里有小伙伴过生日哦~记得给Ta送去一份生日祝福💌**",
"tag": "lark_md"
}
},
{
"tag": "hr"
},
{
"tag": "markdown",
"content": "\n**👑今日寿星:%s**\n</at>",
"href": {
"urlVal": {
"url": "https://www.feishu.com",
"android_url": "https://developer.android.com/",
"ios_url": "lark://msgcard/unsupported_action",
"pc_url": "https://www.feishu.com"
}
}
},
{
"tag": "hr"
}
]
}
\ No newline at end of file \ No newline at end of file
{
"config": {
"wide_screen_mode": true
},
"header": {
"template": "red",
"title": {
"content": "✨✨@%s,恭喜你转正啦!",
"tag": "plain_text"
}
},
"i18n_elements": {
"zh_cn": [
{
"alt": {
"content": "",
"tag": "plain_text"
},
"img_key": "img_v2_0e9d8b45-53c8-4e1d-a7a7-58908ec66cfg",
"tag": "img"
},
{
"tag": "markdown",
"content": "**人才服务中心“拍了拍你”**😘",
"href": {
"urlVal": {
"url": "https://www.feishu.com",
"android_url": "https://developer.android.com/",
"ios_url": "lark://msgcard/unsupported_action",
"pc_url": "https://www.feishu.com"
}
}
},
{
"tag": "hr"
},
{
"tag": "div",
"text": {
"content": "\n亲爱的%s\n恭喜你今天**转正**啦~🎉🎉\n经过一段时间的相处,我们确定你就是与皮皮气味相投的**The one**!\n请在接下来的旅途中,继续**保持热爱,闪闪发光**吧!🌺🌺",
"tag": "lark_md"
}
},
{
"tag": "hr"
}
]
}
}
\ No newline at end of file \ No newline at end of file
{
"config": {
"wide_screen_mode": true
},
"header": {
"template": "red",
"title": {
"content": "✨✨团队有小伙伴转正啦~",
"tag": "plain_text"
}
},
"i18n_elements": {
"zh_cn": [
{
"alt": {
"content": "",
"tag": "plain_text"
},
"img_key": "img_v2_0e9d8b45-53c8-4e1d-a7a7-58908ec66cfg",
"tag": "img"
},
{
"tag": "markdown",
"content": "**人才服务中心“拍了拍你”**😘",
"href": {
"urlVal": {
"url": "https://www.feishu.com",
"android_url": "https://developer.android.com/",
"ios_url": "lark://msgcard/unsupported_action",
"pc_url": "https://www.feishu.com"
}
}
},
{
"tag": "hr"
},
{
"tag": "div",
"text": {
"content": "\n哈喽~ %s 已顺利通过试用期,今天正式转正啦~🎉🎉\n作为上级可以表示一下祝福与欢迎,不要忘了与TA的**1v1沟通**噢🌺🌺",
"tag": "lark_md"
}
},
{
"tag": "hr"
}
]
}
}
\ No newline at end of file \ No newline at end of file
{
"config": {
"wide_screen_mode": true,
"update_multi":true
},
"elements": [
{
"tag": "div",
"text": {
"content": " 🔴 **${section}关键词:**\n**${keyWord}**",
"tag": "lark_md"
}
},
{
"tag": "div",
"text": {
"content": "**${section}反馈数:**\n**${totalFeedBack}**",
"tag": "lark_md"
}
},
{
"tag": "div",
"text": {
"content": "**${section}反馈平均处理时长:**\n**${totalAvgTime}秒**",
"tag": "lark_md"
}
},
{
"fields": [
{
"is_short": false,
"text": {
"content": "**${module}:**\n${section}受理问题最多,有${moduleSum}个",
"tag": "lark_md"
}
},
{
"is_short": false,
"text": {
"content": "",
"tag": "lark_md"
}
},
{
"is_short": false,
"text": {
"content": "**值班人名下剩余未关闭问题:**\n${dutyInfo}",
"tag": "lark_md"
}
},
{
"is_short": false,
"text": {
"content": "",
"tag": "lark_md"
}
},
{
"is_short": false,
"text": {
"content": " <at id=${verifyOpenId}></at>\n作为跟进人响应问题最快,平均${verifyAvgTime}秒",
"tag": "lark_md"
}
}
{
"is_short": false,
"text": {
"content": "",
"tag": "lark_md"
}
},
,
{
"is_short": false,
"text": {
"content": " <at id=${assignOpenId}></at>\n作为负责人响应问题最快,平均${assignAvgTime}秒",
"tag": "lark_md"
}
},
{
"is_short": false,
"text": {
"content": "",
"tag": "lark_md"
}
},
{
"is_short": false,
"text": {
"content": " <at id=${assignOpenId1}></at>\n作为负责人处理问题最快,平均${assignAvgTime1}秒",
"tag": "lark_md"
}
}
,
{
"is_short": false,
"text": {
"content": "",
"tag": "lark_md"
}
},
{
"is_short": false,
"text": {
"content": " <at id=${assignOpenId2}></at>\n作为负责人处理问题最多,共${assignSum2}条",
"tag": "lark_md"
}
}
],
"tag": "div"
},
{
"tag": "hr"
}
],
"header": {
"template": "red",
"title": {
"content": " 📢 ${section}反馈情况汇总",
"tag": "plain_text"
}
}
}
\ No newline at end of file \ No newline at end of file
{
"elements": [
{
"tag": "markdown",
"content": "${content}"
}
<#if action??>,
{
"tag": "action",
"actions": [
{
"tag": "select_static",
"placeholder": {
"tag": "plain_text",
"content": "${action.action_content}"
},
"value":{
"key" : "TEST_DATA.${action.action_type}"
},
"options": [
<#list action.options as option>
{
"text": {
"tag": "plain_text",
"content": "${option.option}"
},
"value": "${option.mobile_option}"
}
<#if option_has_next>,</#if>
</#list>
]
}
]
}
</#if>
],
"config": {
"wide_screen_mode": true,
"update_multi":true
},
"header": {
"template": "${color}",
"title": {
"tag": "plain_text",
"content": "${mobile}"
}
}
}
\ No newline at end of file \ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<jmeterTestPlan version="1.2" properties="5.0" jmeter="5.4.1">
<hashTree>
<TestPlan guiclass="TestPlanGui" testclass="TestPlan" testname="测试计划" enabled="true">
<stringProp name="TestPlan.comments"></stringProp>
<boolProp name="TestPlan.functional_mode">false</boolProp>
<boolProp name="TestPlan.tearDown_on_shutdown">true</boolProp>
<boolProp name="TestPlan.serialize_threadgroups">false</boolProp>
<elementProp name="TestPlan.user_defined_variables" elementType="Arguments" guiclass="ArgumentsPanel" testclass="Arguments" testname="用户定义的变量" enabled="true">
<collectionProp name="Arguments.arguments"/>
</elementProp>
<stringProp name="TestPlan.user_define_classpath"></stringProp>
</TestPlan>
<hashTree>
<ThreadGroup guiclass="ThreadGroupGui" testclass="ThreadGroup" testname="" enabled="true">
<stringProp name="ThreadGroup.on_sample_error">continue</stringProp>
<elementProp name="ThreadGroup.main_controller" elementType="LoopController" guiclass="LoopControlPanel" testclass="LoopController" testname="循环控制器" enabled="true">
<boolProp name="LoopController.continue_forever">false</boolProp>
<stringProp name="LoopController.loops">1</stringProp>
</elementProp>
<stringProp name="ThreadGroup.num_threads">1</stringProp>
<stringProp name="ThreadGroup.ramp_time">1</stringProp>
<boolProp name="ThreadGroup.scheduler">false</boolProp>
<stringProp name="ThreadGroup.duration"></stringProp>
<stringProp name="ThreadGroup.delay"></stringProp>
<boolProp name="ThreadGroup.same_user_on_next_iteration">true</boolProp>
</ThreadGroup>
<hashTree>
<HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="" enabled="true">
<elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" testname="用户定义的变量" enabled="true">
<collectionProp name="Arguments.arguments">
<elementProp name="buffId" elementType="HTTPArgument">
<boolProp name="HTTPArgument.always_encode">false</boolProp>
<stringProp name="Argument.metadata">=</stringProp>
<boolProp name="HTTPArgument.use_equals">true</boolProp>
</elementProp>
</collectionProp>
</elementProp>
<stringProp name="HTTPSampler.domain"></stringProp>
<stringProp name="HTTPSampler.port"></stringProp>
<stringProp name="HTTPSampler.protocol">https</stringProp>
<stringProp name="HTTPSampler.contentEncoding">utf-8</stringProp>
<boolProp name="HTTPSampler.follow_redirects">true</boolProp>
<boolProp name="HTTPSampler.auto_redirects">false</boolProp>
<boolProp name="HTTPSampler.use_keepalive">true</boolProp>
<boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp>
<stringProp name="HTTPSampler.embedded_url_re"></stringProp>
<stringProp name="HTTPSampler.connect_timeout"></stringProp>
<stringProp name="HTTPSampler.response_timeout"></stringProp>
</HTTPSamplerProxy>
<hashTree/>
</hashTree>
</hashTree>
</hashTree>
</jmeterTestPlan>
{
"elements": [
{
"tag": "markdown",
"content": "${content}"
}
<#if appType??>,
{
"tag": "action",
"actions": [
{
"tag": "${appType.tag}",
"placeholder": {
"tag": "plain_text",
"content": "${appType.content}"
},
"value":{
"key" : "appType"
},
"options": [
<#list appType.options as appType_option>
{
"text": {
"tag": "plain_text",
"content": "${appType_option}"
},
"value": "${appType_option}"
}
<#if appType_option_has_next>,</#if>
</#list>
]
}
]
}
</#if>
<#if module??>,
{
"tag": "action",
"actions": [
{
"tag": "${module.tag}",
"placeholder": {
"tag": "plain_text",
"content": "${module.content}"
},
"value":{
"key" : "module"
},
"options": [
<#list module.options as module_option>
{
"text": {
"tag": "plain_text",
"content": "${module_option}"
},
"value": "${module_option}"
}
<#if module_option_has_next>,</#if>
</#list>
]
}
]
}
</#if>
<#if level??>,
{
"tag": "action",
"actions": [
{
"tag": "${level.tag}",
"placeholder": {
"tag": "plain_text",
"content": "${level.content}"
},
"value":{
"key" : "level"
},
"options": [
<#list level.options as levle_option>
{
"text": {
"tag": "plain_text",
"content": "${levle_option}"
},
"value": "${levle_option}"
}<#if levle_option_has_next>,</#if>
</#list>
]
}
]
}
</#if>
<#if assign??>,
{
"tag": "action",
"actions": [
{
"tag": "${assign.tag}",
"placeholder": {
"tag": "plain_text",
"content": "${assign.content}"
},
"value":{
"key" : "assign"
},
"options": [
<#list assign.options as assign_option>
{
"value": "${assign_option}"
}<#if assign_option_has_next>,</#if>
</#list>
]
}
]
}
</#if>
<#if verify??>,
{
"tag": "action",
"actions": [
{
"tag": "${verify.tag}",
"placeholder": {
"tag": "plain_text",
"content": "${verify.content}"
},
"value":{
"key" : "verify"
},
"options": [
<#list verify.options as verify_option>
{
"value": "${verify_option}"
}<#if verify_option_has_next>,</#if>
</#list>
]
}
]
}
</#if>
<#if handle??>,
{
"tag": "div",
"text": {
"content": "若需要转发可重新指派,自己处理请点击“我来”",
"tag": "lark_md"
}
},
{
"tag": "action",
"actions": [
{
"tag": "${handle.tag}",
"text": {
"tag": "plain_text",
"content": "${handle.content}"
},
"type": "primary",
"value":{
"key" : "handle"
}
}
]
}
</#if>
<#if finishTime??>,
{
"tag": "action",
"actions": [
{
"tag": "${finishTime.tag}",
"placeholder": {
"tag": "plain_text",
"content": "${finishTime.content}"
},
"value":{
"key" : "finishTime"
}
}
]
}
</#if>
<#if fileFinish??>,
{
"tag": "div",
"text": {
"tag": "lark_md",
"content": " 负责人任务已完成,您可选择直接完成或归档。注意后续仍需要跟进处理的请选择归档,系统会帮你创建飞书任务,请慎重选择!"
},
"extra": {
"tag": "${fileFinish.tag}",
"options": [
<#list fileFinish.options as fileFinish_option>
{
"text": {
"tag": "plain_text",
"content": "${fileFinish_option}"
},
"value": "${fileFinish_option}"
}<#if fileFinish_option_has_next>,</#if>
</#list>
],
"value": {
"key": "fileFinish"
}
}
}
</#if>
<#if finish??>,
{
"tag": "action",
"actions": [
{
"tag": "${finish.tag}",
"text": {
"tag": "plain_text",
"content": "${finish.content}"
},
"type": "primary",
"value":{
"key" : "finish"
}
}
]
}
</#if>
],
"header": {
"template": "${color}",
"title": {
"tag": "plain_text",
"content": "你有一条新的反馈群消息待处理!"
}
},
"card_link": {
"url": "https://applink.feishu.cn/client/chat/open?openChatId=oc_ffe88a3306221742b18f2bf9e119c17a"
},
"config": {
"wide_screen_mode": true,
"update_multi":true
}
}
\ No newline at end of file \ No newline at end of file
import freemarker.template.TemplateExceptionHandler;
import java.io.*;
import java.util.*;
/**
* @Description: TODO
* @author: charles
* @date: 2022年05月27日 10:13
*/
public class CardGenerator {
private static final String PROJECT_PATH = System.getProperty("user.dir");//项目在硬盘上的基础路径
private static final String TEMPLATE_FILE_PATH =PROJECT_PATH+ "/src/main/resources";//模板位置
private static final List<String> APP_TYPE = Arrays.asList("理想玩伴","皮皮");
public static void main(String[] args) {
List<Map<String, Object>> select_statics = new ArrayList<>();
Map<String, Object> select_actions = new HashMap<>();
select_actions.put("actionContent", "所属App");
select_actions.put("selectOptions", APP_TYPE);
select_statics.add(select_actions);
genMsgCard("red", "一段文本", select_statics);
}
public static void genMsgCard(String color, String content, List<Map<String, Object>> select_statics) {
try {
freemarker.template.Configuration cfg = getConfiguration();
Map<String, Object> data = new HashMap<>();
data.put("color", color);
data.put("content", content);
data.put("select_statics", select_statics);
StringWriter stringWriter = new StringWriter();
cfg.getTemplate("templates/msg-card.ftl").process(data,
stringWriter);
System.out.println(stringWriter.toString());
stringWriter.flush();
System.out.println("生成成功");
} catch (Exception e) {
throw new RuntimeException("生成失败", e);
}
}
private static freemarker.template.Configuration getConfiguration() throws IOException {
freemarker.template.Configuration cfg = new freemarker.template.Configuration(freemarker.template.Configuration.VERSION_2_3_23);
cfg.setDirectoryForTemplateLoading(new File(TEMPLATE_FILE_PATH));
cfg.setDefaultEncoding("UTF-8");
cfg.setTemplateExceptionHandler(TemplateExceptionHandler.IGNORE_HANDLER);
return cfg;
}
}
import com.google.common.base.CaseFormat;
import freemarker.template.TemplateExceptionHandler;
import org.apache.commons.lang3.StringUtils;
import org.mybatis.generator.api.MyBatisGenerator;
import org.mybatis.generator.config.*;
import org.mybatis.generator.internal.DefaultShellCallback;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.*;
/**
* 代码生成器,根据数据表名称生成对应的Model、Mapper、Service、Controller简化开发。
*/
public class CodeGenerator {
//JDBC配置,请修改为你项目的实际配置
private static final String JDBC_URL = "jdbc:mysql://124.71.186.146:3316/t_test";
private static final String JDBC_USERNAME = "weishuangshuang";
private static final String JDBC_PASSWORD = "weishuangshuang2020PipiTest";
private static final String JDBC_DIVER_CLASS_NAME = "com.mysql.jdbc.Driver";
private static final String PROJECT_PATH = System.getProperty("user.dir");//项目在硬盘上的基础路径
private static final String TEMPLATE_FILE_PATH = PROJECT_PATH + "/src/test/resources/generator/template";//模板位置
private static final String JAVA_PATH = "/src/main/java"; //java文件路径
private static final String RESOURCES_PATH = "/src/main/resources";//资源文件路径
private static final String PACKAGE_PATH_SERVICE = packageConvertPath(SERVICE_PACKAGE);//生成的Service存放路径
private static final String PACKAGE_PATH_SERVICE_IMPL = packageConvertPath(SERVICE_IMPL_PACKAGE);//生成的Service实现存放路径
private static final String PACKAGE_PATH_CONTROLLER = packageConvertPath(CONTROLLER_PACKAGE);//生成的Controller存放路径
private static final String AUTHOR = "CodeGenerator";//@author
private static final String DATE = new SimpleDateFormat("yyyy/MM/dd").format(new Date());//@date
public static void main(String[] args) {
genCode("t_feishu_customer_feedback");
//genCodeByCustomModelName("输入表名","输入自定义Model名称");
}
/**
* 通过数据表名称生成代码,Model 名称通过解析数据表名称获得,下划线转大驼峰的形式。
* 如输入表名称 "t_user_detail" 将生成 TUserDetail、TUserDetailMapper、TUserDetailService ...
* @param tableNames 数据表名称...
*/
public static void genCode(String... tableNames) {
for (String tableName : tableNames) {
genCodeByCustomModelName(tableName, null);
}
}
/**
* 通过数据表名称,和自定义的 Model 名称生成代码
* 如输入表名称 "t_user_detail" 和自定义的 Model 名称 "User" 将生成 User、UserMapper、UserService ...
* @param tableName 数据表名称
* @param modelName 自定义的 Model 名称
*/
public static void genCodeByCustomModelName(String tableName, String modelName) {
genModelAndMapper(tableName, modelName);
genService(tableName, modelName);
genController(tableName, modelName);
}
public static void genModelAndMapper(String tableName, String modelName) {
Context context = new Context(ModelType.FLAT);
context.setId("Potato");
context.setTargetRuntime("MyBatis3Simple");
context.addProperty(PropertyRegistry.CONTEXT_BEGINNING_DELIMITER, "`");
context.addProperty(PropertyRegistry.CONTEXT_ENDING_DELIMITER, "`");
JDBCConnectionConfiguration jdbcConnectionConfiguration = new JDBCConnectionConfiguration();
jdbcConnectionConfiguration.setConnectionURL(JDBC_URL);
jdbcConnectionConfiguration.setUserId(JDBC_USERNAME);
jdbcConnectionConfiguration.setPassword(JDBC_PASSWORD);
jdbcConnectionConfiguration.setDriverClass(JDBC_DIVER_CLASS_NAME);
context.setJdbcConnectionConfiguration(jdbcConnectionConfiguration);
PluginConfiguration pluginConfiguration = new PluginConfiguration();
pluginConfiguration.setConfigurationType("tk.mybatis.mapper.generator.MapperPlugin");
pluginConfiguration.addProperty("mappers", MAPPER_INTERFACE_REFERENCE);
context.addPluginConfiguration(pluginConfiguration);
JavaModelGeneratorConfiguration javaModelGeneratorConfiguration = new JavaModelGeneratorConfiguration();
javaModelGeneratorConfiguration.setTargetProject(PROJECT_PATH + JAVA_PATH);
javaModelGeneratorConfiguration.setTargetPackage(MODEL_PACKAGE);
context.setJavaModelGeneratorConfiguration(javaModelGeneratorConfiguration);
SqlMapGeneratorConfiguration sqlMapGeneratorConfiguration = new SqlMapGeneratorConfiguration();
sqlMapGeneratorConfiguration.setTargetProject(PROJECT_PATH + RESOURCES_PATH);
sqlMapGeneratorConfiguration.setTargetPackage("mapper");
context.setSqlMapGeneratorConfiguration(sqlMapGeneratorConfiguration);
JavaClientGeneratorConfiguration javaClientGeneratorConfiguration = new JavaClientGeneratorConfiguration();
javaClientGeneratorConfiguration.setTargetProject(PROJECT_PATH + JAVA_PATH);
javaClientGeneratorConfiguration.setTargetPackage(MAPPER_PACKAGE);
javaClientGeneratorConfiguration.setConfigurationType("XMLMAPPER");
context.setJavaClientGeneratorConfiguration(javaClientGeneratorConfiguration);
TableConfiguration tableConfiguration = new TableConfiguration(context);
tableConfiguration.setTableName(tableName);
if (StringUtils.isNotEmpty(modelName))tableConfiguration.setDomainObjectName(modelName);
tableConfiguration.setGeneratedKey(new GeneratedKey("id", "Mysql", true, null));
context.addTableConfiguration(tableConfiguration);
List<String> warnings;
MyBatisGenerator generator;
try {
Configuration config = new Configuration();
config.addContext(context);
config.validate();
boolean overwrite = true;
DefaultShellCallback callback = new DefaultShellCallback(overwrite);
warnings = new ArrayList<String>();
generator = new MyBatisGenerator(config, callback, warnings);
generator.generate(null);
} catch (Exception e) {
throw new RuntimeException("生成Model和Mapper失败", e);
}
if (generator.getGeneratedJavaFiles().isEmpty() || generator.getGeneratedXmlFiles().isEmpty()) {
throw new RuntimeException("生成Model和Mapper失败:" + warnings);
}
if (StringUtils.isEmpty(modelName)) modelName = tableNameConvertUpperCamel(tableName);
System.out.println(modelName + ".java 生成成功");
System.out.println(modelName + "Mapper.java 生成成功");
System.out.println(modelName + "Mapper.xml 生成成功");
}
public static void genService(String tableName, String modelName) {
try {
freemarker.template.Configuration cfg = getConfiguration();
Map<String, Object> data = new HashMap<>();
data.put("date", DATE);
data.put("author", AUTHOR);
String modelNameUpperCamel = StringUtils.isEmpty(modelName) ? tableNameConvertUpperCamel(tableName) : modelName;
data.put("modelNameUpperCamel", modelNameUpperCamel);
data.put("modelNameLowerCamel", tableNameConvertLowerCamel(tableName));
data.put("basePackage", BASE_PACKAGE);
File file = new File(PROJECT_PATH + JAVA_PATH + PACKAGE_PATH_SERVICE + modelNameUpperCamel + "Service.java");
if (!file.getParentFile().exists()) {
file.getParentFile().mkdirs();
}
cfg.getTemplate("service.ftl").process(data,
new FileWriter(file));
System.out.println(modelNameUpperCamel + "Service.java 生成成功");
File file1 = new File(PROJECT_PATH + JAVA_PATH + PACKAGE_PATH_SERVICE_IMPL + modelNameUpperCamel + "ServiceImpl.java");
if (!file1.getParentFile().exists()) {
file1.getParentFile().mkdirs();
}
cfg.getTemplate("service-impl.ftl").process(data,
new FileWriter(file1));
System.out.println(modelNameUpperCamel + "ServiceImpl.java 生成成功");
} catch (Exception e) {
throw new RuntimeException("生成Service失败", e);
}
}
public static void genController(String tableName, String modelName) {
try {
freemarker.template.Configuration cfg = getConfiguration();
Map<String, Object> data = new HashMap<>();
data.put("date", DATE);
data.put("author", AUTHOR);
String modelNameUpperCamel = StringUtils.isEmpty(modelName) ? tableNameConvertUpperCamel(tableName) : modelName;
data.put("baseRequestMapping", modelNameConvertMappingPath(modelNameUpperCamel));
data.put("modelNameUpperCamel", modelNameUpperCamel);
data.put("modelNameLowerCamel", CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_CAMEL, modelNameUpperCamel));
data.put("basePackage", BASE_PACKAGE);
File file = new File(PROJECT_PATH + JAVA_PATH + PACKAGE_PATH_CONTROLLER + modelNameUpperCamel + "Controller.java");
if (!file.getParentFile().exists()) {
file.getParentFile().mkdirs();
}
//cfg.getTemplate("controller-restful.ftl").process(data, new FileWriter(file));
cfg.getTemplate("controller.ftl").process(data, new FileWriter(file));
System.out.println(modelNameUpperCamel + "Controller.java 生成成功");
} catch (Exception e) {
throw new RuntimeException("生成Controller失败", e);
}
}
private static freemarker.template.Configuration getConfiguration() throws IOException {
freemarker.template.Configuration cfg = new freemarker.template.Configuration(freemarker.template.Configuration.VERSION_2_3_23);
cfg.setDirectoryForTemplateLoading(new File(TEMPLATE_FILE_PATH));
cfg.setDefaultEncoding("UTF-8");
cfg.setTemplateExceptionHandler(TemplateExceptionHandler.IGNORE_HANDLER);
return cfg;
}
private static String tableNameConvertLowerCamel(String tableName) {
return CaseFormat.LOWER_UNDERSCORE.to(CaseFormat.LOWER_CAMEL, tableName.toLowerCase());
}
private static String tableNameConvertUpperCamel(String tableName) {
return CaseFormat.LOWER_UNDERSCORE.to(CaseFormat.UPPER_CAMEL, tableName.toLowerCase());
}
private static String tableNameConvertMappingPath(String tableName) {
tableName = tableName.toLowerCase();//兼容使用大写的表名
return "/" + (tableName.contains("_") ? tableName.replaceAll("_", "/") : tableName);
}
private static String modelNameConvertMappingPath(String modelName) {
String tableName = CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_UNDERSCORE, modelName);
return tableNameConvertMappingPath(tableName);
}
private static String packageConvertPath(String packageName) {
return String.format("/%s/", packageName.contains(".") ? packageName.replaceAll("\\.", "/") : packageName);
}
}
import cn.hutool.core.util.RandomUtil;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
/**
* @Description: TODO
* @author: charles
* @date: 2022年07月25日 14:23
*/
public class RedisTest {
public static void main(String[] args) {
}
}
/*
Navicat MySQL Data Transfer
Source Server : Localhost
Source Server Version : 50713
Source Host : localhost:3306
Source Database : test
Target Server Type : MYSQL
Target Server Version : 50713
File Encoding : 65001
Date: 2017-06-23 14:25:27
*/
SET FOREIGN_KEY_CHECKS=0;
-- ----------------------------
-- Table structure for user
-- ----------------------------
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(255) NOT NULL,
`password` varchar(255) NOT NULL,
`nick_name` varchar(255) DEFAULT NULL,
`sex` int(1) DEFAULT NULL,
`register_date` datetime NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of user
-- ----------------------------
INSERT INTO `user` VALUES ('1', '89921218@qq.com', '1ee04e0b1cb5af7367c80c22e42efd8b', '土豆', '1', '2017-06-23 14:24:23');
INSERT INTO `user` VALUES ('2', '2@qq.com', '1ee04e0b1cb5af7367c80c22e42efd8b', '土豆-2', '1', '2017-06-23 14:24:23');
INSERT INTO `user` VALUES ('3', '3@qq.com', '1ee04e0b1cb5af7367c80c22e42efd8b', '土豆-3', '1', '2017-06-23 14:24:23');
INSERT INTO `user` VALUES ('4', '4@qq.com', '1ee04e0b1cb5af7367c80c22e42efd8b', '土豆-4', '1', '2017-06-23 14:24:23');
INSERT INTO `user` VALUES ('5', '5@qq.com', '1ee04e0b1cb5af7367c80c22e42efd8b', '土豆-5', '1', '2017-06-23 14:24:23');
INSERT INTO `user` VALUES ('6', '6@qq.com', '1ee04e0b1cb5af7367c80c22e42efd8b', '土豆-6', '1', '2017-06-23 14:24:23');
INSERT INTO `user` VALUES ('7', '7@qq.com', '1ee04e0b1cb5af7367c80c22e42efd8b', '土豆-7', '1', '2017-06-23 14:24:23');
INSERT INTO `user` VALUES ('8', '8@qq.com', '1ee04e0b1cb5af7367c80c22e42efd8b', '土豆-8', '1', '2017-06-23 14:24:23');
INSERT INTO `user` VALUES ('9', '9@qq.com', '1ee04e0b1cb5af7367c80c22e42efd8b', '土豆-9', '1', '2017-06-23 14:24:23');
INSERT INTO `user` VALUES ('10', '10@qq.com', '1ee04e0b1cb5af7367c80c22e42efd8b', '土豆-10', '1', '2017-06-23 14:24:23');
SET FOREIGN_KEY_CHECKS=1;
package ${basePackage}.web;
import ${basePackage}.core.Result;
import ${basePackage}.core.ResultGenerator;
import ${basePackage}.model.${modelNameUpperCamel};
import ${basePackage}.service.${modelNameUpperCamel}Service;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import java.util.List;
/**
* Created by ${author} on ${date}.
*/
@RestController
@RequestMapping("${baseRequestMapping}")
public class ${modelNameUpperCamel}Controller {
@Resource
private ${modelNameUpperCamel}Service ${modelNameLowerCamel}Service;
@PostMapping
public Result add(@RequestBody ${modelNameUpperCamel} ${modelNameLowerCamel}) {
${modelNameLowerCamel}Service.save(${modelNameLowerCamel});
return ResultGenerator.genSuccessResult();
}
@DeleteMapping("/{id}")
public Result delete(@PathVariable Integer id) {
${modelNameLowerCamel}Service.deleteById(id);
return ResultGenerator.genSuccessResult();
}
@PutMapping
public Result update(@RequestBody ${modelNameUpperCamel} ${modelNameLowerCamel}) {
${modelNameLowerCamel}Service.update(${modelNameLowerCamel});
return ResultGenerator.genSuccessResult();
}
@GetMapping("/{id}")
public Result detail(@PathVariable Integer id) {
${modelNameUpperCamel} ${modelNameLowerCamel} = ${modelNameLowerCamel}Service.findById(id);
return ResultGenerator.genSuccessResult(${modelNameLowerCamel});
}
@GetMapping
public Result list(@RequestParam(defaultValue = "0") Integer page, @RequestParam(defaultValue = "0") Integer size) {
PageHelper.startPage(page, size);
List<${modelNameUpperCamel}> list = ${modelNameLowerCamel}Service.findAll();
PageInfo pageInfo = new PageInfo(list);
return ResultGenerator.genSuccessResult(pageInfo);
}
}
package ${basePackage}.web;
import ${basePackage}.core.Result;
import ${basePackage}.core.ResultGenerator;
import ${basePackage}.model.${modelNameUpperCamel};
import ${basePackage}.service.${modelNameUpperCamel}Service;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import java.util.List;
/**
* Created by ${author} on ${date}.
*/
@RestController
@RequestMapping("${baseRequestMapping}")
public class ${modelNameUpperCamel}Controller {
@Resource
private ${modelNameUpperCamel}Service ${modelNameLowerCamel}Service;
@PostMapping("/add")
public Result add(${modelNameUpperCamel} ${modelNameLowerCamel}) {
${modelNameLowerCamel}Service.save(${modelNameLowerCamel});
return ResultGenerator.genSuccessResult();
}
@PostMapping("/delete")
public Result delete(@RequestParam Integer id) {
${modelNameLowerCamel}Service.deleteById(id);
return ResultGenerator.genSuccessResult();
}
@PostMapping("/update")
public Result update(${modelNameUpperCamel} ${modelNameLowerCamel}) {
${modelNameLowerCamel}Service.update(${modelNameLowerCamel});
return ResultGenerator.genSuccessResult();
}
@PostMapping("/detail")
public Result detail(@RequestParam Integer id) {
${modelNameUpperCamel} ${modelNameLowerCamel} = ${modelNameLowerCamel}Service.findById(id);
return ResultGenerator.genSuccessResult(${modelNameLowerCamel});
}
@PostMapping("/list")
public Result list(@RequestParam(defaultValue = "0") Integer page, @RequestParam(defaultValue = "0") Integer size) {
PageHelper.startPage(page, size);
List<${modelNameUpperCamel}> list = ${modelNameLowerCamel}Service.findAll();
PageInfo pageInfo = new PageInfo(list);
return ResultGenerator.genSuccessResult(pageInfo);
}
}
package ${basePackage}.service.impl;
import ${basePackage}.dao.${modelNameUpperCamel}Mapper;
import ${basePackage}.model.${modelNameUpperCamel};
import ${basePackage}.service.${modelNameUpperCamel}Service;
import ${basePackage}.core.AbstractService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
/**
* Created by ${author} on ${date}.
*/
@Service
@Transactional
public class ${modelNameUpperCamel}ServiceImpl extends AbstractService<${modelNameUpperCamel}> implements ${modelNameUpperCamel}Service {
@Resource
private ${modelNameUpperCamel}Mapper ${modelNameLowerCamel}Mapper;
}
package ${basePackage}.service;
import ${basePackage}.model.${modelNameUpperCamel};
import ${basePackage}.core.Service;
/**
* Created by ${author} on ${date}.
*/
public interface ${modelNameUpperCamel}Service extends Service<${modelNameUpperCamel}> {
}
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!