()
62 | }
--------------------------------------------------------------------------------
/src/main/kotlin/org/lynxz/server/bean/MessageTextBean.java:
--------------------------------------------------------------------------------
1 | package org.lynxz.server.bean;
2 |
3 | /**
4 | * Created by lynxz on 22/03/2017.
5 | * 钉钉发送text类型消息实体
6 | */
7 | public class MessageTextBean {
8 |
9 | /**
10 | * touser : manager104
11 | * agentid : 8028911
12 | * msgtype : text
13 | * text : {"content":"测试[链接](http://www.baidu.com)"}
14 | */
15 | private String touser;
16 | private String agentid;
17 | private String msgtype;
18 | private TextBean text;
19 |
20 | public String getTouser() {
21 | return touser;
22 | }
23 |
24 | public void setTouser(String touser) {
25 | this.touser = touser;
26 | }
27 |
28 | public String getAgentid() {
29 | return agentid;
30 | }
31 |
32 | public void setAgentid(String agentid) {
33 | this.agentid = agentid;
34 | }
35 |
36 | public String getMsgtype() {
37 | return msgtype;
38 | }
39 |
40 | public void setMsgtype(String msgtype) {
41 | this.msgtype = msgtype;
42 | }
43 |
44 | public TextBean getText() {
45 | return text;
46 | }
47 |
48 | public void setText(TextBean text) {
49 | this.text = text;
50 | }
51 |
52 | public static class TextBean {
53 | /**
54 | * content : 测试[链接](http://www.baidu.com)
55 | */
56 |
57 | private String content;
58 |
59 | public String getContent() {
60 | return content;
61 | }
62 |
63 | public void setContent(String content) {
64 | this.content = content;
65 | }
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/src/main/kotlin/org/lynxz/server/bean/TgSendMessageRespBean.kt:
--------------------------------------------------------------------------------
1 | package org.lynxz.server.bean
2 |
3 | /** 调用tg sendmessage接口的返回结果
4 | *
5 | * {
6 | * "ok": true, // 发送消息收成功
7 | * "error_code": 200,
8 | * "description": "",
9 | * "result": { // ok=true时有值
10 | * "message_id": 25,
11 | * "from": {
12 | * "id": 974962570,
13 | * "is_bot": true,
14 | * "first_name": "\u77ed\u606f\u8f6c\u53d1\u63a5\u6536bot",
15 | * "username": "lynxzz_message_bot"
16 | * },
17 | * "chat": { // 接收方信息
18 | * "id": 417044892,
19 | * "first_name": "\u4f60\u8bf4\u5f97\u5bf9tg",
20 | * "username": "us3jxjk8d",
21 | * "type": "private" // 个人聊天,对应的还有 channel, group,supergroup
22 | * },
23 | * "date": 1570245182,
24 | * "text": "hellobot" // 发送的消息内容
25 | * }
26 | * }
27 | *
28 | */
29 | data class TgSendMessageRespBean(
30 | val ok: Boolean, // 发送消息是否成功
31 | val result: TgRespResult?, // ok=true时有值
32 | val description: String, // ok=false时有值
33 | val error_code: Long// ok=false时有值
34 | )
35 |
36 | data class TgRespResult(
37 | val chat: TgRespChat,
38 | val date: Long,
39 | val from: TgRespFrom,
40 | val message_id: Long,
41 | val text: String?
42 | )
43 |
44 | data class TgRespChat(
45 | val first_name: String,
46 | val id: Long,
47 | val type: String,
48 | val username: String
49 | )
50 |
51 | data class TgRespFrom(
52 | val first_name: String,
53 | val id: Long,
54 | val is_bot: Boolean,
55 | val username: String
56 | )
--------------------------------------------------------------------------------
/src/main/kotlin/org/lynxz/server/service/JenkinsService.kt:
--------------------------------------------------------------------------------
1 | package org.lynxz.server.service
2 |
3 | import com.google.gson.Gson
4 | import org.lynxz.server.config.ConstantsPara
5 | import org.lynxz.server.msec2date
6 | import org.lynxz.server.network.HttpManager
7 | import org.lynxz.webhook.bean.JenkensUploadPygerBean
8 | import javax.servlet.http.HttpServletRequest
9 |
10 | /**
11 | * Created by lynxz on 28/08/2017.
12 | * 处理自定义的jenkins通知消息
13 | * 这个需要按需在jenkins脚本中调用网络请求处理,不具有通用性
14 | * 我自己用的, 在header中有 "userName" 和 "msg" 字段用于指示谁发起的打包和打包概要说明
15 | * 具体可参考我的博客: http://www.jianshu.com/p/733cfa75ff8b
16 | */
17 | class JenkinsService : PlatformService {
18 | override fun process(req: HttpServletRequest?) {
19 | req?.let {
20 | val msg = req.getParameter("msg")
21 | Gson().fromJson(msg, JenkensUploadPygerBean::class.java)?.let {
22 | // 发起 jenkins 打包的用户名,同时消息的接收方
23 | // 注意 userName需要与钉钉后台通讯录中的用户名一致或者能够一一映射才可
24 | val userName = req.getParameter("userName") ?: ConstantsPara.defaultNoticeUserName
25 |
26 | StringBuilder(100).apply {
27 | append("jenkins上传apk到蒲公英-code=${it.code}\n")
28 | append("错误消息: ${it.message ?: "无"}")
29 | it.data?.let {
30 | append("名称: ${it.appName}-v${it.appVersion}(build ${it.appBuildVersion})\n")
31 | append("版本说明: ${it.appUpdateDescription}\n")
32 | append("下载链接: https://www.pgyer.com/${it.appShortcutUrl}\n")
33 | append("二维码链接: ${it.appQRCodeURL}\n")
34 | }
35 | append("服务器时间: ${msec2date()}")
36 | HttpManager.sendTextMessage(userName, null, toString())
37 | }
38 | }
39 | }
40 | }
41 | }
--------------------------------------------------------------------------------
/src/main/kotlin/org/lynxz/server/network/ApiService.kt:
--------------------------------------------------------------------------------
1 | package org.lynxz.server.network
2 |
3 | import io.reactivex.Observable
4 | import org.lynxz.server.bean.*
5 | import retrofit2.http.*
6 |
7 | /**
8 | * Created by lynxz on 25/08/2017.
9 | * 钉钉相关接口请求
10 | */
11 | interface ApiService {
12 |
13 | /**
14 | * [获取钉钉AccessToken](https://open-doc.dingtalk.com/docs/doc.htm?spm=a219a.7629140.0.0.dfrJ5p&treeId=172&articleId=104980&docType=1)
15 | * @param id corpid 企业id
16 | * @param secret corpsecret 企业应用的凭证密钥
17 | * */
18 | @GET("gettoken")
19 | fun getAccessToken(@Query("corpid") id: String, @Query("corpsecret") secret: String): Observable
20 |
21 | /**
22 | * [获取部门列表信息](https://open-doc.dingtalk.com/docs/doc.htm?spm=a219a.7629140.0.0.xIVqtB&treeId=172&articleId=104979&docType=1#s0)
23 | * 只会返回 用户名 和 id
24 | */
25 | @GET("department/list")
26 | fun getDepartmentList(): Observable
27 |
28 | /**
29 | * [获取部门用户详情](https://ding-doc.dingtalk.com/doc#/serverapi2/ege851)
30 | * 会返回成员用户的详细信息,包括手机号码,归属部门等
31 | * */
32 | @GET("user/listbypage")
33 | fun getDepartmentMemberDetailList(@Query("department_id") id: Int = 1, // 部门id,默认为跟部门
34 | @Query("offset") offset: Int = 0, // 偏移量
35 | @Query("size") size: Int = 40, // 分页大小,默认为20,最大100
36 | @Query("order") order: String = "entry_asc" // 列表排序规则, 默认按进入部门的时间升序
37 | ): Observable
38 |
39 | /**
40 | * [向指定用户发送普通文本消息](https://open-doc.dingtalk.com/docs/doc.htm?spm=a219a.7629140.0.0.oavHEu&treeId=172&articleId=104973&docType=1#s2)
41 | */
42 | @POST("message/send")
43 | fun sendTextMessage(@Body bean: MessageTextBean): Observable
44 |
45 |
46 | /**
47 | * [tg getUpdates](https://core.telegram.org/bots/api#getupdates)
48 | * */
49 | @GET
50 | fun getTgUpdates(@Url url: String): Observable
51 |
52 | /**
53 | * [向tg发送消息](https://core.telegram.org/bots/api#sendmessage)
54 | * */
55 | @POST
56 | fun sendTgBotMessage(@Url url: String, @Body bean: TgSendMessageReqBean): Observable
57 |
58 | }
--------------------------------------------------------------------------------
/src/main/kotlin/org/lynxz/server/bean/MessageResponseBean.java:
--------------------------------------------------------------------------------
1 | package org.lynxz.server.bean;
2 |
3 | /**
4 | * Created by lynxz on 21/03/2017.
5 | * 钉钉发送消息接口的返回信息
6 | */
7 | public class MessageResponseBean {
8 |
9 | /**
10 | * errcode : 0
11 | * errmsg : ok
12 | * invaliduser : UserID1|UserID2
13 | * invalidparty : PartyID1
14 | * forbiddenUserId : UserID1|UserID2
15 | * messageId : xxxxxxxxxxxxxxxx
16 | */
17 | private int errcode;
18 | private String errmsg;
19 |
20 | private String invaliduser;
21 | private String invalidparty;
22 | private String forbiddenUserId;
23 | private String messageId;
24 |
25 | public int getErrcode() {
26 | return errcode;
27 | }
28 |
29 | public void setErrcode(int errcode) {
30 | this.errcode = errcode;
31 | }
32 |
33 | public String getErrmsg() {
34 | return errmsg;
35 | }
36 |
37 | public void setErrmsg(String errmsg) {
38 | this.errmsg = errmsg;
39 | }
40 |
41 | public String getInvaliduser() {
42 | return invaliduser;
43 | }
44 |
45 | public void setInvaliduser(String invaliduser) {
46 | this.invaliduser = invaliduser;
47 | }
48 |
49 | public String getInvalidparty() {
50 | return invalidparty;
51 | }
52 |
53 | public void setInvalidparty(String invalidparty) {
54 | this.invalidparty = invalidparty;
55 | }
56 |
57 | public String getForbiddenUserId() {
58 | return forbiddenUserId;
59 | }
60 |
61 | public void setForbiddenUserId(String forbiddenUserId) {
62 | this.forbiddenUserId = forbiddenUserId;
63 | }
64 |
65 | public String getMessageId() {
66 | return messageId;
67 | }
68 |
69 | public void setMessageId(String messageId) {
70 | this.messageId = messageId;
71 | }
72 |
73 | @Override
74 | public String toString() {
75 | return "MessageResponseBean{" +
76 | "errcode=" + errcode +
77 | ", errmsg='" + errmsg + '\'' +
78 | ", invaliduser='" + invaliduser + '\'' +
79 | ", invalidparty='" + invalidparty + '\'' +
80 | ", forbiddenUserId='" + forbiddenUserId + '\'' +
81 | ", messageId='" + messageId + '\'' +
82 | '}';
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/src/main/kotlin/org/lynxz/server/Router.kt:
--------------------------------------------------------------------------------
1 | package org.lynxz.server
2 |
3 | import org.lynxz.server.config.KeyNames
4 | import org.lynxz.server.config.PathInfo
5 | import org.lynxz.server.network.HttpManager
6 | import org.lynxz.server.service.*
7 | import org.lynxz.server.util.CommonUtil
8 | import javax.servlet.http.HttpServletRequest
9 |
10 | /**
11 | * Created by lynxz on 28/08/2017.
12 | * 根据header头部信息,调用对应的处理类
13 | */
14 | object Router {
15 | fun route(req: HttpServletRequest?) {
16 | req?.let {
17 | val userAgent = req.getHeader(KeyNames.HEADER_USER_AGENT)
18 | val gitlabHeader = req.getHeader(KeyNames.HEADER_GITLAB)
19 | val pathInfo = req.pathInfo
20 | println("${msec2date()} Router userAgent is: $userAgent ,gitlabHeader is: $gitlabHeader ,req.pathInfo is: $pathInfo")
21 | if (!pathInfo.isNullOrBlank()) {
22 | when {
23 | pathInfo.endsWith(PathInfo.KEY_ACTION_REFRESH_TOKEN, true) -> {
24 | HttpManager.refreshAccessToken()
25 | HttpManager.getTgBotUpdates()
26 | }
27 | pathInfo.endsWith(PathInfo.KEY_ACTION_UPDATE_DEPARTMENT_INFO, true) -> HttpManager.getDepartmentInfo()
28 | pathInfo.endsWith(PathInfo.KEY_ACTION_SAVE_DATA, true) -> CommonUtil.log2File(req.queryString)
29 | pathInfo.endsWith(PathInfo.KEY_ACTION_SEND_MSG, true) -> SendMessageService().process(req)
30 | else -> println("cannot process this type of path, ignore....$pathInfo")
31 | }
32 | } else if (!gitlabHeader.isNullOrBlank()) {
33 | GitlabService().process(req)
34 | } else if (!userAgent.isNullOrBlank()) {
35 | when {
36 | userAgent.contains(KeyNames.HEADER_GIRA) -> GiraService().process(req)
37 | userAgent.contains(KeyNames.HEADER_PGYER) -> PgyerService().process(req)
38 | userAgent.contains(KeyNames.HEADER_JENKINS_PGYER) -> JenkinsService().process(req)
39 | else -> println("connot process this type of user-agent: $userAgent")
40 | }
41 | } else {
42 | println("cannot process this type of request, ignore....${req.requestURL}")
43 | }
44 | }
45 | }
46 | }
--------------------------------------------------------------------------------
/src/main/kotlin/org/lynxz/server/service/SendMessageService.kt:
--------------------------------------------------------------------------------
1 | package org.lynxz.server.service
2 |
3 | import org.lynxz.server.bean.ImType
4 | import org.lynxz.server.bean.SendMessageReqBean
5 | import org.lynxz.server.bean.TgSendMessageReqBean
6 | import org.lynxz.server.config.ConstantsPara
7 | import org.lynxz.server.convertBody
8 | import org.lynxz.server.network.HttpManager
9 | import javax.servlet.http.HttpServletRequest
10 |
11 | /**
12 | * 发送钉钉消息给指定人员或者群组
13 | * */
14 | class SendMessageService : PlatformService {
15 | override fun process(req: HttpServletRequest?) {
16 |
17 | convertBody(req?.inputStream, SendMessageReqBean::class.java)?.let {
18 | when (it.imType) {
19 | ImType.TG -> {
20 | if (it.name.isNullOrBlank()) {
21 | it.name = ConstantsPara.defaultTgUserName
22 | }
23 |
24 | if (it.tgBotToken.isBlank()) {
25 | it.tgBotToken = ConstantsPara.defaultTgBotToken
26 | }
27 |
28 | val key = "${it.tgBotToken}_${it.name}"
29 | val doOnComplete = {
30 | val chatId = ConstantsPara.tgChatInfoMap[key]
31 | HttpManager.sendTgMessage(TgSendMessageReqBean(chatId, it.content), it.tgBotToken)
32 | }
33 | if (ConstantsPara.tgChatInfoMap[key] == null) {
34 | HttpManager.getTgBotUpdates(it.tgBotToken, doOnComplete)
35 | } else {
36 | doOnComplete.invoke()
37 | }
38 | }
39 | ImType.DingDing -> {
40 | var departmentId = 1
41 | if (it.departmentName.isNullOrBlank() || ConstantsPara.departmentList == null) {
42 | departmentId = 1
43 | } else {
44 | ConstantsPara.departmentList!!.department.forEach { depart ->
45 | if (depart.name.equals(it.departmentName, true)) {
46 | departmentId = depart.id
47 | }
48 | }
49 | }
50 |
51 | HttpManager.sendTextMessage(it.name, it.mobile, it.content, departmentId)
52 | }
53 | }
54 |
55 | }
56 | }
57 | }
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | set DIRNAME=%~dp0
12 | if "%DIRNAME%" == "" set DIRNAME=.
13 | set APP_BASE_NAME=%~n0
14 | set APP_HOME=%DIRNAME%
15 |
16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
17 | set DEFAULT_JVM_OPTS=
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windows variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 |
53 | :win9xME_args
54 | @rem Slurp the command line arguments.
55 | set CMD_LINE_ARGS=
56 | set _SKIP=2
57 |
58 | :win9xME_args_slurp
59 | if "x%~1" == "x" goto execute
60 |
61 | set CMD_LINE_ARGS=%*
62 |
63 | :execute
64 | @rem Setup the command line
65 |
66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
67 |
68 | @rem Execute Gradle
69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
70 |
71 | :end
72 | @rem End local scope for the variables with windows NT shell
73 | if "%ERRORLEVEL%"=="0" goto mainEnd
74 |
75 | :fail
76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
77 | rem the _cmd.exe /c_ return code!
78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
79 | exit /b 1
80 |
81 | :mainEnd
82 | if "%OS%"=="Windows_NT" endlocal
83 |
84 | :omega
85 |
--------------------------------------------------------------------------------
/src/main/kotlin/org/lynxz/server/bean/DepartmentListBean.java:
--------------------------------------------------------------------------------
1 | package org.lynxz.server.bean;
2 |
3 | import java.util.List;
4 |
5 | /**
6 | * Created by lynxz on 08/03/2017.
7 | * 部门列表信息
8 | */
9 | public class DepartmentListBean {
10 |
11 | /**
12 | * errcode : 0
13 | * errmsg : ok
14 | * department : [{"id":2,"name":"钉钉事业部","parentid":1,"createDeptGroup":true,"autoAddUser":true},{"id":3,"name":"服务端开发组","parentid":2,"createDeptGroup":false,"autoAddUser":false}]
15 | */
16 |
17 | private int errcode;
18 | private String errmsg;
19 | private List department;
20 |
21 | public int getErrcode() {
22 | return errcode;
23 | }
24 |
25 | public void setErrcode(int errcode) {
26 | this.errcode = errcode;
27 | }
28 |
29 | public String getErrmsg() {
30 | return errmsg;
31 | }
32 |
33 | public void setErrmsg(String errmsg) {
34 | this.errmsg = errmsg;
35 | }
36 |
37 | public List getDepartment() {
38 | return department;
39 | }
40 |
41 | public void setDepartment(List department) {
42 | this.department = department;
43 | }
44 |
45 | public static class DepartmentBean {
46 | /**
47 | * id : 2
48 | * name : 钉钉事业部
49 | * parentid : 1
50 | * createDeptGroup : true
51 | * autoAddUser : true
52 | */
53 |
54 | private int id;
55 | private String name;
56 | private int parentid;
57 | private boolean createDeptGroup;
58 | private boolean autoAddUser;
59 |
60 | public int getId() {
61 | return id;
62 | }
63 |
64 | public void setId(int id) {
65 | this.id = id;
66 | }
67 |
68 | public String getName() {
69 | return name;
70 | }
71 |
72 | public void setName(String name) {
73 | this.name = name;
74 | }
75 |
76 | public int getParentid() {
77 | return parentid;
78 | }
79 |
80 | public void setParentid(int parentid) {
81 | this.parentid = parentid;
82 | }
83 |
84 | public boolean isCreateDeptGroup() {
85 | return createDeptGroup;
86 | }
87 |
88 | public void setCreateDeptGroup(boolean createDeptGroup) {
89 | this.createDeptGroup = createDeptGroup;
90 | }
91 |
92 | public boolean isAutoAddUser() {
93 | return autoAddUser;
94 | }
95 |
96 | public void setAutoAddUser(boolean autoAddUser) {
97 | this.autoAddUser = autoAddUser;
98 | }
99 | }
100 | }
101 |
--------------------------------------------------------------------------------
/src/main/kotlin/org/lynxz/server/bean/JenkensUploadPygerBean.kt:
--------------------------------------------------------------------------------
1 | package org.lynxz.webhook.bean
2 |
3 | /**
4 | * Created by lynxz on 06/05/2017.
5 | * 使用jenkins打包生成pak后,上传到蒲公英,蒲公英返回的响应内容
6 | * 本服务器依据该内容通知钉钉
7 | * [蒲公英上传接口说明](https://www.pgyer.com/doc/api#uploadApp)
8 | */
9 | data class JenkensUploadPygerBean(var code: Int = 0,
10 | var message: String? = null,
11 | var data: DataBean? = null) {
12 |
13 | /**
14 | * code : 0
15 | * message :
16 | * data : {"appKey":"451359ad7b69bfb21f4d124588f749f0","userKey":"9f2634129de0e58c06366b6e4c355b6f","appType":"2","appIsLastest":"1","appFileSize":"3333852","appName":"PrivatePhoto","appVersion":"0.1.1","appVersionNo":"2","appBuildVersion":"5","appIdentifier":"org.lynxz.privatephoto","appIcon":"33b678170f8b387a6923d428f772a178","appDescription":"","appUpdateDescription":"测试python上传文件功能21","appScreenshots":"","appShortcutUrl":"5faf","appCreated":"2017-05-06 21:00:50","appUpdated":"2017-05-06 21:00:50","appQRCodeURL":"https://static.pgyer.com/app/qrcodeHistory/3a41378d4e000e4562095290920a1142d86a8accddfecab490691faf5486c89a"}
17 | */
18 | class DataBean {
19 | /**
20 | * appKey : 451359ad7b69bfb21f4d124588f749f0
21 | * userKey : 9f2634129de0e58c06366b6e4c355b6f
22 | * appType : 2
23 | * appIsLastest : 1
24 | * appFileSize : 3333852
25 | * appName : PrivatePhoto
26 | * appVersion : 0.1.1
27 | * appVersionNo : 2
28 | * appBuildVersion : 5
29 | * appIdentifier : org.lynxz.privatephoto
30 | * appIcon : 33b678170f8b387a6923d428f772a178
31 | * appDescription :
32 | * appUpdateDescription : 测试python上传文件功能21
33 | * appScreenshots :
34 | * appShortcutUrl : 5faf
35 | * appCreated : 2017-05-06 21:00:50
36 | * appUpdated : 2017-05-06 21:00:50
37 | * appQRCodeURL : https://static.pgyer.com/app/qrcodeHistory/3a41378d4e000e4562095290920a1142d86a8accddfecab490691faf5486c89a
38 | */
39 |
40 | var appKey: String? = null
41 | var userKey: String? = null
42 | var appType: String? = null
43 | var appIsLastest: String? = null
44 | var appFileSize: String? = null
45 | var appName: String? = null
46 | var appVersion: String? = null
47 | var appVersionNo: String? = null
48 | var appBuildVersion: String? = null
49 | var appIdentifier: String? = null
50 | var appIcon: String? = null
51 | var appDescription: String? = null
52 | var appUpdateDescription: String? = null
53 | var appScreenshots: String? = null
54 | var appShortcutUrl: String? = null
55 | var appCreated: String? = null
56 | var appUpdated: String? = null
57 | var appQRCodeURL: String? = null
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | > 使用Gradle和Kotlin重写一下之前 [钉钉通知服务器](https://github.com/lucid-lynxz/Webhook_server) 顺便再熟悉下服务端基本编写操作
2 |
3 | [博客: 打通gitlab与钉钉之间的联系](https://juejin.im/post/5a433b206fb9a0452725de4f)
4 |
5 | ### 1. 配置 `config.properties` 文件参数
6 | clone项目后,请在项目根目录下创建 `src/main/webapp/config.properties` 并设置如下属性值,以便获取钉钉相关参数
7 | ```properties
8 | # 必填,企业在钉钉中的标识,每个企业拥有一个唯一的CorpID
9 | corpid=dingaa******c2f4657eb6378f
10 | # 必填,企业每个应用的凭证密钥
11 | corpsecret=Bp1_HoQej2s******LE7aRWaJm_lYpSMYvVQi-_Q
12 | # 必填,钉钉微应用id
13 | agentId=123456
14 |
15 | # 可选,gitlab默认通知审核的人员或者gira默认通知的bug归属人的真实名字,用于匹配钉钉通讯录获取userId
16 | # 同时也是蒲公英上传应用后回调通知的默认用户
17 | defaultNoticeUserName="张三"
18 | # 可选,jira 详情面板网址前缀(带斜杠),其后添加jira bugId接口拼接成完整地址,若无请放空
19 | jira_borwse_url=http://jira.soundbus.tech/browse/
20 | # gitlab合并的目标分支是该分支时, 此hook请求才需要发送钉钉消息,不填的话,默认master
21 | gitlab_push_merge_branch=master
22 |
23 | #---IM-Telegram 配置---
24 | # 用于接收消息的默认bot token
25 | tg_bot_token=9749123470:abced
26 | # 默认接收消息的tg用户username
27 | tg_user_name=helobotxxx
28 | ```
29 |
30 | ### 2. 通讯录规则
31 | 在通讯录root部门中添加所有人,以便发送消息到特定用户时可以从root部门中查询得到用户id;
32 | 根据gitlab项目路径配置各项目部门,比如:
33 | * gitlab项目地址为: https://gitlab.lynxz.org/demo-android/detail-android
34 | 则表示项目名称(`name`) 为: `detail-android` ,项目所在空间(`namespace`)为: `demo-android`
35 | * 在钉钉后台通讯录中需要先创建部门: `demo_android` ,然后创建其子部门 `detail_android`
36 | 注意: 由于钉钉部门名称不允许使用 `-`,因此创建时改为 `_` 替代
37 | * 目前只支持两级部门结构,若有多个部门符合上述规则gitlab merge通过时会通知所有匹配的部门成员;
38 | 
39 |
40 | ### 3. 功能url
41 | 1. `{serverHost}/action/refreshToken` 重新刷新access_token,并重新获取通讯录
42 | 2. `{serverHost}/action/updateDepartmentInfo` 请求该url会立即重新获取钉钉通讯录信息,用于用户更新了钉钉通讯录后主动触发服务器刷新数据
43 | 3. `{serverHost}/action/save_data?**=**` 可以将 query 数据保存到文件中
44 | 如: 1.1.1.1:8080/server-V0.1.4/action/updateDepartmentInfo
45 | 4. `{serverHost}/action/send_msg` post 请求,发送消息给钉钉指定的人员或者部门
46 | ```shell
47 | curl -d "{\"name\":\"\",\"mobile\":\"\",\"content\":\"消息内容\",\"departmentName\":\"部门名称\"}" {serverHost}/action/send_msg
48 | ```
49 | 5. `{serverHost}/action/send_msg` post 请求,发送消息给钉tg指定用户
50 | ```shell
51 | curl -d "{\"name\":\"目标用户userName\",\"imType\":\"Telegram\",\"content\":\"消息内容\",\"tgBotToken\":\"tg bot token\"}" {serverHost}/action/send_msg
52 | ```
53 | name: 发送钉钉消息时可空,钉钉用户姓名或者备注名, 发送tg消息时必填,为用户昵称或者userName
54 | content: 必填,要发送的消息
55 | mobile: 可空, 钉钉用户手机号,优先匹配
56 | imType: 可空,用于发送的IM类型,目前支持两种: Telegram 和 DingDing, 默认为 DingDing
57 | departmentName: 发送钉钉消息时必填,为用户所在部门名称
58 | tgBotToken: 用于接收消息的tg机器人token,默认值为 `config.properties` 中 `tg_bot_token`
59 |
60 | 服务端会查询 `departmentName` 部门下的 手机号和名称相符的用户, 若查得到,则发消息给他
61 | 否则,发送消息给该部门所有人
62 |
63 | ### 4. telegram bot使用
64 | 1. 在tg中,`@BotFather` - `/newbot` - `输入任意名称说明` - `输入以 _bot 结尾的名称` - `成功,得到token`
65 | 2. 将上述得到的botToken信息填入 `src/main/webapp/config.properties` 中的 `tg_bot_token` 字段
66 | 3. 将你的tg userName填入 `src/main/webapp/config.properties` 中的 `tg_user_name` 字段,用于默认接收消息用户
67 | 4. 任意发送一条消息给上面创建的bot
68 |
69 | ### 4. 效果介绍
70 | #### 1. gitlab相关
71 | 
72 | 
73 | 
74 | 
75 |
76 | #### 2. jira相关
77 | 
78 | 
79 |
80 | #### 3. 蒲公英
81 | 
--------------------------------------------------------------------------------
/src/main/kotlin/org/lynxz/server/service/GiraService.kt:
--------------------------------------------------------------------------------
1 | package org.lynxz.server.service
2 |
3 | import org.lynxz.server.bean.JiraBugEventBean
4 | import org.lynxz.server.config.ConstantsPara
5 | import org.lynxz.server.convertBody
6 | import org.lynxz.server.msec2date
7 | import org.lynxz.server.network.HttpManager
8 | import java.io.IOException
9 | import javax.servlet.http.HttpServletRequest
10 |
11 | /**
12 | * Created by lynxz on 28/08/2017.
13 | * 处理gira的webhook通知
14 | */
15 | class GiraService : PlatformService {
16 |
17 | @Throws(IOException::class)
18 | override fun process(req: HttpServletRequest?) {
19 | req?.let {
20 | convertBody(req.inputStream, JiraBugEventBean::class.java)?.let {
21 | val bean = it
22 | /*
23 | * issue 创建或修改的操作人
24 | * */
25 | val user = bean.user
26 |
27 | val event = bean.webhookEvent
28 | val issue = bean.issue
29 | val fields = issue.fields
30 |
31 | val affectLabels = fields.labels//测试版本号
32 | val type = fields.issuetype.name//issue类型,如 Bug
33 | val projectName = fields.project.key
34 |
35 | val creatorName = fields.creator.displayName
36 | val summary = fields.summary// bug标题
37 | val keyId = issue.key// bug编号,如 UPLUSGO-1241
38 | val url = ConstantsPara.jiraUrl + keyId//issue详情访问网址
39 | var assigneeName = fields.assignee.displayName//bug归属人
40 |
41 | /*
42 | * issue 状态信息
43 | * */
44 | val status = fields.status
45 |
46 | /*
47 | * 存在 changelog 对象时,表明 issue 内容被修改过,比如标题描述
48 | * 测试了下,属性 items 是个列表,但改变多次也只有最新的一条记录
49 | * */
50 | val changelog = bean.changelog
51 |
52 | /*
53 | * issue 的备注信息列表
54 | * 按提交 comment 的先后顺序排列
55 | * 测试过,一定有值
56 | * */
57 | val comment = fields.comment
58 | val comments = comment.comments
59 |
60 | val sb = StringBuilder(100).apply {
61 | append("$event\n")
62 | append("人员: ${user.displayName}\n")
63 | append("归属: $assigneeName\n")
64 | append("类型: $type (状态: ${status.name} )\n")
65 | append("项目: $projectName - $affectLabels\n")
66 | append("概要: $summary\n")
67 |
68 | if (changelog != null) {
69 | val items = changelog.items
70 | if (items.size >= 1) {
71 | append("编辑issue: ${items[0].field}->${items[0].toString}\n")
72 | }
73 | } else if (comments.size > 0) {
74 | append("最新备注: ${comments[comments.size - 1].body}\n")
75 | }
76 |
77 | append("点击查看: $url\n")
78 | append("服务器时间: ${msec2date()}")
79 | }
80 |
81 | // println("要发送的jira信息为: $sb")
82 | assigneeName = if (assigneeName.isNullOrBlank()) ConstantsPara.defaultNoticeUserName else assigneeName
83 | HttpManager.sendTextMessage(assigneeName, null, sb.toString())
84 | }
85 | }
86 | }
87 | }
--------------------------------------------------------------------------------
/src/main/kotlin/org/lynxz/server/bean/TgGetUpdateResponseBean.kt:
--------------------------------------------------------------------------------
1 | package org.lynxz.server.bean
2 |
3 | /**
4 | * tg 获取 bot新消息列表的响应
5 | * [文档](https://core.telegram.org/bots/api#getupdates)
6 | *
7 | ```json
8 | * {
9 | * "ok": true,
10 | * "result": [{
11 | * "update_id": 629414424,
12 | * "channel_post": { // 来自于channel
13 | * "message_id": 22,
14 | * "chat": {
15 | * "id": -1001232599787, // channel 的 chat_id,用于回复
16 | * "title": "11_\u6280\u672f\u5b66\u4e60\ud83e\udd13", // channel名称
17 | * "type": "channel"
18 | * },
19 | * "date": 1570239840,
20 | * "text": "\u6d4b\u8bd5" // 消息内容
21 | * }
22 | * },{
23 | * "update_id": 629414427,
24 | * "message": { // 普通消息,直接通过机器人聊天发送的消息
25 | * "message_id": 8,
26 | * "from": {
27 | * "id": 417044892, // 发送人 id
28 | * "is_bot": false, // 发送人是否是bot
29 | * "first_name": "\u4f60\u8bf4\u5f97\u5bf9tg", // 发送人昵称
30 | * "username": "us3jxjk8d", // user name
31 | * "language_code": "en"
32 | * },
33 | * "chat": {
34 | * "id": 417044892,
35 | * "first_name": "\u4f60\u8bf4\u5f97\u5bf9tg",
36 | * "username": "us3jxjk8d",
37 | * "type": "private"
38 | * },
39 | * "date": 1570239945,
40 | * "text": "\u5f88\u7cbe\u660e\u7a7a\u7075" // 消息内容
41 | * }
42 | * }, {
43 | * "update_id": 629414429,
44 | * "message": {
45 | * "message_id": 9,
46 | * "from": {
47 | * "id": 417044892,
48 | * "is_bot": false,
49 | * "first_name": "\u4f60\u8bf4\u5f97\u5bf9tg",
50 | * "username": "us3jxjk8d",
51 | * "language_code": "en"
52 | * },
53 | * "chat": {
54 | * "id": -394752664,
55 | * "title": "testbotgroup\ud83d\ude44,"
56 | * "type": "group", // 群组消息
57 | * "all_members_are_administrators": true
58 | * },
59 | * "date": 1570240581,
60 | * "group_chat_created": true
61 | * }
62 | * }]
63 | * }
64 | *
65 | * */
66 | data class TgGetUpdateResponseBean(
67 | val ok: Boolean,
68 | val result: List?
69 | )
70 |
71 | data class Result(
72 | val channel_post: ChannelPost?,
73 | val message: Message?,
74 | val update_id: Long
75 | )
76 |
77 | data class ChannelPost(
78 | val chat: ChannelChat,
79 | val date: Long,
80 | val message_id: Long,
81 | val text: String
82 | )
83 |
84 | data class ChannelChat(
85 | val id: Long,
86 | val title: String,
87 | val type: String
88 | )
89 |
90 | data class Message(
91 | val chat: MessageChat,
92 | val date: Long,
93 | val from: From,
94 | val message_id: Long,
95 | val text: String
96 | )
97 |
98 | data class MessageChat(
99 | val first_name: String?, // 昵称
100 | val id: Long,
101 | val type: String,
102 | val username: String? // userName
103 | )
104 |
105 | data class From(
106 | val first_name: String,
107 | val id: Long,
108 | val is_bot: Boolean,
109 | val language_code: String,
110 | val username: String
111 | )
--------------------------------------------------------------------------------
/src/main/kotlin/org/lynxz/server/ApiServlet.kt:
--------------------------------------------------------------------------------
1 | package org.lynxz.server
2 |
3 | import io.reactivex.Observable
4 | import org.lynxz.server.config.ConstantsPara
5 | import org.lynxz.server.config.KeyNames
6 | import org.lynxz.server.network.HttpManager
7 | import java.io.File
8 | import java.io.FileInputStream
9 | import java.io.InputStreamReader
10 | import java.util.*
11 | import java.util.concurrent.TimeUnit
12 | import javax.servlet.ServletConfig
13 | import javax.servlet.annotation.WebServlet
14 | import javax.servlet.http.HttpServlet
15 | import javax.servlet.http.HttpServletRequest
16 | import javax.servlet.http.HttpServletResponse
17 | import kotlin.collections.HashMap
18 |
19 |
20 | /**
21 | * Created by lynxz on 25/08/2017.
22 | * 对所有请求进行处理
23 | * 关于 [@WebServlet的使用](http://blog.csdn.net/maozi_bsz/article/details/46431189)
24 | */
25 | @WebServlet(name = "home", value = ["/*"], loadOnStartup = 1)
26 | class ApiServlet : HttpServlet() {
27 |
28 | override fun init(config: ServletConfig?) {
29 | super.init(config)
30 | getConfigPath("config.properties").let {
31 | // 避免多次初始化
32 | if (ConstantsPara.dd_corp_id.isBlank()) {
33 | println("init configPath is $it \n${File(it).exists()}")
34 | loadConfig(it)
35 | // access_token有效期7200秒
36 | Observable.interval(0, 3600, TimeUnit.SECONDS)
37 | .subscribe {
38 | HttpManager.refreshAccessToken()
39 | HttpManager.getTgBotUpdates()
40 | }
41 | }
42 | }
43 | }
44 |
45 | override fun doGet(req: HttpServletRequest?, resp: HttpServletResponse?) {
46 | doPost(req, resp)
47 | }
48 |
49 | override fun doPost(req: HttpServletRequest?, resp: HttpServletResponse?) {
50 | processRequest(req, resp)
51 | }
52 |
53 | private fun processRequest(req: HttpServletRequest?, resp: HttpServletResponse?) {
54 | req?.characterEncoding = "UTF-8"
55 | getHeadersInfo(req)
56 | Router.route(req)
57 | resp?.apply {
58 | characterEncoding = "UTF-8"
59 | // 返回给客户端的数据
60 | writer.apply {
61 | // todo 2019.10.5 返回实际调用结果
62 | write("{\"serverTime\":\"${msec2date()} hello, I'm running...\"}") // 使用println(...) 等效
63 | flush()
64 | close()
65 | }
66 | }
67 | }
68 |
69 | /**
70 | * 加载指定根目录下指定路径的属性文件
71 | * @param configPath 文件路径,如 "/config.properties"
72 | * @return 属性对象 Properties
73 | */
74 | private fun loadConfig(configPath: String) {
75 | if (File(configPath).exists()) {
76 | Properties().apply {
77 | load(InputStreamReader(FileInputStream(File(configPath)), "UTF-8"))
78 | // load(FileInputStream(File(configPath)))
79 | ConstantsPara.dd_corp_id = getProperty(KeyNames.corpid)
80 | ConstantsPara.dd_corp_secret = getProperty(KeyNames.corpsecret)
81 | ConstantsPara.dd_agent_id = getProperty(KeyNames.agentId)
82 | ConstantsPara.jiraUrl = getProperty(KeyNames.jiraUrl)
83 | ConstantsPara.defaultNoticeUserName = getProperty(KeyNames.defaultNoticeUserName)
84 | ConstantsPara.targetMergeBranch = getProperty(KeyNames.gitlabPushMergeBranch)
85 | ConstantsPara.defaultTgBotToken = getProperty(KeyNames.defaultTgBotToken)
86 | ConstantsPara.defaultTgUserName = getProperty(KeyNames.defaultTgUserName)
87 | println("the corp id is: ${ConstantsPara.dd_corp_id} ,defaultNoticeUserName = ${ConstantsPara.defaultNoticeUserName}")
88 | }
89 | }
90 | }
91 |
92 | //将配置文件放置于 src/main/webapp 目录下
93 | private fun getConfigPath(fileName: String) = "${servletContext.getRealPath("/")}$fileName"
94 |
95 | /**
96 | * 获取header信息
97 | */
98 | private fun getHeadersInfo(request: HttpServletRequest?) = HashMap().apply {
99 | request?.headerNames?.let {
100 | while (it.hasMoreElements()) {
101 | val key = it.nextElement()
102 | put(key, request.getHeader(key))
103 | }
104 | }
105 | println("${msec2date()} header of this request: ${toString()}")
106 | }
107 | }
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Attempt to set APP_HOME
10 | # Resolve links: $0 may be a link
11 | PRG="$0"
12 | # Need this for relative symlinks.
13 | while [ -h "$PRG" ] ; do
14 | ls=`ls -ld "$PRG"`
15 | link=`expr "$ls" : '.*-> \(.*\)$'`
16 | if expr "$link" : '/.*' > /dev/null; then
17 | PRG="$link"
18 | else
19 | PRG=`dirname "$PRG"`"/$link"
20 | fi
21 | done
22 | SAVED="`pwd`"
23 | cd "`dirname \"$PRG\"`/" >/dev/null
24 | APP_HOME="`pwd -P`"
25 | cd "$SAVED" >/dev/null
26 |
27 | APP_NAME="Gradle"
28 | APP_BASE_NAME=`basename "$0"`
29 |
30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
31 | DEFAULT_JVM_OPTS=""
32 |
33 | # Use the maximum available, or set MAX_FD != -1 to use that value.
34 | MAX_FD="maximum"
35 |
36 | warn ( ) {
37 | echo "$*"
38 | }
39 |
40 | die ( ) {
41 | echo
42 | echo "$*"
43 | echo
44 | exit 1
45 | }
46 |
47 | # OS specific support (must be 'true' or 'false').
48 | cygwin=false
49 | msys=false
50 | darwin=false
51 | nonstop=false
52 | case "`uname`" in
53 | CYGWIN* )
54 | cygwin=true
55 | ;;
56 | Darwin* )
57 | darwin=true
58 | ;;
59 | MINGW* )
60 | msys=true
61 | ;;
62 | NONSTOP* )
63 | nonstop=true
64 | ;;
65 | esac
66 |
67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
68 |
69 | # Determine the Java command to use to start the JVM.
70 | if [ -n "$JAVA_HOME" ] ; then
71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
72 | # IBM's JDK on AIX uses strange locations for the executables
73 | JAVACMD="$JAVA_HOME/jre/sh/java"
74 | else
75 | JAVACMD="$JAVA_HOME/bin/java"
76 | fi
77 | if [ ! -x "$JAVACMD" ] ; then
78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
79 |
80 | Please set the JAVA_HOME variable in your environment to match the
81 | location of your Java installation."
82 | fi
83 | else
84 | JAVACMD="java"
85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
86 |
87 | Please set the JAVA_HOME variable in your environment to match the
88 | location of your Java installation."
89 | fi
90 |
91 | # Increase the maximum file descriptors if we can.
92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
93 | MAX_FD_LIMIT=`ulimit -H -n`
94 | if [ $? -eq 0 ] ; then
95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
96 | MAX_FD="$MAX_FD_LIMIT"
97 | fi
98 | ulimit -n $MAX_FD
99 | if [ $? -ne 0 ] ; then
100 | warn "Could not set maximum file descriptor limit: $MAX_FD"
101 | fi
102 | else
103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
104 | fi
105 | fi
106 |
107 | # For Darwin, add options to specify how the application appears in the dock
108 | if $darwin; then
109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
110 | fi
111 |
112 | # For Cygwin, switch paths to Windows format before running java
113 | if $cygwin ; then
114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
116 | JAVACMD=`cygpath --unix "$JAVACMD"`
117 |
118 | # We build the pattern for arguments to be converted via cygpath
119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
120 | SEP=""
121 | for dir in $ROOTDIRSRAW ; do
122 | ROOTDIRS="$ROOTDIRS$SEP$dir"
123 | SEP="|"
124 | done
125 | OURCYGPATTERN="(^($ROOTDIRS))"
126 | # Add a user-defined pattern to the cygpath arguments
127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
129 | fi
130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
131 | i=0
132 | for arg in "$@" ; do
133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
135 |
136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
138 | else
139 | eval `echo args$i`="\"$arg\""
140 | fi
141 | i=$((i+1))
142 | done
143 | case $i in
144 | (0) set -- ;;
145 | (1) set -- "$args0" ;;
146 | (2) set -- "$args0" "$args1" ;;
147 | (3) set -- "$args0" "$args1" "$args2" ;;
148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
154 | esac
155 | fi
156 |
157 | # Escape application args
158 | save ( ) {
159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
160 | echo " "
161 | }
162 | APP_ARGS=$(save "$@")
163 |
164 | # Collect all arguments for the java command, following the shell quoting and substitution rules
165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
166 |
167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
169 | cd "$(dirname "$0")"
170 | fi
171 |
172 | exec "$JAVACMD" "$@"
173 |
--------------------------------------------------------------------------------
/src/main/kotlin/org/lynxz/server/service/GitlabService.kt:
--------------------------------------------------------------------------------
1 | package org.lynxz.server.service
2 |
3 | import org.lynxz.server.bean.GbMergeRequestEventBean
4 | import org.lynxz.server.bean.GbPushRequestEventBean
5 | import org.lynxz.server.config.Actions
6 | import org.lynxz.server.config.ConstantsPara
7 | import org.lynxz.server.config.KeyNames
8 | import org.lynxz.server.convertBody
9 | import org.lynxz.server.msec2date
10 | import org.lynxz.server.network.HttpManager
11 | import java.util.regex.Pattern
12 | import javax.servlet.http.HttpServletRequest
13 |
14 | /**
15 | * Created by lynxz on 28/08/2017.
16 | * 处理gitlab的合并请求
17 | * 假设目标分支是master,可能出现两种情况的hook:
18 | * 1. 在master分支上的push hook;
19 | * 2. 正常的merge hook;
20 | */
21 | class GitlabService : PlatformService {
22 | /*
23 | * 上次merge后的commit_sha值
24 | * 由于merge hook有时候不发出,因此之前多加了一个push hook处理
25 | * 但是有时merge hook 又发出,则会出现两次通知(master分支的push hook测试后每次都有收到)
26 | * 因此在此处添加一个commitId作为判断依据,避免多次重复同样的消息
27 | */
28 | companion object {
29 | var lastMergeCommitSha: String? = ""
30 | }
31 |
32 | override fun process(req: HttpServletRequest?) {
33 | val gitlabHookType = req?.getHeader(KeyNames.HEADER_GITLAB)
34 | println("gitlabService gitlabHookType: ${gitlabHookType?.toLowerCase()}")
35 | when (gitlabHookType?.toLowerCase()) {
36 | KeyNames.HEADER_GITLAB_PUSH_HOOK -> processPushHook(req)
37 | KeyNames.HEADER_GITLAB_MERGE_HOOK -> processMergeHook(req)
38 | else -> println("GitlabService cannot distinguish $gitlabHookType")
39 | }
40 | }
41 |
42 | /**
43 | * 处理gitlab的merge请求通知
44 | */
45 | private fun processMergeHook(req: HttpServletRequest) {
46 | convertBody(req.inputStream, GbMergeRequestEventBean::class.java)?.let {
47 | val objectAttributes = it.object_attributes
48 | val action = objectAttributes.action
49 | println("processMergeHook action = " + action + " , url = " + objectAttributes.url)
50 | val user = it.user
51 | val project = it.project
52 | StringBuilder(100).apply {
53 | when (action) {
54 |
55 | Actions.OPEN, Actions.REOPEN, Actions.UPDATE -> {// 有新的merge请求时,通知审核人员审核
56 | append("Gitlab: 有新merge请求: $action\n")
57 | append("项目: ${project.name}\n")
58 | append("用户: ${user.name}\n")
59 | append("目标分支: ${objectAttributes.target_branch}\n")
60 | append("请求概要: ${objectAttributes.title}\n")
61 | append("服务器时间: ${msec2date()}\n")
62 | append("审核地址: ${objectAttributes.url}")
63 | HttpManager.sendTextMessage(it.assignee.name, null, toString())
64 | }
65 |
66 | Actions.CLOSE -> {// merge请求被关闭的时候,通知提交请求的人
67 | append("Gitlab: 您的merge请求被关闭\n")
68 | .append("项目: ${project.name}\n")
69 | .append("目标分支: ${objectAttributes.target_branch}\n")
70 | .append("概要: ${objectAttributes.title}\n")
71 | .append("服务器时间: ${msec2date()}")
72 | HttpManager.sendTextMessage(user.name, null, toString())
73 | }
74 |
75 | Actions.MERGE -> {// merge请求被通过,通知相关所有人更新代码
76 | val mergeCommitSha = objectAttributes.merge_commit_sha
77 | println("${msec2date()} processMergeHook $lastMergeCommitSha $mergeCommitSha")
78 | if (lastMergeCommitSha.isNullOrBlank() or
79 | !lastMergeCommitSha.equals(mergeCommitSha, ignoreCase = true)) {
80 | lastMergeCommitSha = mergeCommitSha
81 |
82 | append("Gitlab: 有merge请求被通过,请更新代码").append("\n")
83 | append("项目: ${project.name}\n")
84 | append("分支: ${objectAttributes.target_branch}\n")
85 | append("概要: ${objectAttributes.title}\n")
86 | append("服务器时间: ${msec2date()}")
87 | HttpManager.sendTestMessageToDepartment(toString(), project.name, project.namespace)
88 | }
89 | }
90 |
91 | else -> {
92 | append("Gitlab: 有新merge请求: ").append(action).append("\n")
93 | append("项目: ${project.name}\n")
94 | append("用户: ${user.username}\n")
95 | append("目标分支: ${objectAttributes.target_branch}\n")
96 | append("请求概要: ${objectAttributes.title}\n")
97 | append("服务器时间: ${msec2date()}\n")
98 | append("审核地址: ${objectAttributes.url}")
99 | HttpManager.sendTextMessage(it.assignee.name, null, toString())
100 | }
101 | }
102 | }
103 | }
104 | }
105 |
106 | /**
107 | * 判断gitlab push hook 是否是 merge hook,是的话就通知对应人员
108 | */
109 | private fun processPushHook(req: HttpServletRequest) {
110 | convertBody(req.inputStream, GbPushRequestEventBean::class.java)?.let {
111 | /*
112 | * 包含 "master" 字样的话就说明是merge hook
113 | * 具体哪个分支根据 'config.properties' 文件中的 'gitlab_push_merge_branch' 属性来确定
114 | * ref : refs/heads/MASTER
115 | * */
116 | if (!it.ref.toLowerCase().contains(ConstantsPara.targetMergeBranch)) {
117 | return
118 | }
119 |
120 | val after = if (it.after.isNullOrBlank()) it.checkout_sha else it.after
121 |
122 | // 若之前已经处理过该id的消息,则不作处理
123 | println("${msec2date()} processPushHook $lastMergeCommitSha $after")
124 | if (lastMergeCommitSha?.equals(after, ignoreCase = true) ?: false) {
125 | return
126 | }
127 | lastMergeCommitSha = after
128 | // 获取仓库信息,用于判断是通知哪个部门的人,如Android/iOS
129 | val repository = it.repository
130 | val repositoryName = repository.name
131 |
132 | // 获取提交信息
133 | val commits = it.commits
134 | if (commits?.size ?: 0 > 0) {
135 | val commitsBean = commits[commits.size - 1]
136 | var mergeInfo = commitsBean.message.replace("\\r", "").replace("\\n", "")
137 |
138 | // 去除 master 分支 push 信息中的无用字符串
139 | val p = Pattern.compile("Merge branch\\D.+into\\s." + ConstantsPara.targetMergeBranch + ".")
140 | val matcher = p.matcher(mergeInfo)
141 | val s1 = matcher.replaceFirst("")
142 |
143 | val p1 = Pattern.compile("See\\smerge.+$")
144 | val matcher1 = p1.matcher(s1)
145 | mergeInfo = matcher1.replaceAll("")
146 |
147 | StringBuilder(100).apply {
148 | append("Gitlab: ${ConstantsPara.targetMergeBranch}分支有代码更新,请刷新本地代码\n")
149 | append("项目: $repositoryName\n")
150 | append("分支: ${ConstantsPara.targetMergeBranch}\n")
151 | append("概要: $mergeInfo\n")
152 | append("服务器时间: ${msec2date()}")
153 | HttpManager.sendTestMessageToDepartment(toString(), it.project.name, it.project.namespace)
154 | }
155 | }
156 | }
157 | }
158 | }
--------------------------------------------------------------------------------
/src/main/kotlin/org/lynxz/server/network/HttpManager.kt:
--------------------------------------------------------------------------------
1 | package org.lynxz.server.network
2 |
3 | import io.reactivex.Observable
4 | import io.reactivex.Observer
5 | import io.reactivex.disposables.CompositeDisposable
6 | import io.reactivex.disposables.Disposable
7 | import io.reactivex.functions.BiFunction
8 | import io.reactivex.rxkotlin.toObservable
9 | import io.reactivex.schedulers.Schedulers
10 | import okhttp3.Interceptor
11 | import okhttp3.OkHttpClient
12 | import okhttp3.logging.HttpLoggingInterceptor
13 | import org.lynxz.server.bean.*
14 | import org.lynxz.server.config.ConstantsPara
15 | import org.lynxz.server.config.KeyNames
16 | import org.lynxz.server.config.MessageType
17 | import org.lynxz.server.msec2date
18 | import retrofit2.Retrofit
19 | import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory
20 | import retrofit2.converter.gson.GsonConverterFactory
21 |
22 |
23 | /**
24 | * Created by lynxz on 25/08/2017.
25 | * 网络访问具体处理类
26 | */
27 | object HttpManager {
28 |
29 | // 给请求添加统一的query参数:access_token
30 | private val queryInterceptor = Interceptor { chain ->
31 | val original = chain.request()
32 | val url = original.url().newBuilder()
33 | .addQueryParameter(KeyNames.QUERY_KEY_ACCESS_TOKEN, ConstantsPara.accessToken)
34 | .build()
35 |
36 | val requestBuilder = original.newBuilder().url(url)
37 | chain.proceed(requestBuilder.build())
38 | }
39 |
40 | // 给请求添加统一的header参数:Content-Type
41 | private val headerInterceptor = Interceptor { chain ->
42 | val request = chain.request().newBuilder()
43 | .addHeader(KeyNames.HEADER_KEY_CONTENT_TYPE, "application/json")
44 | .build()
45 | chain.proceed(request)
46 | }
47 |
48 | // 显示请求日志,可选
49 | private val logInterceptor = HttpLoggingInterceptor().apply { level = HttpLoggingInterceptor.Level.BODY }
50 |
51 | private val okHttpClient: OkHttpClient = OkHttpClient()
52 | .newBuilder()
53 | .addInterceptor(headerInterceptor)
54 | .addInterceptor(queryInterceptor)
55 | .addInterceptor(logInterceptor)
56 | .build()
57 |
58 | private val ddRetrofit: Retrofit = Retrofit.Builder()
59 | .client(okHttpClient)
60 | .baseUrl(ConstantsPara.DINGDING_SERVER_URL)
61 | .addConverterFactory(GsonConverterFactory.create())
62 | .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
63 | .build()
64 |
65 | private val apiService: ApiService = ddRetrofit.create(ApiService::class.java)
66 |
67 | private val compositeDisposable = CompositeDisposable()
68 |
69 | /**
70 | * 刷新钉钉accessToken
71 | * */
72 | fun refreshAccessToken() {
73 | apiService.getAccessToken(ConstantsPara.dd_corp_id, ConstantsPara.dd_corp_secret)
74 | .retry(1)
75 | .subscribe(object : Observer {
76 | override fun onError(e: Throwable) {
77 | e.printStackTrace()
78 | }
79 |
80 | override fun onSubscribe(d: Disposable) {
81 | addDisposable(d)
82 | }
83 |
84 | override fun onComplete() {
85 | }
86 |
87 | override fun onNext(t: AccessTokenBean) {
88 | println("refreshAccessToken $t")
89 | ConstantsPara.accessToken = t.access_token ?: ""
90 | // todo 若有上一次未发送成功的数据,则尝试发送
91 | getDepartmentInfo()
92 | }
93 | })
94 | }
95 |
96 | /**
97 | * 获取部门列表信息以及各部门成员信息
98 | */
99 | fun getDepartmentInfo() {
100 | apiService.getDepartmentList()
101 | .flatMap { list ->
102 | ConstantsPara.departmentList = list
103 | list.department.forEach { ConstantsPara.departmentNameMap[it.id] = it.name }
104 | Observable.fromIterable(list.department)
105 | }
106 | .map { departmentBean -> departmentBean.id }
107 | .flatMap { departmentId ->
108 | Observable.zip(Observable.create { it.onNext(departmentId) },
109 | apiService.getDepartmentMemberDetailList(departmentId),
110 | BiFunction { t1, t2 ->
111 | t2.departmentId = t1
112 | t2
113 | })
114 | }
115 | .retry(1)
116 | .subscribe(object : Observer {
117 | override fun onNext(t: DepartmentMemberDetailListBean) {
118 | ConstantsPara.departmentMemberDetailMap[t.departmentId] = t.userlist
119 | }
120 |
121 | override fun onSubscribe(d: Disposable) {
122 | addDisposable(d)
123 | }
124 |
125 | override fun onError(e: Throwable) {
126 | e.printStackTrace()
127 | }
128 |
129 | override fun onComplete() {
130 | println("getDepartmentInfo onComplete:\n${ConstantsPara.departmentMemberDetailMap.keys.forEach { println("departId: $it") }}")
131 | // sendTextMessage(ConstantsPara.defaultNoticeUserName, null, "test from server")
132 | }
133 | })
134 | }
135 |
136 | /**
137 | * 根据gitlab返回的项目组别和名称,发送消息给对应的部门群成员
138 | * 钉钉部门名称与项目名称一致,切部门的上级部门与项目所在组的namespace一致,则可确定要通知的部门
139 | *
140 | * 如钉钉通讯录中有某群: father/child , 部门名为 child, 上级部门为 father
141 | * 而gitlab中有某项目地址为: https://gitlab.lynxz.org/father/child
142 | * 则可完全确定所需通知的部门child
143 | * 备注: 本项目只支持两级
144 | * */
145 | fun sendTestMessageToDepartment(msg: String? = "", projectName: String? = "", projectNameSpace: String = "") {
146 | if (msg.isNullOrBlank() or projectName.isNullOrBlank()) {
147 | return
148 | }
149 |
150 | // 由于钉钉部门名称不支持 "-" ,因此自动替换为 "_",创建通讯录时请注意
151 | ConstantsPara.departmentList?.department?.toObservable()
152 | ?.filter { (projectName!!.replace("-", "_").equals(it.name, true)) and (projectNameSpace.replace("-", "_").equals(ConstantsPara.departmentNameMap[it.parentid], true)) }
153 | ?.flatMap { bean -> Observable.just(ConstantsPara.departmentMemberDetailMap[bean.id]) }
154 | ?.flatMap { Observable.fromIterable(it) }
155 | ?.doOnNext { sendTextMessage(it.name, null, msg!!) }
156 | ?.doOnSubscribe { println("要群发给 $projectNameSpace/$projectName 的消息是:\n${toString()}") }
157 | ?.subscribe()
158 | }
159 |
160 | /**
161 | * 向指定用户发送文本内容[message]
162 | * 若目标用户为空,则发送给指定部门[departmentId]所有人,比如gitlab merge请求通过时,通知所有人
163 | * 优先通过手机号匹配用户, [userName] 只要匹配用户名或者用户备注信息即算符合要求
164 | *
165 | * @param userMobile 用户手机号
166 | * @param userName 用户姓名
167 | *
168 | * */
169 | fun sendTextMessage(userName: String? = null, userMobile: String? = null,
170 | message: String = "", departmentId: Int = 1) {
171 | ConstantsPara.departmentMemberDetailMap[departmentId]?.apply {
172 | stream().filter { userMobile.isNullOrBlank() or userMobile.equals(it.mobile, true) }
173 | .filter { userName.isNullOrBlank() or userName.equals(it.name, true) or userName.equals(it.remark, true) }
174 | .forEach {
175 | val textBean = MessageTextBean().apply {
176 | touser = it.userid
177 | agentid = ConstantsPara.dd_agent_id
178 | msgtype = MessageType.TEXT
179 | text = MessageTextBean.TextBean().apply {
180 | // 自动添加时间信息,避免被钉钉服务器拦截
181 | content = if (message.contains("服务器时间")) message else "$message\n服务器时间: ${msec2date()}"
182 | }
183 | }
184 | apiService.sendTextMessage(textBean)
185 | .subscribeOn(Schedulers.io())
186 | .subscribe(object : Observer {
187 | override fun onComplete() {
188 | }
189 |
190 | override fun onSubscribe(d: Disposable) {
191 | addDisposable(d)
192 | }
193 |
194 | override fun onNext(t: MessageResponseBean) {
195 | println("${msec2date()} sendTextMessage $t")
196 | }
197 |
198 | override fun onError(e: Throwable) {
199 | e.printStackTrace()
200 | }
201 | })
202 | }
203 | }
204 | }
205 |
206 |
207 | /**
208 | * 获取tg bot的所有聊天信息,主要用于获取 chat_id
209 | * */
210 | fun getTgBotUpdates(botToken: String = ConstantsPara.defaultTgBotToken, doOnComplete: () -> Unit = {}) {
211 | apiService.getTgUpdates("https://api.telegram.org/bot$botToken/getUpdates")
212 | .retry(1)
213 | .subscribe(object : Observer {
214 | override fun onError(e: Throwable) {
215 | e.printStackTrace()
216 | }
217 |
218 | override fun onSubscribe(d: Disposable) {
219 | addDisposable(d)
220 | }
221 |
222 | override fun onComplete() {
223 | doOnComplete.invoke()
224 | }
225 |
226 | override fun onNext(t: TgGetUpdateResponseBean) {
227 | if (t.ok) {
228 | val chatMap = mutableMapOf()
229 |
230 | t.result?.filter {
231 | // 只取私聊,非channel/group机器人
232 | it.message?.chat?.type == "private"
233 | }?.forEach {
234 | it.message?.chat?.let { chat ->
235 | // userName可能为空,缓存userName和firstName,减少客户端对接成本
236 | val firstName = chat.first_name
237 | val userName = chat.username
238 |
239 | if (!firstName.isNullOrBlank()) {
240 | chatMap["${botToken}_$firstName"] = chat.id
241 | }
242 |
243 | if (!userName.isNullOrBlank()) {
244 | chatMap["${botToken}_$userName"] = chat.id
245 | }
246 | }
247 | }
248 | ConstantsPara.tgChatInfoMap = chatMap
249 | }
250 | }
251 | })
252 | }
253 |
254 | /**
255 | * 通过bot,发送tg消息给指定人员
256 | * */
257 | fun sendTgMessage(body: TgSendMessageReqBean, botToken: String = ConstantsPara.defaultTgBotToken) {
258 | if (!body.isValid()) {
259 | print("发送tg消息失败,内容异常,请检查$body")
260 | return
261 | }
262 | apiService.sendTgBotMessage("https://api.telegram.org/bot$botToken/sendMessage", body)
263 | .retry(1)
264 | .subscribe(object : Observer {
265 | override fun onError(e: Throwable) {
266 | e.printStackTrace()
267 | }
268 |
269 | override fun onSubscribe(d: Disposable) {
270 | addDisposable(d)
271 | }
272 |
273 | override fun onComplete() {
274 | }
275 |
276 | override fun onNext(t: TgSendMessageRespBean) {
277 | println("发送消息给tg bot $botToken, 结果: ${t.ok}\n内容:${t.result?.text}")
278 | }
279 | })
280 | }
281 |
282 |
283 | fun addDisposable(d: Disposable) {
284 | compositeDisposable.add(d)
285 | }
286 | }
--------------------------------------------------------------------------------
/src/main/kotlin/org/lynxz/server/bean/GbPushRequestEventBean.java:
--------------------------------------------------------------------------------
1 | package org.lynxz.server.bean;
2 |
3 | import java.util.List;
4 |
5 | /**
6 | * Created by lynxz on 10/04/2017.
7 | * 偶尔审核人merge gitlab请求通过时,gitlab发送的hook是push hook,格式如下
8 | */
9 | public class GbPushRequestEventBean {
10 |
11 | /**
12 | * object_kind : push
13 | * before : 43b864d91ca0af1fdeeff609a64499b7e1afa72
14 | * after : 3340795ced98e7a6c86f26612c63a55efa868a5
15 | * ref : refs/heads/MASTER
16 | * checkout_sha : 334079ced978e7a6c86f26612c63a55efa868a5
17 | * message : null
18 | * user_id : 31
19 | * user_name : 张三
20 | * user_email : lz@st.c.c
21 | * user_avatar : https://gitlab.sous.tech/upads/user/avar/31/ly_aatar.png
22 | * project_id : 216
23 | * project : {"name":"sonicmng-android","description":" andorid版本","web_url":"https://gitlab.soundbus.th/sonving-app-team/sonicmoving-android","avatar_url":null,"git_ssh_url":"git@gitlab.soundbus.tech:sonicmoving-app-team/sonicmoving-android.git","git_http_url":"https://gitlab.soundbus.tech/sonicmoving-app-team/sonicmoving-android.git","namespace":"sonicmoving-app-team","visibility_level":0,"path_with_namespace":"sonicmoving-app-team/sonicmoving-android","default_branch":"MASTER","homepage":"https://gitlab.soundbus.tech/sonicmoving-app-team/sonicmoving-android","url":"git@gitlab.soundbus.tech:sonicmoving-app-team/sonicmoving-android.git","ssh_url":"git@gitlab.soundbus.tech:sonicmoving-app-team/sonicmoving-android.git","http_url":"https://gitlab.soundbus.tech/sonicmoving-app-team/sonicmoving-android.git"}
24 | * commits : [{"id":"50f4f5d56e32c74959a4c03a23e18fedc20cea8f","message":"第三方绑定信息UI细节调整\n","timestamp":"2017-04-10T16:30:26+08:00","url":"https://gitlab.soundbus.th/sonicmoving-app-team/sonicmoving-android/commit/50f4f5d56e32c74959a4c03a28fedc20cea8f","author":{"name":"zhuyic","email":"9663202@qq.com"},"added":[],"modified":["app/src/main/res/layout/activity_account_security.xml","app/src/main/res/values/strings.xml"],"removed":[]},{"id":"c6531663349ba042bd8ce1adda232ae3ed8bf9d0","message":"Merge branch 'MASTER' of https://gitlab.soundbus.tech/sonicmoving-app-team/sonicmoving-android into zyc_sonicmoving\n","timestamp":"2017-04-10T16:30:44+08:00","url":"https://gitlab.soundbus.tech/sonicmoving-app-team/sonicmoving-android/commit/c6531663349ba042bd8ce1adda232ae3ed8bf9d0","author":{"name":"zhuyichao","email":"97663202@qq.com"},"added":[],"modified":[],"removed":[]},{"id":"3340795ced978e7a6c86f26612c63a55efa868a5","message":"Merge branch 'zyc_sonicmoving' into 'MASTER'\r\n\r\n第三方绑定信息UI细节调整\r\n\r\n\r\n\r\nSee merge request !164","timestamp":"2017-04-10T16:33:04+08:00","url":"https://gitlab.soundbus.tech/sonicmoving-app-team/sonicmoving-android/commit/3340795ced978e7a6c86f26612c63a55efa868a5","author":{"name":"曾显状","email":"lynxz@soundnet.com.cn"},"added":[],"modified":["app/src/main/res/layout/activity_account_security.xml","app/src/main/res/values/strings.xml"],"removed":[]}]
25 | * total_commits_count : 3
26 | * repository : {"name":"sonicmoving-android","url":"git@gitlab.soundbus.tech:sonicmoving-app-team/sonicmoving-android.git","description":"声动app andorid版本","homepage":"https://gitlab.soundbus.tech/sonicmoving-app-team/sonicmoving-android","git_http_url":"https://gitlab.soundbus.tech/sonicmoving-app-team/sonicmoving-android.git","git_ssh_url":"git@gitlab.soundbus.tech:sonicmoving-app-team/sonicmoving-android.git","visibility_level":0}
27 | */
28 |
29 | private String object_kind;
30 | private String before;
31 | private String after;
32 | private String ref;
33 | private String checkout_sha;
34 | private Object message;
35 | private long user_id;
36 | private String user_name;
37 | private String user_email;
38 | private String user_avatar;
39 | private long project_id;
40 | private ProjectBean project;
41 | private int total_commits_count;
42 | private RepositoryBean repository;
43 | private List commits;
44 |
45 | public String getObject_kind() {
46 | return object_kind;
47 | }
48 |
49 | public void setObject_kind(String object_kind) {
50 | this.object_kind = object_kind;
51 | }
52 |
53 | public String getBefore() {
54 | return before;
55 | }
56 |
57 | public void setBefore(String before) {
58 | this.before = before;
59 | }
60 |
61 | public String getAfter() {
62 | return after;
63 | }
64 |
65 | public void setAfter(String after) {
66 | this.after = after;
67 | }
68 |
69 | public String getRef() {
70 | return ref;
71 | }
72 |
73 | public void setRef(String ref) {
74 | this.ref = ref;
75 | }
76 |
77 | public String getCheckout_sha() {
78 | return checkout_sha;
79 | }
80 |
81 | public void setCheckout_sha(String checkout_sha) {
82 | this.checkout_sha = checkout_sha;
83 | }
84 |
85 | public Object getMessage() {
86 | return message;
87 | }
88 |
89 | public void setMessage(Object message) {
90 | this.message = message;
91 | }
92 |
93 | public long getUser_id() {
94 | return user_id;
95 | }
96 |
97 | public void setUser_id(long user_id) {
98 | this.user_id = user_id;
99 | }
100 |
101 | public String getUser_name() {
102 | return user_name;
103 | }
104 |
105 | public void setUser_name(String user_name) {
106 | this.user_name = user_name;
107 | }
108 |
109 | public String getUser_email() {
110 | return user_email;
111 | }
112 |
113 | public void setUser_email(String user_email) {
114 | this.user_email = user_email;
115 | }
116 |
117 | public String getUser_avatar() {
118 | return user_avatar;
119 | }
120 |
121 | public void setUser_avatar(String user_avatar) {
122 | this.user_avatar = user_avatar;
123 | }
124 |
125 | public long getProject_id() {
126 | return project_id;
127 | }
128 |
129 | public void setProject_id(long project_id) {
130 | this.project_id = project_id;
131 | }
132 |
133 | public ProjectBean getProject() {
134 | return project;
135 | }
136 |
137 | public void setProject(ProjectBean project) {
138 | this.project = project;
139 | }
140 |
141 | public int getTotal_commits_count() {
142 | return total_commits_count;
143 | }
144 |
145 | public void setTotal_commits_count(int total_commits_count) {
146 | this.total_commits_count = total_commits_count;
147 | }
148 |
149 | public RepositoryBean getRepository() {
150 | return repository;
151 | }
152 |
153 | public void setRepository(RepositoryBean repository) {
154 | this.repository = repository;
155 | }
156 |
157 | public List getCommits() {
158 | return commits;
159 | }
160 |
161 | public void setCommits(List commits) {
162 | this.commits = commits;
163 | }
164 |
165 | public static class ProjectBean {
166 | /**
167 | * name : sonicmoving-android
168 | * description : 声动app andorid版本
169 | * web_url : https://gitlab.soundbus.tech/sonicmoving-app-team/sonicmoving-android
170 | * avatar_url : null
171 | * git_ssh_url : git@gitlab.soundbus.tech:sonicmoving-app-team/sonicmoving-android.git
172 | * git_http_url : https://gitlab.soundbus.tech/sonicmoving-app-team/sonicmoving-android.git
173 | * namespace : sonicmoving-app-team
174 | * visibility_level : 0
175 | * path_with_namespace : sonicmoving-app-team/sonicmoving-android
176 | * default_branch : MASTER
177 | * homepage : https://gitlab.soundbus.tech/sonicmoving-app-team/sonicmoving-android
178 | * url : git@gitlab.soundbus.tech:sonicmoving-app-team/sonicmoving-android.git
179 | * ssh_url : git@gitlab.soundbus.tech:sonicmoving-app-team/sonicmoving-android.git
180 | * http_url : https://gitlab.soundbus.tech/sonicmoving-app-team/sonicmoving-android.git
181 | */
182 |
183 | private String name;
184 | private String description;
185 | private String web_url;
186 | private Object avatar_url;
187 | private String git_ssh_url;
188 | private String git_http_url;
189 | private String namespace;
190 | private int visibility_level;
191 | private String path_with_namespace;
192 | private String default_branch;
193 | private String homepage;
194 | private String url;
195 | private String ssh_url;
196 | private String http_url;
197 |
198 | public String getName() {
199 | return name;
200 | }
201 |
202 | public void setName(String name) {
203 | this.name = name;
204 | }
205 |
206 | public String getDescription() {
207 | return description;
208 | }
209 |
210 | public void setDescription(String description) {
211 | this.description = description;
212 | }
213 |
214 | public String getWeb_url() {
215 | return web_url;
216 | }
217 |
218 | public void setWeb_url(String web_url) {
219 | this.web_url = web_url;
220 | }
221 |
222 | public Object getAvatar_url() {
223 | return avatar_url;
224 | }
225 |
226 | public void setAvatar_url(Object avatar_url) {
227 | this.avatar_url = avatar_url;
228 | }
229 |
230 | public String getGit_ssh_url() {
231 | return git_ssh_url;
232 | }
233 |
234 | public void setGit_ssh_url(String git_ssh_url) {
235 | this.git_ssh_url = git_ssh_url;
236 | }
237 |
238 | public String getGit_http_url() {
239 | return git_http_url;
240 | }
241 |
242 | public void setGit_http_url(String git_http_url) {
243 | this.git_http_url = git_http_url;
244 | }
245 |
246 | public String getNamespace() {
247 | return namespace;
248 | }
249 |
250 | public void setNamespace(String namespace) {
251 | this.namespace = namespace;
252 | }
253 |
254 | public int getVisibility_level() {
255 | return visibility_level;
256 | }
257 |
258 | public void setVisibility_level(int visibility_level) {
259 | this.visibility_level = visibility_level;
260 | }
261 |
262 | public String getPath_with_namespace() {
263 | return path_with_namespace;
264 | }
265 |
266 | public void setPath_with_namespace(String path_with_namespace) {
267 | this.path_with_namespace = path_with_namespace;
268 | }
269 |
270 | public String getDefault_branch() {
271 | return default_branch;
272 | }
273 |
274 | public void setDefault_branch(String default_branch) {
275 | this.default_branch = default_branch;
276 | }
277 |
278 | public String getHomepage() {
279 | return homepage;
280 | }
281 |
282 | public void setHomepage(String homepage) {
283 | this.homepage = homepage;
284 | }
285 |
286 | public String getUrl() {
287 | return url;
288 | }
289 |
290 | public void setUrl(String url) {
291 | this.url = url;
292 | }
293 |
294 | public String getSsh_url() {
295 | return ssh_url;
296 | }
297 |
298 | public void setSsh_url(String ssh_url) {
299 | this.ssh_url = ssh_url;
300 | }
301 |
302 | public String getHttp_url() {
303 | return http_url;
304 | }
305 |
306 | public void setHttp_url(String http_url) {
307 | this.http_url = http_url;
308 | }
309 | }
310 |
311 | public static class RepositoryBean {
312 | /**
313 | * name : sonicmoving-android
314 | * url : git@gitlab.soundbus.tech:sonicmoving-app-team/sonicmoving-android.git
315 | * description : 声动app andorid版本
316 | * homepage : https://gitlab.soundbus.tech/sonicmoving-app-team/sonicmoving-android
317 | * git_http_url : https://gitlab.soundbus.tech/sonicmoving-app-team/sonicmoving-android.git
318 | * git_ssh_url : git@gitlab.soundbus.tech:sonicmoving-app-team/sonicmoving-android.git
319 | * visibility_level : 0
320 | */
321 |
322 | private String name;
323 | private String url;
324 | private String description;
325 | private String homepage;
326 | private String git_http_url;
327 | private String git_ssh_url;
328 | private int visibility_level;
329 |
330 | public String getName() {
331 | return name;
332 | }
333 |
334 | public void setName(String name) {
335 | this.name = name;
336 | }
337 |
338 | public String getUrl() {
339 | return url;
340 | }
341 |
342 | public void setUrl(String url) {
343 | this.url = url;
344 | }
345 |
346 | public String getDescription() {
347 | return description;
348 | }
349 |
350 | public void setDescription(String description) {
351 | this.description = description;
352 | }
353 |
354 | public String getHomepage() {
355 | return homepage;
356 | }
357 |
358 | public void setHomepage(String homepage) {
359 | this.homepage = homepage;
360 | }
361 |
362 | public String getGit_http_url() {
363 | return git_http_url;
364 | }
365 |
366 | public void setGit_http_url(String git_http_url) {
367 | this.git_http_url = git_http_url;
368 | }
369 |
370 | public String getGit_ssh_url() {
371 | return git_ssh_url;
372 | }
373 |
374 | public void setGit_ssh_url(String git_ssh_url) {
375 | this.git_ssh_url = git_ssh_url;
376 | }
377 |
378 | public int getVisibility_level() {
379 | return visibility_level;
380 | }
381 |
382 | public void setVisibility_level(int visibility_level) {
383 | this.visibility_level = visibility_level;
384 | }
385 | }
386 |
387 | public static class CommitsBean {
388 | /**
389 | * id : 50f4f5d56e32c74959a4c03a23e18fedc20cea8f
390 | * message : 第三方绑定信息UI细节调整
391 |
392 | * timestamp : 2017-04-10T16:30:26+08:00
393 | * url : https://gitlab.soundbus.tech/sonicmoving-app-team/sonicmoving-android/commit/50f4f5d56e32c74959a4c03a23e18fedc20cea8f
394 | * author : {"name":"zhuyichao","email":"97663202@qq.com"}
395 | * added : []
396 | * modified : ["app/src/main/res/layout/activity_account_security.xml","app/src/main/res/values/strings.xml"]
397 | * removed : []
398 | */
399 |
400 | private String id;
401 | private String message;
402 | private String timestamp;
403 | private String url;
404 | private AuthorBean author;
405 | private List> added;
406 | private List modified;
407 | private List> removed;
408 |
409 | public String getId() {
410 | return id;
411 | }
412 |
413 | public void setId(String id) {
414 | this.id = id;
415 | }
416 |
417 | public String getMessage() {
418 | return message;
419 | }
420 |
421 | public void setMessage(String message) {
422 | this.message = message;
423 | }
424 |
425 | public String getTimestamp() {
426 | return timestamp;
427 | }
428 |
429 | public void setTimestamp(String timestamp) {
430 | this.timestamp = timestamp;
431 | }
432 |
433 | public String getUrl() {
434 | return url;
435 | }
436 |
437 | public void setUrl(String url) {
438 | this.url = url;
439 | }
440 |
441 | public AuthorBean getAuthor() {
442 | return author;
443 | }
444 |
445 | public void setAuthor(AuthorBean author) {
446 | this.author = author;
447 | }
448 |
449 | public List> getAdded() {
450 | return added;
451 | }
452 |
453 | public void setAdded(List> added) {
454 | this.added = added;
455 | }
456 |
457 | public List getModified() {
458 | return modified;
459 | }
460 |
461 | public void setModified(List modified) {
462 | this.modified = modified;
463 | }
464 |
465 | public List> getRemoved() {
466 | return removed;
467 | }
468 |
469 | public void setRemoved(List> removed) {
470 | this.removed = removed;
471 | }
472 |
473 | public static class AuthorBean {
474 | /**
475 | * name : zhuyichao
476 | * email : 97663202@qq.com
477 | */
478 |
479 | private String name;
480 | private String email;
481 |
482 | public String getName() {
483 | return name;
484 | }
485 |
486 | public void setName(String name) {
487 | this.name = name;
488 | }
489 |
490 | public String getEmail() {
491 | return email;
492 | }
493 |
494 | public void setEmail(String email) {
495 | this.email = email;
496 | }
497 | }
498 | }
499 | }
500 |
--------------------------------------------------------------------------------
/src/main/kotlin/org/lynxz/server/bean/GbMergeRequestEventBean.java:
--------------------------------------------------------------------------------
1 | package org.lynxz.server.bean;
2 |
3 | /**
4 | * Created by lynxz on 21/03/2017.
5 | * 实际的merge数据
6 | */
7 | public class GbMergeRequestEventBean {
8 |
9 | /**
10 | * object_kind : merge_request
11 | * user : {"name":"真实姓名","username":"lynxz","avatar_url":"https://gitlab.soundbus.tech/uploads/user/avatar/31/lynxz_avatar.png"}
12 | * project : {"name":"sonicmoving-android","description":"声动app andorid项目","web_url":"https://gitlab.soundbus.tech/sonicmoving-app-team/sonicmoving-android","avatar_url":null,"git_ssh_url":"git@gitlab.soundbus.tech:sonicmoving-app-team/sonicmoving-android.git","git_http_url":"https://gitlab.soundbus.tech/sonicmoving-app-team/sonicmoving-android.git","namespace":"sonicmoving-app-team","visibility_level":0,"path_with_namespace":"sonicmoving-app-team/sonicmoving-android","default_branch":"MASTER","homepage":"https://gitlab.soundbus.tech/sonicmoving-app-team/sonicmoving-android","url":"git@gitlab.soundbus.tech:sonicmoving-app-team/sonicmoving-android.git","ssh_url":"git@gitlab.soundbus.tech:sonicmoving-app-team/sonicmoving-android.git","http_url":"https://gitlab.soundbus.tech/sonicmoving-app-team/sonicmoving-android.git"}
13 | * object_attributes : {"id":3204,"target_branch":"zxzWdf","source_branch":"zxzUlife","source_project_id":216,"author_id":31,"assignee_id":31,"title":"Zxz ulife","created_at":"2017-03-21 15:05:00 +0800","updated_at":"2017-03-21 15:05:00 +0800","milestone_id":null,"state":"opened","merge_status":"unchecked","target_project_id":216,"iid":95,"description":"","position":0,"locked_at":null,"updated_by_id":null,"merge_error":null,"merge_params":{},"merge_when_build_succeeds":false,"merge_user_id":null,"merge_commit_sha":null,"deleted_at":null,"source":{"name":"sonicmoving-android","description":"??????app andorid??????","web_url":"https://gitlab.soundbus.tech/sonicmoving-app-team/sonicmoving-android","avatar_url":null,"git_ssh_url":"git@gitlab.soundbus.tech:sonicmoving-app-team/sonicmoving-android.git","git_http_url":"https://gitlab.soundbus.tech/sonicmoving-app-team/sonicmoving-android.git","namespace":"sonicmoving-app-team","visibility_level":0,"path_with_namespace":"sonicmoving-app-team/sonicmoving-android","default_branch":"MASTER","homepage":"https://gitlab.soundbus.tech/sonicmoving-app-team/sonicmoving-android","url":"git@gitlab.soundbus.tech:sonicmoving-app-team/sonicmoving-android.git","ssh_url":"git@gitlab.soundbus.tech:sonicmoving-app-team/sonicmoving-android.git","http_url":"https://gitlab.soundbus.tech/sonicmoving-app-team/sonicmoving-android.git"},"target":{"name":"sonicmoving-android","description":"??????app andorid??????","web_url":"https://gitlab.soundbus.tech/sonicmoving-app-team/sonicmoving-android","avatar_url":null,"git_ssh_url":"git@gitlab.soundbus.tech:sonicmoving-app-team/sonicmoving-android.git","git_http_url":"https://gitlab.soundbus.tech/sonicmoving-app-team/sonicmoving-android.git","namespace":"sonicmoving-app-team","visibility_level":0,"path_with_namespace":"sonicmoving-app-team/sonicmoving-android","default_branch":"MASTER","homepage":"https://gitlab.soundbus.tech/sonicmoving-app-team/sonicmoving-android","url":"git@gitlab.soundbus.tech:sonicmoving-app-team/sonicmoving-android.git","ssh_url":"git@gitlab.soundbus.tech:sonicmoving-app-team/sonicmoving-android.git","http_url":"https://gitlab.soundbus.tech/sonicmoving-app-team/sonicmoving-android.git"},"last_commit":{"id":"71cf3cf59d33dd03b2bc34145dbea9b6e7444423","message":"??????hook\n","timestamp":"2017-03-21T14:54:36+08:00","url":"https://gitlab.soundbus.tech/sonicmoving-app-team/sonicmoving-android/commit/71cf3cf59d33dd03b2bc34145dbea9b6e7444423","author":{"name":"lynxz","email":"lynxz8866@gmail.com"}},"work_in_progress":false,"url":"https://gitlab.soundbus.tech/sonicmoving-app-team/sonicmoving-android/merge_requests/95","action":"open"}
14 | * repository : {"name":"sonicmoving-android","url":"git@gitlab.soundbus.tech:sonicmoving-app-team/sonicmoving-android.git","description":"??????app andorid??????","homepage":"https://gitlab.soundbus.tech/sonicmoving-app-team/sonicmoving-android"}
15 | * assignee : {"name":"真实姓名","username":"lynxz","avatar_url":"https://gitlab.soundbus.tech/uploads/user/avatar/31/lynxz_avatar.png"}
16 | */
17 |
18 | private String object_kind;
19 | private UserBean user;
20 | private ProjectBean project;
21 | private ObjectAttributesBean object_attributes;
22 | private RepositoryBean repository;
23 | private AssigneeBean assignee;
24 |
25 | public String getObject_kind() {
26 | return object_kind;
27 | }
28 |
29 | public void setObject_kind(String object_kind) {
30 | this.object_kind = object_kind;
31 | }
32 |
33 | public UserBean getUser() {
34 | return user;
35 | }
36 |
37 | public void setUser(UserBean user) {
38 | this.user = user;
39 | }
40 |
41 | public ProjectBean getProject() {
42 | return project;
43 | }
44 |
45 | public void setProject(ProjectBean project) {
46 | this.project = project;
47 | }
48 |
49 | public ObjectAttributesBean getObject_attributes() {
50 | return object_attributes;
51 | }
52 |
53 | public void setObject_attributes(ObjectAttributesBean object_attributes) {
54 | this.object_attributes = object_attributes;
55 | }
56 |
57 | public RepositoryBean getRepository() {
58 | return repository;
59 | }
60 |
61 | public void setRepository(RepositoryBean repository) {
62 | this.repository = repository;
63 | }
64 |
65 | public AssigneeBean getAssignee() {
66 | return assignee;
67 | }
68 |
69 | public void setAssignee(AssigneeBean assignee) {
70 | this.assignee = assignee;
71 | }
72 |
73 | public static class UserBean {
74 | /**
75 | * name : 真实姓名
76 | * username : lynxz
77 | * avatar_url : https://gitlab.soundbus.tech/uploads/user/avatar/31/lynxz_avatar.png
78 | */
79 |
80 | private String name;
81 | private String username;
82 | private String avatar_url;
83 |
84 | public String getName() {
85 | return name;
86 | }
87 |
88 | public void setName(String name) {
89 | this.name = name;
90 | }
91 |
92 | public String getUsername() {
93 | return username;
94 | }
95 |
96 | public void setUsername(String username) {
97 | this.username = username;
98 | }
99 |
100 | public String getAvatar_url() {
101 | return avatar_url;
102 | }
103 |
104 | public void setAvatar_url(String avatar_url) {
105 | this.avatar_url = avatar_url;
106 | }
107 | }
108 |
109 | public static class ProjectBean {
110 | /**
111 | * name : sonicmoving-android
112 | * description : 声动app andorid项目
113 | * web_url : https://gitlab.soundbus.tech/sonicmoving-app-team/sonicmoving-android
114 | * avatar_url : null
115 | * git_ssh_url : git@gitlab.soundbus.tech:sonicmoving-app-team/sonicmoving-android.git
116 | * git_http_url : https://gitlab.soundbus.tech/sonicmoving-app-team/sonicmoving-android.git
117 | * namespace : sonicmoving-app-team
118 | * visibility_level : 0
119 | * path_with_namespace : sonicmoving-app-team/sonicmoving-android
120 | * default_branch : MASTER
121 | * homepage : https://gitlab.soundbus.tech/sonicmoving-app-team/sonicmoving-android
122 | * url : git@gitlab.soundbus.tech:sonicmoving-app-team/sonicmoving-android.git
123 | * ssh_url : git@gitlab.soundbus.tech:sonicmoving-app-team/sonicmoving-android.git
124 | * http_url : https://gitlab.soundbus.tech/sonicmoving-app-team/sonicmoving-android.git
125 | */
126 |
127 | private String name;
128 | private String description;
129 | private String web_url;
130 | private Object avatar_url;
131 | private String git_ssh_url;
132 | private String git_http_url;
133 | private String namespace;
134 | private int visibility_level;
135 | private String path_with_namespace;
136 | private String default_branch;
137 | private String homepage;
138 | private String url;
139 | private String ssh_url;
140 | private String http_url;
141 |
142 | public String getName() {
143 | return name;
144 | }
145 |
146 | public void setName(String name) {
147 | this.name = name;
148 | }
149 |
150 | public String getDescription() {
151 | return description;
152 | }
153 |
154 | public void setDescription(String description) {
155 | this.description = description;
156 | }
157 |
158 | public String getWeb_url() {
159 | return web_url;
160 | }
161 |
162 | public void setWeb_url(String web_url) {
163 | this.web_url = web_url;
164 | }
165 |
166 | public Object getAvatar_url() {
167 | return avatar_url;
168 | }
169 |
170 | public void setAvatar_url(Object avatar_url) {
171 | this.avatar_url = avatar_url;
172 | }
173 |
174 | public String getGit_ssh_url() {
175 | return git_ssh_url;
176 | }
177 |
178 | public void setGit_ssh_url(String git_ssh_url) {
179 | this.git_ssh_url = git_ssh_url;
180 | }
181 |
182 | public String getGit_http_url() {
183 | return git_http_url;
184 | }
185 |
186 | public void setGit_http_url(String git_http_url) {
187 | this.git_http_url = git_http_url;
188 | }
189 |
190 | public String getNamespace() {
191 | return namespace;
192 | }
193 |
194 | public void setNamespace(String namespace) {
195 | this.namespace = namespace;
196 | }
197 |
198 | public int getVisibility_level() {
199 | return visibility_level;
200 | }
201 |
202 | public void setVisibility_level(int visibility_level) {
203 | this.visibility_level = visibility_level;
204 | }
205 |
206 | public String getPath_with_namespace() {
207 | return path_with_namespace;
208 | }
209 |
210 | public void setPath_with_namespace(String path_with_namespace) {
211 | this.path_with_namespace = path_with_namespace;
212 | }
213 |
214 | public String getDefault_branch() {
215 | return default_branch;
216 | }
217 |
218 | public void setDefault_branch(String default_branch) {
219 | this.default_branch = default_branch;
220 | }
221 |
222 | public String getHomepage() {
223 | return homepage;
224 | }
225 |
226 | public void setHomepage(String homepage) {
227 | this.homepage = homepage;
228 | }
229 |
230 | public String getUrl() {
231 | return url;
232 | }
233 |
234 | public void setUrl(String url) {
235 | this.url = url;
236 | }
237 |
238 | public String getSsh_url() {
239 | return ssh_url;
240 | }
241 |
242 | public void setSsh_url(String ssh_url) {
243 | this.ssh_url = ssh_url;
244 | }
245 |
246 | public String getHttp_url() {
247 | return http_url;
248 | }
249 |
250 | public void setHttp_url(String http_url) {
251 | this.http_url = http_url;
252 | }
253 | }
254 |
255 | public static class ObjectAttributesBean {
256 | /**
257 | * id : 3204
258 | * target_branch : zxzWdf
259 | * source_branch : zxzUlife
260 | * source_project_id : 216
261 | * author_id : 31
262 | * assignee_id : 31
263 | * title : Zxz ulife
264 | * created_at : 2017-03-21 15:05:00 +0800
265 | * updated_at : 2017-03-21 15:05:00 +0800
266 | * milestone_id : null
267 | * state : opened
268 | * merge_status : unchecked
269 | * target_project_id : 216
270 | * iid : 95
271 | * description :
272 | * position : 0
273 | * locked_at : null
274 | * updated_by_id : null
275 | * merge_error : null
276 | * merge_params : {}
277 | * merge_when_build_succeeds : false
278 | * merge_user_id : null
279 | * merge_commit_sha : null
280 | * deleted_at : null
281 | * source : {"name":"sonicmoving-android","description":"??????app andorid??????","web_url":"https://gitlab.soundbus.tech/sonicmoving-app-team/sonicmoving-android","avatar_url":null,"git_ssh_url":"git@gitlab.soundbus.tech:sonicmoving-app-team/sonicmoving-android.git","git_http_url":"https://gitlab.soundbus.tech/sonicmoving-app-team/sonicmoving-android.git","namespace":"sonicmoving-app-team","visibility_level":0,"path_with_namespace":"sonicmoving-app-team/sonicmoving-android","default_branch":"MASTER","homepage":"https://gitlab.soundbus.tech/sonicmoving-app-team/sonicmoving-android","url":"git@gitlab.soundbus.tech:sonicmoving-app-team/sonicmoving-android.git","ssh_url":"git@gitlab.soundbus.tech:sonicmoving-app-team/sonicmoving-android.git","http_url":"https://gitlab.soundbus.tech/sonicmoving-app-team/sonicmoving-android.git"}
282 | * target : {"name":"sonicmoving-android","description":"??????app andorid??????","web_url":"https://gitlab.soundbus.tech/sonicmoving-app-team/sonicmoving-android","avatar_url":null,"git_ssh_url":"git@gitlab.soundbus.tech:sonicmoving-app-team/sonicmoving-android.git","git_http_url":"https://gitlab.soundbus.tech/sonicmoving-app-team/sonicmoving-android.git","namespace":"sonicmoving-app-team","visibility_level":0,"path_with_namespace":"sonicmoving-app-team/sonicmoving-android","default_branch":"MASTER","homepage":"https://gitlab.soundbus.tech/sonicmoving-app-team/sonicmoving-android","url":"git@gitlab.soundbus.tech:sonicmoving-app-team/sonicmoving-android.git","ssh_url":"git@gitlab.soundbus.tech:sonicmoving-app-team/sonicmoving-android.git","http_url":"https://gitlab.soundbus.tech/sonicmoving-app-team/sonicmoving-android.git"}
283 | * last_commit : {"id":"71cf3cf59d33dd03b2bc34145dbea9b6e7444423","message":"??????hook\n","timestamp":"2017-03-21T14:54:36+08:00","url":"https://gitlab.soundbus.tech/sonicmoving-app-team/sonicmoving-android/commit/71cf3cf59d33dd03b2bc34145dbea9b6e7444423","author":{"name":"lynxz","email":"lynxz8866@gmail.com"}}
284 | * work_in_progress : false
285 | * url : https://gitlab.soundbus.tech/sonicmoving-app-team/sonicmoving-android/merge_requests/95
286 | * action : open
287 | */
288 |
289 | private int id;
290 | private String target_branch;
291 | private String source_branch;
292 | private int source_project_id;
293 | private int author_id;
294 | private int assignee_id;
295 | private String title;
296 | private String created_at;
297 | private String updated_at;
298 | private Object milestone_id;
299 | private String state;
300 | private String merge_status;
301 | private int target_project_id;
302 | private int iid;
303 | private String description;
304 | private int position;
305 | private Object locked_at;
306 | private Object updated_by_id;
307 | private Object merge_error;
308 | private MergeParamsBean merge_params;
309 | private boolean merge_when_build_succeeds;
310 | private Object merge_user_id;
311 | private String merge_commit_sha;
312 | private Object deleted_at;
313 | private SourceBean source;
314 | private TargetBean target;
315 | private LastCommitBean last_commit;
316 | private boolean work_in_progress;
317 | private String url;
318 | private String action;
319 |
320 | public int getId() {
321 | return id;
322 | }
323 |
324 | public void setId(int id) {
325 | this.id = id;
326 | }
327 |
328 | public String getTarget_branch() {
329 | return target_branch;
330 | }
331 |
332 | public void setTarget_branch(String target_branch) {
333 | this.target_branch = target_branch;
334 | }
335 |
336 | public String getSource_branch() {
337 | return source_branch;
338 | }
339 |
340 | public void setSource_branch(String source_branch) {
341 | this.source_branch = source_branch;
342 | }
343 |
344 | public int getSource_project_id() {
345 | return source_project_id;
346 | }
347 |
348 | public void setSource_project_id(int source_project_id) {
349 | this.source_project_id = source_project_id;
350 | }
351 |
352 | public int getAuthor_id() {
353 | return author_id;
354 | }
355 |
356 | public void setAuthor_id(int author_id) {
357 | this.author_id = author_id;
358 | }
359 |
360 | public int getAssignee_id() {
361 | return assignee_id;
362 | }
363 |
364 | public void setAssignee_id(int assignee_id) {
365 | this.assignee_id = assignee_id;
366 | }
367 |
368 | public String getTitle() {
369 | return title;
370 | }
371 |
372 | public void setTitle(String title) {
373 | this.title = title;
374 | }
375 |
376 | public String getCreated_at() {
377 | return created_at;
378 | }
379 |
380 | public void setCreated_at(String created_at) {
381 | this.created_at = created_at;
382 | }
383 |
384 | public String getUpdated_at() {
385 | return updated_at;
386 | }
387 |
388 | public void setUpdated_at(String updated_at) {
389 | this.updated_at = updated_at;
390 | }
391 |
392 | public Object getMilestone_id() {
393 | return milestone_id;
394 | }
395 |
396 | public void setMilestone_id(Object milestone_id) {
397 | this.milestone_id = milestone_id;
398 | }
399 |
400 | public String getState() {
401 | return state;
402 | }
403 |
404 | public void setState(String state) {
405 | this.state = state;
406 | }
407 |
408 | public String getMerge_status() {
409 | return merge_status;
410 | }
411 |
412 | public void setMerge_status(String merge_status) {
413 | this.merge_status = merge_status;
414 | }
415 |
416 | public int getTarget_project_id() {
417 | return target_project_id;
418 | }
419 |
420 | public void setTarget_project_id(int target_project_id) {
421 | this.target_project_id = target_project_id;
422 | }
423 |
424 | public int getIid() {
425 | return iid;
426 | }
427 |
428 | public void setIid(int iid) {
429 | this.iid = iid;
430 | }
431 |
432 | public String getDescription() {
433 | return description;
434 | }
435 |
436 | public void setDescription(String description) {
437 | this.description = description;
438 | }
439 |
440 | public int getPosition() {
441 | return position;
442 | }
443 |
444 | public void setPosition(int position) {
445 | this.position = position;
446 | }
447 |
448 | public Object getLocked_at() {
449 | return locked_at;
450 | }
451 |
452 | public void setLocked_at(Object locked_at) {
453 | this.locked_at = locked_at;
454 | }
455 |
456 | public Object getUpdated_by_id() {
457 | return updated_by_id;
458 | }
459 |
460 | public void setUpdated_by_id(Object updated_by_id) {
461 | this.updated_by_id = updated_by_id;
462 | }
463 |
464 | public Object getMerge_error() {
465 | return merge_error;
466 | }
467 |
468 | public void setMerge_error(Object merge_error) {
469 | this.merge_error = merge_error;
470 | }
471 |
472 | public MergeParamsBean getMerge_params() {
473 | return merge_params;
474 | }
475 |
476 | public void setMerge_params(MergeParamsBean merge_params) {
477 | this.merge_params = merge_params;
478 | }
479 |
480 | public boolean isMerge_when_build_succeeds() {
481 | return merge_when_build_succeeds;
482 | }
483 |
484 | public void setMerge_when_build_succeeds(boolean merge_when_build_succeeds) {
485 | this.merge_when_build_succeeds = merge_when_build_succeeds;
486 | }
487 |
488 | public Object getMerge_user_id() {
489 | return merge_user_id;
490 | }
491 |
492 | public void setMerge_user_id(Object merge_user_id) {
493 | this.merge_user_id = merge_user_id;
494 | }
495 |
496 | public String getMerge_commit_sha() {
497 | return merge_commit_sha;
498 | }
499 |
500 | public void setMerge_commit_sha(String merge_commit_sha) {
501 | this.merge_commit_sha = merge_commit_sha;
502 | }
503 |
504 | public Object getDeleted_at() {
505 | return deleted_at;
506 | }
507 |
508 | public void setDeleted_at(Object deleted_at) {
509 | this.deleted_at = deleted_at;
510 | }
511 |
512 | public SourceBean getSource() {
513 | return source;
514 | }
515 |
516 | public void setSource(SourceBean source) {
517 | this.source = source;
518 | }
519 |
520 | public TargetBean getTarget() {
521 | return target;
522 | }
523 |
524 | public void setTarget(TargetBean target) {
525 | this.target = target;
526 | }
527 |
528 | public LastCommitBean getLast_commit() {
529 | return last_commit;
530 | }
531 |
532 | public void setLast_commit(LastCommitBean last_commit) {
533 | this.last_commit = last_commit;
534 | }
535 |
536 | public boolean isWork_in_progress() {
537 | return work_in_progress;
538 | }
539 |
540 | public void setWork_in_progress(boolean work_in_progress) {
541 | this.work_in_progress = work_in_progress;
542 | }
543 |
544 | public String getUrl() {
545 | return url;
546 | }
547 |
548 | public void setUrl(String url) {
549 | this.url = url;
550 | }
551 |
552 | public String getAction() {
553 | return action;
554 | }
555 |
556 | public void setAction(String action) {
557 | this.action = action;
558 | }
559 |
560 | public static class MergeParamsBean {
561 | }
562 |
563 | public static class SourceBean {
564 | /**
565 | * name : sonicmoving-android
566 | * description : ??????app andorid??????
567 | * web_url : https://gitlab.soundbus.tech/sonicmoving-app-team/sonicmoving-android
568 | * avatar_url : null
569 | * git_ssh_url : git@gitlab.soundbus.tech:sonicmoving-app-team/sonicmoving-android.git
570 | * git_http_url : https://gitlab.soundbus.tech/sonicmoving-app-team/sonicmoving-android.git
571 | * namespace : sonicmoving-app-team
572 | * visibility_level : 0
573 | * path_with_namespace : sonicmoving-app-team/sonicmoving-android
574 | * default_branch : MASTER
575 | * homepage : https://gitlab.soundbus.tech/sonicmoving-app-team/sonicmoving-android
576 | * url : git@gitlab.soundbus.tech:sonicmoving-app-team/sonicmoving-android.git
577 | * ssh_url : git@gitlab.soundbus.tech:sonicmoving-app-team/sonicmoving-android.git
578 | * http_url : https://gitlab.soundbus.tech/sonicmoving-app-team/sonicmoving-android.git
579 | */
580 |
581 | private String name;
582 | private String description;
583 | private String web_url;
584 | private Object avatar_url;
585 | private String git_ssh_url;
586 | private String git_http_url;
587 | private String namespace;
588 | private int visibility_level;
589 | private String path_with_namespace;
590 | private String default_branch;
591 | private String homepage;
592 | private String url;
593 | private String ssh_url;
594 | private String http_url;
595 |
596 | public String getName() {
597 | return name;
598 | }
599 |
600 | public void setName(String name) {
601 | this.name = name;
602 | }
603 |
604 | public String getDescription() {
605 | return description;
606 | }
607 |
608 | public void setDescription(String description) {
609 | this.description = description;
610 | }
611 |
612 | public String getWeb_url() {
613 | return web_url;
614 | }
615 |
616 | public void setWeb_url(String web_url) {
617 | this.web_url = web_url;
618 | }
619 |
620 | public Object getAvatar_url() {
621 | return avatar_url;
622 | }
623 |
624 | public void setAvatar_url(Object avatar_url) {
625 | this.avatar_url = avatar_url;
626 | }
627 |
628 | public String getGit_ssh_url() {
629 | return git_ssh_url;
630 | }
631 |
632 | public void setGit_ssh_url(String git_ssh_url) {
633 | this.git_ssh_url = git_ssh_url;
634 | }
635 |
636 | public String getGit_http_url() {
637 | return git_http_url;
638 | }
639 |
640 | public void setGit_http_url(String git_http_url) {
641 | this.git_http_url = git_http_url;
642 | }
643 |
644 | public String getNamespace() {
645 | return namespace;
646 | }
647 |
648 | public void setNamespace(String namespace) {
649 | this.namespace = namespace;
650 | }
651 |
652 | public int getVisibility_level() {
653 | return visibility_level;
654 | }
655 |
656 | public void setVisibility_level(int visibility_level) {
657 | this.visibility_level = visibility_level;
658 | }
659 |
660 | public String getPath_with_namespace() {
661 | return path_with_namespace;
662 | }
663 |
664 | public void setPath_with_namespace(String path_with_namespace) {
665 | this.path_with_namespace = path_with_namespace;
666 | }
667 |
668 | public String getDefault_branch() {
669 | return default_branch;
670 | }
671 |
672 | public void setDefault_branch(String default_branch) {
673 | this.default_branch = default_branch;
674 | }
675 |
676 | public String getHomepage() {
677 | return homepage;
678 | }
679 |
680 | public void setHomepage(String homepage) {
681 | this.homepage = homepage;
682 | }
683 |
684 | public String getUrl() {
685 | return url;
686 | }
687 |
688 | public void setUrl(String url) {
689 | this.url = url;
690 | }
691 |
692 | public String getSsh_url() {
693 | return ssh_url;
694 | }
695 |
696 | public void setSsh_url(String ssh_url) {
697 | this.ssh_url = ssh_url;
698 | }
699 |
700 | public String getHttp_url() {
701 | return http_url;
702 | }
703 |
704 | public void setHttp_url(String http_url) {
705 | this.http_url = http_url;
706 | }
707 | }
708 |
709 | public static class TargetBean {
710 | /**
711 | * name : sonicmoving-android
712 | * description : ??????app andorid??????
713 | * web_url : https://gitlab.soundbus.tech/sonicmoving-app-team/sonicmoving-android
714 | * avatar_url : null
715 | * git_ssh_url : git@gitlab.soundbus.tech:sonicmoving-app-team/sonicmoving-android.git
716 | * git_http_url : https://gitlab.soundbus.tech/sonicmoving-app-team/sonicmoving-android.git
717 | * namespace : sonicmoving-app-team
718 | * visibility_level : 0
719 | * path_with_namespace : sonicmoving-app-team/sonicmoving-android
720 | * default_branch : MASTER
721 | * homepage : https://gitlab.soundbus.tech/sonicmoving-app-team/sonicmoving-android
722 | * url : git@gitlab.soundbus.tech:sonicmoving-app-team/sonicmoving-android.git
723 | * ssh_url : git@gitlab.soundbus.tech:sonicmoving-app-team/sonicmoving-android.git
724 | * http_url : https://gitlab.soundbus.tech/sonicmoving-app-team/sonicmoving-android.git
725 | */
726 |
727 | private String name;
728 | private String description;
729 | private String web_url;
730 | private Object avatar_url;
731 | private String git_ssh_url;
732 | private String git_http_url;
733 | private String namespace;
734 | private int visibility_level;
735 | private String path_with_namespace;
736 | private String default_branch;
737 | private String homepage;
738 | private String url;
739 | private String ssh_url;
740 | private String http_url;
741 |
742 | public String getName() {
743 | return name;
744 | }
745 |
746 | public void setName(String name) {
747 | this.name = name;
748 | }
749 |
750 | public String getDescription() {
751 | return description;
752 | }
753 |
754 | public void setDescription(String description) {
755 | this.description = description;
756 | }
757 |
758 | public String getWeb_url() {
759 | return web_url;
760 | }
761 |
762 | public void setWeb_url(String web_url) {
763 | this.web_url = web_url;
764 | }
765 |
766 | public Object getAvatar_url() {
767 | return avatar_url;
768 | }
769 |
770 | public void setAvatar_url(Object avatar_url) {
771 | this.avatar_url = avatar_url;
772 | }
773 |
774 | public String getGit_ssh_url() {
775 | return git_ssh_url;
776 | }
777 |
778 | public void setGit_ssh_url(String git_ssh_url) {
779 | this.git_ssh_url = git_ssh_url;
780 | }
781 |
782 | public String getGit_http_url() {
783 | return git_http_url;
784 | }
785 |
786 | public void setGit_http_url(String git_http_url) {
787 | this.git_http_url = git_http_url;
788 | }
789 |
790 | public String getNamespace() {
791 | return namespace;
792 | }
793 |
794 | public void setNamespace(String namespace) {
795 | this.namespace = namespace;
796 | }
797 |
798 | public int getVisibility_level() {
799 | return visibility_level;
800 | }
801 |
802 | public void setVisibility_level(int visibility_level) {
803 | this.visibility_level = visibility_level;
804 | }
805 |
806 | public String getPath_with_namespace() {
807 | return path_with_namespace;
808 | }
809 |
810 | public void setPath_with_namespace(String path_with_namespace) {
811 | this.path_with_namespace = path_with_namespace;
812 | }
813 |
814 | public String getDefault_branch() {
815 | return default_branch;
816 | }
817 |
818 | public void setDefault_branch(String default_branch) {
819 | this.default_branch = default_branch;
820 | }
821 |
822 | public String getHomepage() {
823 | return homepage;
824 | }
825 |
826 | public void setHomepage(String homepage) {
827 | this.homepage = homepage;
828 | }
829 |
830 | public String getUrl() {
831 | return url;
832 | }
833 |
834 | public void setUrl(String url) {
835 | this.url = url;
836 | }
837 |
838 | public String getSsh_url() {
839 | return ssh_url;
840 | }
841 |
842 | public void setSsh_url(String ssh_url) {
843 | this.ssh_url = ssh_url;
844 | }
845 |
846 | public String getHttp_url() {
847 | return http_url;
848 | }
849 |
850 | public void setHttp_url(String http_url) {
851 | this.http_url = http_url;
852 | }
853 | }
854 |
855 | public static class LastCommitBean {
856 | /**
857 | * id : 71cf3cf59d33dd03b2bc34145dbea9b6e7444423
858 | * message : ??????hook
859 | *
860 | * timestamp : 2017-03-21T14:54:36+08:00
861 | * url : https://gitlab.soundbus.tech/sonicmoving-app-team/sonicmoving-android/commit/71cf3cf59d33dd03b2bc34145dbea9b6e7444423
862 | * author : {"name":"lynxz","email":"lynxz8866@gmail.com"}
863 | */
864 |
865 | private String id;
866 | private String message;
867 | private String timestamp;
868 | private String url;
869 | private AuthorBean author;
870 |
871 | public String getId() {
872 | return id;
873 | }
874 |
875 | public void setId(String id) {
876 | this.id = id;
877 | }
878 |
879 | public String getMessage() {
880 | return message;
881 | }
882 |
883 | public void setMessage(String message) {
884 | this.message = message;
885 | }
886 |
887 | public String getTimestamp() {
888 | return timestamp;
889 | }
890 |
891 | public void setTimestamp(String timestamp) {
892 | this.timestamp = timestamp;
893 | }
894 |
895 | public String getUrl() {
896 | return url;
897 | }
898 |
899 | public void setUrl(String url) {
900 | this.url = url;
901 | }
902 |
903 | public AuthorBean getAuthor() {
904 | return author;
905 | }
906 |
907 | public void setAuthor(AuthorBean author) {
908 | this.author = author;
909 | }
910 |
911 | public static class AuthorBean {
912 | /**
913 | * name : lynxz
914 | * email : lynxz8866@gmail.com
915 | */
916 |
917 | private String name;
918 | private String email;
919 |
920 | public String getName() {
921 | return name;
922 | }
923 |
924 | public void setName(String name) {
925 | this.name = name;
926 | }
927 |
928 | public String getEmail() {
929 | return email;
930 | }
931 |
932 | public void setEmail(String email) {
933 | this.email = email;
934 | }
935 | }
936 | }
937 | }
938 |
939 | public static class RepositoryBean {
940 | /**
941 | * name : sonicmoving-android
942 | * url : git@gitlab.soundbus.tech:sonicmoving-app-team/sonicmoving-android.git
943 | * description : ??????app andorid??????
944 | * homepage : https://gitlab.soundbus.tech/sonicmoving-app-team/sonicmoving-android
945 | */
946 |
947 | private String name;
948 | private String url;
949 | private String description;
950 | private String homepage;
951 |
952 | public String getName() {
953 | return name;
954 | }
955 |
956 | public void setName(String name) {
957 | this.name = name;
958 | }
959 |
960 | public String getUrl() {
961 | return url;
962 | }
963 |
964 | public void setUrl(String url) {
965 | this.url = url;
966 | }
967 |
968 | public String getDescription() {
969 | return description;
970 | }
971 |
972 | public void setDescription(String description) {
973 | this.description = description;
974 | }
975 |
976 | public String getHomepage() {
977 | return homepage;
978 | }
979 |
980 | public void setHomepage(String homepage) {
981 | this.homepage = homepage;
982 | }
983 | }
984 |
985 | public static class AssigneeBean {
986 | /**
987 | * name : 真实姓名
988 | * username : lynxz
989 | * avatar_url : https://gitlab.soundbus.tech/uploads/user/avatar/31/lynxz_avatar.png
990 | */
991 |
992 | private String name;
993 | private String username;
994 | private String avatar_url;
995 |
996 | public String getName() {
997 | return name;
998 | }
999 |
1000 | public void setName(String name) {
1001 | this.name = name;
1002 | }
1003 |
1004 | public String getUsername() {
1005 | return username;
1006 | }
1007 |
1008 | public void setUsername(String username) {
1009 | this.username = username;
1010 | }
1011 |
1012 | public String getAvatar_url() {
1013 | return avatar_url;
1014 | }
1015 |
1016 | public void setAvatar_url(String avatar_url) {
1017 | this.avatar_url = avatar_url;
1018 | }
1019 | }
1020 | }
1021 |
--------------------------------------------------------------------------------