├── .gitignore
├── LICENSE
├── README.md
├── db
└── init.sql
├── docs
└── demo.gif
├── pom.xml
└── src
├── main
├── java
│ └── org
│ │ └── catframework
│ │ └── agileworking
│ │ ├── Application.java
│ │ ├── BusinessException.java
│ │ ├── common
│ │ └── ResponseCodes.java
│ │ ├── domain
│ │ ├── MeetingRoom.java
│ │ ├── MeetingRoomRepository.java
│ │ ├── Participant.java
│ │ ├── ParticipantRepository.java
│ │ ├── Schedule.java
│ │ ├── ScheduleRepository.java
│ │ ├── ScheduleRepositoryCustom.java
│ │ ├── ScheduleRepositoryImpl.java
│ │ ├── Team.java
│ │ ├── TeamRepository.java
│ │ ├── User.java
│ │ ├── UserRepository.java
│ │ └── package-info.java
│ │ ├── scheduling
│ │ └── SendNotifyMessageJob.java
│ │ ├── service
│ │ ├── ScheduleService.java
│ │ ├── WebTokenService.java
│ │ ├── impl
│ │ │ ├── ScheduleServiceImpl.java
│ │ │ └── SimpleJJWTWebTokenServiceImpl.java
│ │ └── package-info.java
│ │ ├── utils
│ │ ├── DateUtils.java
│ │ └── JsonUtils.java
│ │ ├── vo
│ │ ├── NotifyMessageTemplate.java
│ │ └── ScheduleVO.java
│ │ └── web
│ │ ├── MeetingRoomController.java
│ │ ├── ParticipantController.java
│ │ ├── ScheduleController.java
│ │ ├── TeamController.java
│ │ ├── WechatController.java
│ │ ├── package-info.java
│ │ └── support
│ │ ├── DefaultResult.java
│ │ ├── GlobalExceptionHandler.java
│ │ ├── Result.java
│ │ └── WebTokenHandlerInterceptor.java
└── resources
│ ├── application-dev.properties
│ ├── application-prd.properties
│ ├── application.properties
│ └── logback.xml
└── test
├── java
└── org
│ └── catframework
│ └── agileworking
│ ├── domain
│ ├── MeetingRoomFactory.java
│ ├── ScheduleFactory.java
│ ├── ScheduleRepositoryTest.java
│ ├── ScheduleTest.java
│ ├── TeamFactory.java
│ └── UserFactory.java
│ ├── package-info.java
│ ├── scheduling
│ └── SendNotifyMessageJobTest.java
│ ├── service
│ └── impl
│ │ ├── ScheduleServiceImplTest.java
│ │ ├── SimpleJJWTWebTokenServiceImplTest.java
│ │ └── WeChatApiIntegrationTest.java
│ ├── utils
│ ├── DateUtilsTest.java
│ └── JsonUtilsTest.java
│ ├── vo
│ └── NotifyMessageTemplateTest.java
│ └── web
│ ├── MeetingRoomControllerIntegrationTest.java
│ ├── MeetingRoomControllerTest.java
│ ├── ScheduleControllerTest.java
│ ├── TeamControllerTest.java
│ └── WechatControllerIntegrationTest.java
└── resources
└── logback-test.xml
/.gitignore:
--------------------------------------------------------------------------------
1 | /target/
2 | /.classpath
3 | /.project
4 | /.idea
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2017 7upcat
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # agile-wroking-backend
2 |
3 | 此项目是 *real world* 小程序 *AgileWorking* 应用的后端,*AgileWorking* 是一个用于部门内部会议室及团队管理的小程序,小程序的前端实现 可以访问此项目 [agile-working](https://github.com/wufajin/agile-working-1.0.0)。
4 |
5 | ## 特色
6 |
7 | - 全面使用 *Java8* 函数式编码风格,代码简洁、高效,易读
8 | - 采用 [领域模型设计](https://en.wikipedia.org/wiki/Domain-driven_design) 的风格组织代码,应对复杂逻辑
9 | - 使用 *SpringBoot* 构建,*Convension Over Configuration*,零配置
10 | - 提供 *Restful* 风格的 API 易于前端访问
11 | - 支持小程序的会话管理及接口调用的安全控制
12 | - 支持会议提醒及邀请其他人加入会议
13 |
14 | ## 演示
15 |
16 | 
17 |
18 | ## 构建
19 |
20 | ### 前置条件
21 |
22 | - Jdk1.8+
23 | - 安装配置 [Maven](http://maven.apache.org/install.html)
24 | - 安装 [Git](https://git-scm.com/downloads)
25 |
26 | ### 构建步骤
27 |
28 | - git clone https://github.com/7upcat/agile-wroking-backend.git
29 | - cd agile-working-backend
30 | - mvn package
31 | - java -jar target\agile-working-backend-1.0.3.jar
32 |
33 | ## 单元测试
34 |
35 | - 进行单元测试前在 *src/main/resources/application.properties* 中设置 profile `spring.profiles.active=dev`,将会自动连接测试的数据库,并在每个
36 | 案例执行前重新建表
37 | - 进行单元测试 `mvn test`
38 |
39 | ## 接口清单
40 |
41 | 接口设计遵循 *Restful* 风格的 **API**,**公开** 标签下的服务可以直接访问,**私有** 标签的服务必须在 http header 中指定 `Authorization`(Token) 及 `Subject`(微信 openId):
42 |
43 | - 【公开】通过 `jsCode` 获取用户的 `openId` `/agileworking/wechat/openid/{jsCode}`,成功查询 payload 中返回的即是 openId
44 |
45 | - 【公开】查询团队列表,返回所有的团队 `/agileworking/teams`
46 |
47 | - 【公开】查询指定 `openId` 的用户是否有加入指定的团队 **GET** `/agileworking/team/{teamId}/user/{openId}`,如果加入则返回 `User` 信息及 `token`
48 |
49 | - 【公开】加入指定的团队 **POST** `/agileworking/team/{id}/join` ,加入成功会返回 `User` 及 `token`
50 |
51 | + name/姓名
52 | + mobileNo/手机号
53 | + openId/微信 openId
54 | + nickName/微信昵称
55 | + avatarUrl/微信头像 url
56 | + token/团队的加入口令
57 |
58 | - 【公开】根据 `id` 查询指定的排期 **GET** `/agileworking/schedules/{id}`,含排期的参与人,此接口用于邀请其他用户打开小程序时使用,暂时放开为公共,后续待前端优化后修改回私有
59 |
60 | - 【私有】查询指定团队下的所有会议室列表 **GET** `/agileworking/meetingRooms/{teamId}`
61 |
62 | - 【私有】创建/修改排期 **POST** `/agileworking/meetingRooms/{id}/schedule?formId=?`
63 | + id/排期id(可选,创建排期为空)
64 | + title/标题
65 | + date/日期 `yyyy-MM-dd` 格式
66 | + startTime/开始时间(hh:min)
67 | + endTime/结束时间(hh:min)
68 | + creatorOpenId/创建人微信 openId
69 | + creatorNickName/创建人微信昵称
70 | + creatorAvatarUrl/创建人微信头像URL
71 | + repeatMode/会议重复模式(N-不重复/W-每周)
72 |
73 | - 【私有】取消排期 **DELETE** `/agileworking/meetingRooms/schedule/{id}`
74 |
75 | - 【私有】查询指定会议室指定日期的排期 **GET** `/agileworking/meetingRooms/{id}/schedule?date=yyyyMMdd`
76 |
77 | - 【私有】接受会议邀请 **POST** `/agileworking/schedules/{id}/join`
78 |
79 | + openId/接受邀请人微信 openId
80 | + nickName/接受邀请人微信昵称
81 | + avatarUrl/接受邀请人微信头像URL
82 | + formId/表单 id ,用于后续的微信消息通知
83 |
84 | - 【私有】查询加入的会议 **GET** `/agileworking/participant/{openId}?date=yyyyMMdd`
85 |
86 | + scheduleId/排期id
87 | + meetingRoomId/会议室 id
88 | + date/排期的日期
89 | + title/会议主题
90 | + openId/参会人的微信 openId
91 | + roomNo/会议室
92 | + startTime/开始时间
93 | + endTime/结束时间
94 | + repeatMode/会议重复模式(N-不重复/W-每周)
95 |
--------------------------------------------------------------------------------
/db/init.sql:
--------------------------------------------------------------------------------
1 | -- 组别初始化数据
2 | INSERT INTO team(id,name,team_desc,token) VALUES (1,'深研二部','一个敏捷的团队~','123456');
3 |
4 | -- 会议室初始化数据
5 | INSERT INTO meeting_room (ip, room_no, size, terminal_id, type, vnedor,team_id) VALUES ('182.207.96.163', '3201', '大', '21169', '视频', '思科',1);
6 | INSERT INTO meeting_room (ip, room_no, size, terminal_id, type, vnedor,team_id) VALUES ('182.207.96.166', '3202', '中', '120706', '视频', '华为',1);
7 | INSERT INTO meeting_room (ip, room_no, size, terminal_id, type, vnedor,team_id) VALUES (null, '3203', '小', null, '普通', null,1);
8 | INSERT INTO meeting_room (ip, room_no, size, terminal_id, type, vnedor,team_id) VALUES (null, '3207', '小', null, '普通', null,1);
9 | INSERT INTO meeting_room (ip, room_no, size, terminal_id, type, vnedor,team_id) VALUES (null, '3403', '小', null, '普通', null,1);
10 |
11 |
12 |
--------------------------------------------------------------------------------
/docs/demo.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/7upcat/agile-wroking-backend/3753b8226b28a77655b4746dc7f1d61be61a7969/docs/demo.gif
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
3 | 4.0.0
4 | org.catframework
5 | agile-working-backend
6 | 1.0.3
7 |
8 | org.springframework.boot
9 | spring-boot-starter-parent
10 | 1.5.5.RELEASE
11 |
12 |
13 | 1.8
14 |
15 |
16 |
17 | org.springframework.boot
18 | spring-boot-starter-web
19 |
20 |
21 | org.springframework.boot
22 | spring-boot-starter-data-jpa
23 |
24 |
25 | org.springframework.boot
26 | spring-boot-starter-websocket
27 |
28 |
29 | org.apache.tomcat
30 | tomcat-jdbc
31 |
32 |
33 | mysql
34 | mysql-connector-java
35 |
36 |
37 | io.jsonwebtoken
38 | jjwt
39 | 0.7.0
40 |
41 |
42 | org.springframework.boot
43 | spring-boot-starter-test
44 | test
45 |
46 |
47 |
48 |
49 |
50 | org.springframework.boot
51 | spring-boot-maven-plugin
52 |
53 |
54 | org.apache.maven.plugins
55 | maven-surefire-plugin
56 |
57 |
58 | **/*IntegrationTest.java
59 |
60 |
61 |
62 |
63 |
64 |
--------------------------------------------------------------------------------
/src/main/java/org/catframework/agileworking/Application.java:
--------------------------------------------------------------------------------
1 | package org.catframework.agileworking;
2 |
3 | import javax.sql.DataSource;
4 |
5 | import org.apache.tomcat.jdbc.pool.PoolProperties;
6 | import org.catframework.agileworking.web.support.WebTokenHandlerInterceptor;
7 | import org.springframework.boot.SpringApplication;
8 | import org.springframework.boot.autoconfigure.SpringBootApplication;
9 | import org.springframework.boot.context.properties.ConfigurationProperties;
10 | import org.springframework.context.annotation.Bean;
11 | import org.springframework.scheduling.annotation.EnableScheduling;
12 | import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
13 | import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
14 |
15 | @SpringBootApplication
16 | @EnableScheduling
17 | public class Application extends WebMvcConfigurerAdapter {
18 |
19 | @Bean
20 | public DataSource dataSource() {
21 | return new org.apache.tomcat.jdbc.pool.DataSource(poolProperties());
22 | }
23 |
24 | @Bean
25 | @ConfigurationProperties(prefix = "spring.datasource")
26 | public PoolProperties poolProperties() {
27 | return new PoolProperties();
28 | }
29 |
30 | @Bean
31 | WebTokenHandlerInterceptor webTokenHandlerInterceptor() {
32 | return new WebTokenHandlerInterceptor();
33 | }
34 |
35 | @Override
36 | public void addInterceptors(InterceptorRegistry registry) {
37 | registry.addInterceptor(webTokenHandlerInterceptor()).addPathPatterns("/**");
38 | }
39 |
40 | public static void main(String[] args) throws Exception {
41 | SpringApplication.run(Application.class, args);
42 | }
43 | }
--------------------------------------------------------------------------------
/src/main/java/org/catframework/agileworking/BusinessException.java:
--------------------------------------------------------------------------------
1 | package org.catframework.agileworking;
2 |
3 | public class BusinessException extends RuntimeException {
4 |
5 | private static final long serialVersionUID = 3999713779564898790L;
6 |
7 | private String code;
8 |
9 | public BusinessException(String code) {
10 | this.code = code;
11 | }
12 |
13 | public BusinessException(String code, String message) {
14 | super(message);
15 | this.code = code;
16 | }
17 |
18 | public BusinessException(String code, String message, Throwable cause) {
19 | super(message, cause);
20 | this.code = code;
21 | }
22 |
23 | public String getCode() {
24 | return code;
25 | }
26 |
27 | }
28 |
--------------------------------------------------------------------------------
/src/main/java/org/catframework/agileworking/common/ResponseCodes.java:
--------------------------------------------------------------------------------
1 | package org.catframework.agileworking.common;
2 |
3 | /**
4 | * 集中的定义系统中的响应码,格式如下:
5 | *
6 | * - 成功:统一使用 {@link #RESPONSE_CODE_SUCCESS}
7 | *
- 失败:ER+4位数字
8 | *
9 | *
10 | * @author devzzm
11 | *
12 | */
13 | public final class ResponseCodes {
14 |
15 | /** 响应码:成功. */
16 | public static final String RESPONSE_CODE_SUCCESS = "SC0000";
17 |
18 | /** 响应码:默认的系统处理异常,用于非预期的运行时异常. */
19 | public static final String RESPONSE_CODE_SYSTEM_ERROR = "ER0001";
20 | }
21 |
--------------------------------------------------------------------------------
/src/main/java/org/catframework/agileworking/domain/MeetingRoom.java:
--------------------------------------------------------------------------------
1 | package org.catframework.agileworking.domain;
2 |
3 | import java.io.Serializable;
4 |
5 | import javax.persistence.Column;
6 | import javax.persistence.Entity;
7 | import javax.persistence.GeneratedValue;
8 | import javax.persistence.Id;
9 |
10 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
11 |
12 | /**
13 | * 会议室实体.
14 | *
15 | * @author devzzm
16 | */
17 | @Entity
18 | @JsonIgnoreProperties({ "handler", "hibernateLazyInitializer" })
19 | public class MeetingRoom implements Serializable {
20 |
21 | private static final long serialVersionUID = -1701470269000866582L;
22 |
23 | @Id
24 | @GeneratedValue
25 | private Long id;
26 |
27 | @Column(nullable = false)
28 | private Long teamId;
29 |
30 | /** 会议室房间编号,唯一. */
31 | @Column(nullable = false, unique = true)
32 | private String roomNo;
33 |
34 | /** 会议室大小: 大/中/小. */
35 | @Column(nullable = false)
36 | private String size;
37 |
38 | /** 会议室类型: 视频/其他. */
39 | @Column(nullable = false)
40 | private String type;
41 |
42 | /** 会议室视频设备的厂商,可选:华为/思科. */
43 | private String vnedor;
44 |
45 | /** 会议室视频设备的ip,可选. */
46 | private String ip;
47 |
48 | /** 会议室视频设备终端 id,可选. */
49 | private String terminalId;
50 |
51 | public Long getId() {
52 | return id;
53 | }
54 |
55 | public void setId(Long id) {
56 | this.id = id;
57 | }
58 |
59 | public String getRoomNo() {
60 | return roomNo;
61 | }
62 |
63 | public void setRoomNo(String roomNo) {
64 | this.roomNo = roomNo;
65 | }
66 |
67 | public String getSize() {
68 | return size;
69 | }
70 |
71 | public void setSize(String size) {
72 | this.size = size;
73 | }
74 |
75 | public String getType() {
76 | return type;
77 | }
78 |
79 | public void setType(String type) {
80 | this.type = type;
81 | }
82 |
83 | public String getVnedor() {
84 | return vnedor;
85 | }
86 |
87 | public void setVnedor(String vnedor) {
88 | this.vnedor = vnedor;
89 | }
90 |
91 | public String getIp() {
92 | return ip;
93 | }
94 |
95 | public void setIp(String ip) {
96 | this.ip = ip;
97 | }
98 |
99 | public String getTerminalId() {
100 | return terminalId;
101 | }
102 |
103 | public void setTerminalId(String terminalId) {
104 | this.terminalId = terminalId;
105 | }
106 |
107 | public Long getTeamId() {
108 | return teamId;
109 | }
110 |
111 | public void setTeamId(Long teamId) {
112 | this.teamId = teamId;
113 | }
114 | }
115 |
--------------------------------------------------------------------------------
/src/main/java/org/catframework/agileworking/domain/MeetingRoomRepository.java:
--------------------------------------------------------------------------------
1 | package org.catframework.agileworking.domain;
2 |
3 | import java.util.List;
4 |
5 | import org.springframework.data.jpa.repository.JpaRepository;
6 |
7 | public interface MeetingRoomRepository extends JpaRepository {
8 |
9 | List findByTeamId(Long id);
10 | }
11 |
--------------------------------------------------------------------------------
/src/main/java/org/catframework/agileworking/domain/Participant.java:
--------------------------------------------------------------------------------
1 | package org.catframework.agileworking.domain;
2 |
3 | import java.io.Serializable;
4 | import java.util.Date;
5 |
6 | import javax.persistence.Entity;
7 | import javax.persistence.GeneratedValue;
8 | import javax.persistence.Id;
9 | import javax.persistence.JoinColumn;
10 | import javax.persistence.ManyToOne;
11 |
12 | import com.fasterxml.jackson.annotation.JsonFormat;
13 | import com.fasterxml.jackson.annotation.JsonIgnore;
14 |
15 | @Entity
16 | public class Participant implements Serializable {
17 |
18 | private static final long serialVersionUID = 1L;
19 |
20 | @Id
21 | @GeneratedValue
22 | private Long id;
23 |
24 | /** 排期 id. */
25 | @JsonIgnore
26 | @ManyToOne
27 | @JoinColumn(name = "schedule_id")
28 | private Schedule schedule;
29 |
30 | /** 参会人微信 openId. */
31 | private String openId;
32 |
33 | /** 参会人微信昵名. */
34 | private String nickName;
35 |
36 | /** 参会人微信头像的链接. */
37 | private String avatarUrl;
38 |
39 | /** 参会日期. */
40 | private Date date;
41 |
42 | /** 参加会议表格提交的 formId 用于模板消息通知. */
43 | private String formId;
44 |
45 | public Long getId() {
46 | return id;
47 | }
48 |
49 | public void setId(Long id) {
50 | this.id = id;
51 | }
52 |
53 | public Schedule getSchedule() {
54 | return schedule;
55 | }
56 |
57 | public void setSchedule(Schedule schedule) {
58 | this.schedule = schedule;
59 | }
60 |
61 | public String getNickName() {
62 | return nickName;
63 | }
64 |
65 | public void setNickName(String nickName) {
66 | this.nickName = nickName;
67 | }
68 |
69 | public String getAvatarUrl() {
70 | return avatarUrl;
71 | }
72 |
73 | public void setAvatarUrl(String avatarUrl) {
74 | this.avatarUrl = avatarUrl;
75 | }
76 |
77 | public String getOpenId() {
78 | return openId;
79 | }
80 |
81 | public void setOpenId(String openId) {
82 | this.openId = openId;
83 | }
84 |
85 | @JsonFormat(pattern = "yyyy-MM-dd")
86 | public Date getDate() {
87 | return date;
88 | }
89 |
90 | @JsonFormat(pattern = "yyyy-MM-dd")
91 | public void setDate(Date date) {
92 | this.date = date;
93 | }
94 |
95 | public String getFormId() {
96 | return formId;
97 | }
98 |
99 | public void setFormId(String formId) {
100 | this.formId = formId;
101 | }
102 | }
103 |
--------------------------------------------------------------------------------
/src/main/java/org/catframework/agileworking/domain/ParticipantRepository.java:
--------------------------------------------------------------------------------
1 | package org.catframework.agileworking.domain;
2 |
3 | import org.springframework.data.repository.PagingAndSortingRepository;
4 |
5 | public interface ParticipantRepository extends PagingAndSortingRepository {
6 |
7 | }
8 |
--------------------------------------------------------------------------------
/src/main/java/org/catframework/agileworking/domain/Schedule.java:
--------------------------------------------------------------------------------
1 | package org.catframework.agileworking.domain;
2 |
3 | import java.io.Serializable;
4 | import java.io.UnsupportedEncodingException;
5 | import java.net.URLDecoder;
6 | import java.net.URLEncoder;
7 | import java.util.ArrayList;
8 | import java.util.Date;
9 | import java.util.List;
10 |
11 | import javax.persistence.CascadeType;
12 | import javax.persistence.Column;
13 | import javax.persistence.Entity;
14 | import javax.persistence.FetchType;
15 | import javax.persistence.GeneratedValue;
16 | import javax.persistence.Id;
17 | import javax.persistence.ManyToOne;
18 | import javax.persistence.OneToMany;
19 |
20 | import org.slf4j.Logger;
21 | import org.slf4j.LoggerFactory;
22 |
23 | import com.fasterxml.jackson.annotation.JsonFormat;
24 |
25 | /**
26 | * 会议室排期.
27 | *
28 | * @author devzzm
29 | */
30 | @Entity
31 | public class Schedule implements Serializable, Comparable {
32 |
33 | private static final long serialVersionUID = 1L;
34 |
35 | private static final Logger logger = LoggerFactory.getLogger(Schedule.class);
36 |
37 | /** 排期的重复模式:不重复. */
38 | public static final String REPEAT_MODE_NO = "N";
39 |
40 | /** 排期的重复模式:按周. */
41 | public static final String REPEAT_MODE_WEEKLY = "W";
42 |
43 | @Id
44 | @GeneratedValue
45 | private Long id;
46 |
47 | /** 会议的标题. */
48 | @Column(nullable = false, length = 1024)
49 | private String title;
50 |
51 | /** 会议室房间编号. */
52 | @ManyToOne(fetch = FetchType.EAGER, optional = false)
53 | private MeetingRoom meetingRoom;
54 |
55 | /** 预订日期. */
56 | @Column(nullable = false)
57 | private Date date;
58 |
59 | /** 开始时间,格式为 'HH:mm:ss'. */
60 | @Column(nullable = false)
61 | private String startTime;
62 |
63 | /** 结束时间.格式为 'HH:mm:ss'. */
64 | @Column(nullable = false)
65 | private String endTime;
66 |
67 | /** 创建者的微信 openId */
68 | @Column(nullable = false)
69 | private String creatorOpenId;
70 |
71 | /** 创建者的微信昵称. */
72 | @Column(nullable = false)
73 | private String creatorNickName;
74 |
75 | /** 创建者的微信头像的链接. */
76 | @Column(nullable = false, length = 1024)
77 | private String creatorAvatarUrl;
78 |
79 | @Column(nullable = false)
80 | /** 会议重复的模式: N-不重复/W-每周. */
81 | private String repeatMode;
82 |
83 | @OneToMany(cascade = { CascadeType.ALL }, fetch = FetchType.EAGER, mappedBy = "schedule")
84 | private List participants = new ArrayList<>();
85 |
86 | public Long getId() {
87 | return id;
88 | }
89 |
90 | public void setId(Long id) {
91 | this.id = id;
92 | }
93 |
94 | public String getTitle() {
95 | return title;
96 | }
97 |
98 | public void setTitle(String title) {
99 | this.title = title;
100 | }
101 |
102 | public MeetingRoom getMeetingRoom() {
103 | return meetingRoom;
104 | }
105 |
106 | @JsonFormat(pattern = "yyyy-MM-dd")
107 | public Date getDate() {
108 | return date;
109 | }
110 |
111 | @JsonFormat(pattern = "yyyy-MM-dd")
112 | public void setDate(Date date) {
113 | this.date = date;
114 | }
115 |
116 | public String getStartTime() {
117 | return startTime;
118 | }
119 |
120 | public void setStartTime(String startTime) {
121 | this.startTime = startTime;
122 | }
123 |
124 | public String getEndTime() {
125 | return endTime;
126 | }
127 |
128 | public void setEndTime(String endTime) {
129 | this.endTime = endTime;
130 | }
131 |
132 | public String getCreatorNickName() {
133 | try {
134 | return null == creatorNickName ? null : URLDecoder.decode(this.creatorNickName, "utf-8");
135 | } catch (UnsupportedEncodingException e) {
136 | logger.warn("decode creatorNickName fail:", e);
137 | return this.creatorNickName;
138 | }
139 | }
140 |
141 | public void setCreatorNickName(String creatorNickName) {
142 | try {
143 | this.creatorNickName = URLEncoder.encode(creatorNickName, "utf-8");
144 | } catch (UnsupportedEncodingException e) {
145 | logger.warn("encode creatorNickName fail:", e);
146 | this.creatorNickName = creatorNickName;
147 | }
148 | }
149 |
150 | public String getCreatorAvatarUrl() {
151 | return creatorAvatarUrl;
152 | }
153 |
154 | public void setCreatorAvatarUrl(String creatorAvatarUrl) {
155 | this.creatorAvatarUrl = creatorAvatarUrl;
156 | }
157 |
158 | public String getRepeatMode() {
159 | return repeatMode;
160 | }
161 |
162 | public void setRepeatMode(String repeatMode) {
163 | this.repeatMode = repeatMode;
164 | }
165 |
166 | public List getParticipants() {
167 | return participants;
168 | }
169 |
170 | public void setParticipants(List participants) {
171 | this.participants = participants;
172 | }
173 |
174 | public void setMeetingRoom(MeetingRoom meetingRoom) {
175 | this.meetingRoom = meetingRoom;
176 | }
177 |
178 | public String getCreatorOpenId() {
179 | return creatorOpenId;
180 | }
181 |
182 | public void setCreatorOpenId(String creatorOpenId) {
183 | this.creatorOpenId = creatorOpenId;
184 | }
185 |
186 | public void addParticipant(Participant participant) {
187 | participant.setSchedule(this);
188 | this.getParticipants().add(participant);
189 | }
190 |
191 | /**
192 | * @return 当排期的重复模式为按周重复返回 true
193 | */
194 | public boolean isRepeatModeWeekly() {
195 | return Schedule.REPEAT_MODE_WEEKLY.equals(getRepeatMode());
196 | }
197 |
198 | /**
199 | * 判断当前排期和指定的排期是否有冲突.
200 | *
201 | * @param schedule
202 | * 指定用来判断是否冲突的排期
203 | * @return 有冲突返回 true
204 | */
205 | public boolean isConflict(Schedule schedule) {
206 |
207 | // 同一个排期肯定不算冲突
208 | if (getId().equals(schedule.getId())) {
209 | return false;
210 | }
211 |
212 | if (date.compareTo(schedule.getDate()) != 0) {
213 | return false;
214 | }
215 | if (startTime.compareTo(schedule.getStartTime()) >= 0 && startTime.compareTo(schedule.getEndTime()) < 0) {
216 | return true;
217 | }
218 | if (endTime.compareTo(schedule.getStartTime()) > 0 && endTime.compareTo(schedule.getEndTime()) <= 0) {
219 | return true;
220 | }
221 | if (startTime.compareTo(schedule.getStartTime()) < 0 && endTime.compareTo(schedule.getEndTime()) > 0) {
222 | return true;
223 | }
224 | return false;
225 | }
226 |
227 | @Override
228 | public int compareTo(Schedule o) {
229 | if (this.date.compareTo(o.getDate()) == 0) {
230 | return this.startTime.compareTo(o.getStartTime());
231 | } else {
232 | return this.date.compareTo(o.getDate());
233 | }
234 | }
235 | }
236 |
--------------------------------------------------------------------------------
/src/main/java/org/catframework/agileworking/domain/ScheduleRepository.java:
--------------------------------------------------------------------------------
1 | package org.catframework.agileworking.domain;
2 |
3 | import java.util.Date;
4 | import java.util.List;
5 |
6 | import org.springframework.data.jpa.repository.JpaRepository;
7 |
8 | public interface ScheduleRepository extends JpaRepository, ScheduleRepositoryCustom {
9 |
10 | List findByMeetingRoomAndDate(MeetingRoom meetingRoom, Date date);
11 |
12 | List findByMeetingRoomAndRepeatMode(MeetingRoom meetingRoom, String repeatMode);
13 | }
14 |
--------------------------------------------------------------------------------
/src/main/java/org/catframework/agileworking/domain/ScheduleRepositoryCustom.java:
--------------------------------------------------------------------------------
1 | package org.catframework.agileworking.domain;
2 |
3 | import java.util.Date;
4 | import java.util.List;
5 |
6 | import org.catframework.agileworking.vo.ScheduleVO;
7 |
8 | /**
9 | * 自定义 {@link ScheduleRepository} 接口
10 | *
11 | * @author devzzm
12 | */
13 | public interface ScheduleRepositoryCustom {
14 |
15 | /**
16 | * 查询指定日期指定 openId 的排期,使用了 native sql 来实现.
17 | *
18 | * @param openId 微信 openId
19 | * @param date 查询的日期
20 | * @return 符合条件的排期值对象
21 | */
22 | List findByOpenIdAndDate(String openId, Date date);
23 |
24 |
25 | /**
26 | * 查询指定日期所有的排期
27 | * @param date 查询的日期
28 | * @return 符合条件的排期值对象
29 | */
30 | List findByDate(Date date);
31 | }
32 |
--------------------------------------------------------------------------------
/src/main/java/org/catframework/agileworking/domain/ScheduleRepositoryImpl.java:
--------------------------------------------------------------------------------
1 | package org.catframework.agileworking.domain;
2 |
3 | import java.util.Date;
4 | import java.util.List;
5 | import java.util.stream.Collectors;
6 |
7 | import javax.persistence.EntityManager;
8 | import javax.transaction.Transactional;
9 |
10 | import org.catframework.agileworking.vo.ScheduleVO;
11 | import org.hibernate.SQLQuery;
12 | import org.hibernate.Session;
13 | import org.hibernate.transform.Transformers;
14 | import org.springframework.beans.factory.annotation.Autowired;
15 |
16 | public class ScheduleRepositoryImpl implements ScheduleRepositoryCustom {
17 |
18 | @Autowired
19 | private EntityManager entityManager;
20 |
21 | /**
22 | * 自定义 native query 查询 {@link ScheduleVO} ,略繁琐但是好像没有更好的办法.
23 | */
24 | @Transactional
25 | @Override
26 | public List findByOpenIdAndDate(String openId, Date date) {
27 | String sql = "select t.schedule_id as scheduleId ,t.date,t.meeting_room_id as meetingRoomId,t.title,t.open_id as openId,m.room_no as roomNo,t.start_time as startTime,t.end_time as endTime, t.repeat_mode as repeatMode from (select p.schedule_id,p.date,s.meeting_room_id,s.title,p.open_id,s.start_time,s.end_time,s.repeat_mode from participant p left join schedule s on p.schedule_id = s.id ) as t left join meeting_room m on t.meeting_room_id = m.id where (t.open_id=? and t.date=?) or (t.open_id=? and repeat_mode='W')";
28 | Session session = entityManager.unwrap(org.hibernate.Session.class);
29 | SQLQuery query = session.createSQLQuery(sql);
30 | @SuppressWarnings("unchecked")
31 | List scheduleVOs = query.setResultTransformer(Transformers.aliasToBean(ScheduleVO.class))
32 | .setParameter(0, openId).setParameter(1, date).setParameter(2, openId).list();
33 | return scheduleVOs.stream().filter(s -> s.isNeedInclude(date)).map(s -> {
34 | s.setDate(date);
35 | return s;
36 | }).sorted().collect(Collectors.toList());
37 | }
38 |
39 |
40 | @Transactional
41 | @Override
42 | public List findByDate(Date date) {
43 | String sql = "select t.schedule_id as scheduleId ,t.date,t.meeting_room_id as meetingRoomId,t.title,t.open_id as openId,m.room_no as roomNo,t.start_time as startTime,t.end_time as endTime, t.repeat_mode as repeatMode from (select p.schedule_id,p.date,s.meeting_room_id,s.title, s.creator_open_id open_id,s.start_time,s.end_time,s.repeat_mode from participant p left join schedule s on p.schedule_id = s.id ) as t left join meeting_room m on t.meeting_room_id = m.id where (t.date=?) or (repeat_mode='W')";
44 | Session session = entityManager.unwrap(org.hibernate.Session.class);
45 | SQLQuery query = session.createSQLQuery(sql);
46 | @SuppressWarnings("unchecked")
47 | List scheduleVOs = query.setResultTransformer(Transformers.aliasToBean(ScheduleVO.class))
48 | .setParameter(0, date).list();
49 | return scheduleVOs.stream().filter(s -> s.isNeedInclude(date)).map(s -> {
50 | s.setDate(date);
51 | return s;
52 | }).sorted().collect(Collectors.toList());
53 |
54 | }
55 |
56 | public void setEntityManager(EntityManager entityManager) {
57 | this.entityManager = entityManager;
58 | }
59 |
60 | }
61 |
--------------------------------------------------------------------------------
/src/main/java/org/catframework/agileworking/domain/Team.java:
--------------------------------------------------------------------------------
1 | package org.catframework.agileworking.domain;
2 |
3 | import java.io.Serializable;
4 | import java.util.ArrayList;
5 | import java.util.List;
6 |
7 | import javax.persistence.CascadeType;
8 | import javax.persistence.Column;
9 | import javax.persistence.Entity;
10 | import javax.persistence.FetchType;
11 | import javax.persistence.GeneratedValue;
12 | import javax.persistence.Id;
13 | import javax.persistence.JoinColumn;
14 | import javax.persistence.JoinTable;
15 | import javax.persistence.ManyToMany;
16 |
17 | import com.fasterxml.jackson.annotation.JsonIgnore;
18 |
19 | @Entity
20 | public class Team implements Serializable {
21 |
22 | private static final long serialVersionUID = 1L;
23 |
24 | @Id
25 | @GeneratedValue
26 | private Long id;
27 |
28 | @Column(nullable = false)
29 | private String name;
30 |
31 | // desc 和 mysql 中的关键字有冲突,所以使用了 'team_desc'
32 | @Column(name = "team_desc")
33 | private String desc;
34 |
35 | /** 用于注册校验的 token */
36 | private String token;
37 |
38 | @ManyToMany(cascade = { CascadeType.PERSIST}, fetch = FetchType.EAGER)
39 | @JoinTable(name = "team_user_mapping", joinColumns = {
40 | @JoinColumn(name = "team_id", referencedColumnName = "id") }, inverseJoinColumns = {
41 | @JoinColumn(name = "user_id", referencedColumnName = "id") })
42 | @JsonIgnore
43 | private List users = new ArrayList<>();
44 |
45 | public Long getId() {
46 | return id;
47 | }
48 |
49 | public void setId(Long id) {
50 | this.id = id;
51 | }
52 |
53 | public String getName() {
54 | return name;
55 | }
56 |
57 | public void setName(String name) {
58 | this.name = name;
59 | }
60 |
61 | public String getDesc() {
62 | return desc;
63 | }
64 |
65 | public void setDesc(String desc) {
66 | this.desc = desc;
67 | }
68 |
69 | public List getUsers() {
70 | return users;
71 | }
72 |
73 | public void setUsers(List users) {
74 | this.users = users;
75 | }
76 |
77 | public void addUser(User user) {
78 | this.users.add(user);
79 | }
80 |
81 | @JsonIgnore
82 | public String getToken() {
83 | return token;
84 | }
85 |
86 | public void setToken(String token) {
87 | this.token = token;
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/src/main/java/org/catframework/agileworking/domain/TeamRepository.java:
--------------------------------------------------------------------------------
1 | package org.catframework.agileworking.domain;
2 |
3 | import org.springframework.data.jpa.repository.JpaRepository;
4 |
5 | public interface TeamRepository extends JpaRepository {
6 |
7 | }
8 |
--------------------------------------------------------------------------------
/src/main/java/org/catframework/agileworking/domain/User.java:
--------------------------------------------------------------------------------
1 | package org.catframework.agileworking.domain;
2 |
3 | import java.io.Serializable;
4 | import java.util.List;
5 |
6 | import javax.persistence.Column;
7 | import javax.persistence.Entity;
8 | import javax.persistence.GeneratedValue;
9 | import javax.persistence.Id;
10 | import javax.persistence.ManyToMany;
11 |
12 | import com.fasterxml.jackson.annotation.JsonIgnore;
13 |
14 | @Entity
15 | public class User implements Serializable {
16 |
17 | private static final long serialVersionUID = -726326819568372268L;
18 |
19 | @Id
20 | @GeneratedValue
21 | private Long id;
22 |
23 | @Column(nullable = false, length = 200)
24 | private String name;
25 |
26 | @Column(nullable = false)
27 | private String mobileNo;
28 |
29 | @Column(nullable = false, unique = true)
30 | private String openId;
31 |
32 | @Column(nullable = false)
33 | private String nickName;
34 |
35 | @Column(nullable = false)
36 | private String avatarUrl;
37 |
38 | @ManyToMany(mappedBy="users")
39 | @JsonIgnore
40 | private List teams;
41 |
42 | public Long getId() {
43 | return id;
44 | }
45 |
46 | public void setId(Long id) {
47 | this.id = id;
48 | }
49 |
50 | public String getName() {
51 | return name;
52 | }
53 |
54 | public void setName(String name) {
55 | this.name = name;
56 | }
57 |
58 | public String getMobileNo() {
59 | return mobileNo;
60 | }
61 |
62 | public void setMobileNo(String mobileNo) {
63 | this.mobileNo = mobileNo;
64 | }
65 |
66 | public String getOpenId() {
67 | return openId;
68 | }
69 |
70 | public void setOpenId(String openId) {
71 | this.openId = openId;
72 | }
73 |
74 | public String getNickName() {
75 | return nickName;
76 | }
77 |
78 | public void setNickName(String nickName) {
79 | this.nickName = nickName;
80 | }
81 |
82 | public String getAvatarUrl() {
83 | return avatarUrl;
84 | }
85 |
86 | public void setAvatarUrl(String avatarUrl) {
87 | this.avatarUrl = avatarUrl;
88 | }
89 |
90 | public List getTeams() {
91 | return teams;
92 | }
93 |
94 | public void setTeams(List teams) {
95 | this.teams = teams;
96 | }
97 |
98 |
99 | }
100 |
--------------------------------------------------------------------------------
/src/main/java/org/catframework/agileworking/domain/UserRepository.java:
--------------------------------------------------------------------------------
1 | package org.catframework.agileworking.domain;
2 |
3 | import org.springframework.data.jpa.repository.JpaRepository;
4 |
5 | public interface UserRepository extends JpaRepository {
6 |
7 | User findOneByOpenId(String openId);
8 | }
9 |
--------------------------------------------------------------------------------
/src/main/java/org/catframework/agileworking/domain/package-info.java:
--------------------------------------------------------------------------------
1 | /**
2 | * 实体及领域模型
3 | * @author devzzm
4 | */
5 | package org.catframework.agileworking.domain;
--------------------------------------------------------------------------------
/src/main/java/org/catframework/agileworking/scheduling/SendNotifyMessageJob.java:
--------------------------------------------------------------------------------
1 | package org.catframework.agileworking.scheduling;
2 |
3 | import java.util.Date;
4 | import java.util.HashSet;
5 | import java.util.Set;
6 |
7 | import org.apache.commons.logging.Log;
8 | import org.apache.commons.logging.LogFactory;
9 | import org.catframework.agileworking.domain.Schedule;
10 | import org.catframework.agileworking.domain.ScheduleRepository;
11 | import org.catframework.agileworking.domain.User;
12 | import org.catframework.agileworking.domain.UserRepository;
13 | import org.catframework.agileworking.utils.DateUtils;
14 | import org.catframework.agileworking.utils.JsonUtils;
15 | import org.catframework.agileworking.vo.NotifyMessageTemplate;
16 | import org.catframework.agileworking.vo.ScheduleVO;
17 | import org.springframework.beans.factory.annotation.Autowired;
18 | import org.springframework.beans.factory.annotation.Value;
19 | import org.springframework.scheduling.annotation.Scheduled;
20 | import org.springframework.stereotype.Component;
21 | import org.springframework.web.client.RestTemplate;
22 |
23 | /**
24 | * 发送会议通知,每60秒轮训一次,在会前 15 分钟发送提醒,目前的实现比较简单仅在内存中增加了缓存来存放已发送的排期.
25 | *
26 | * 提示:目前版本只考虑了单 JVM 环境运行,作业调度未考虑并发的问题,如果运行在集群环境下,需要修改此类.
27 | *
28 | * @author devzzm
29 | */
30 | @Component
31 | public class SendNotifyMessageJob {
32 |
33 | private static final Log logger = LogFactory.getLog(SendNotifyMessageJob.class);
34 |
35 | @Value("${wechat.app-id}")
36 | private String appId;
37 |
38 | @Value("${wechat.app-secret}")
39 | private String appSecret;
40 |
41 | @Value("${wechat.notify.template-id}")
42 | private String templateId;
43 |
44 | @Value("${wecaht.acquire-access-token-url}")
45 | private String acquireAccessTokenUrl;
46 |
47 | @Value("${wecaht.send-messag-url}")
48 | private String sendMessageUrl;
49 |
50 | @Autowired
51 | private ScheduleRepository scheduleRepository;
52 |
53 | @Autowired
54 | private UserRepository userRepository;
55 |
56 | private RestTemplate restTemplate = new RestTemplate();
57 |
58 | private Set cache = new HashSet<>();
59 |
60 | private Date date = DateUtils.parse(DateUtils.format(new Date(), DateUtils.PATTERN_SIMPLE_DATE),
61 | DateUtils.PATTERN_SIMPLE_DATE);
62 |
63 | @Scheduled(fixedDelay = 60 * 1000)
64 | public void execute() {
65 | Date date = DateUtils.parse(DateUtils.format(new Date(), DateUtils.PATTERN_SIMPLE_DATE),
66 | DateUtils.PATTERN_SIMPLE_DATE);
67 | if (date.compareTo(this.date) > 0) {
68 | this.date = date;
69 | cache.clear();
70 | }
71 |
72 | String accessToken = getAccessToken();
73 | scheduleRepository.findByDate(date).forEach(s -> {
74 | if (cache.contains(new Long(s.getScheduleId().intValue()))) {
75 | return;
76 | }
77 | if (isNeedSendMessageNow(s)) {
78 | doSend(accessToken, s);
79 | cache.add(new Long(s.getScheduleId().intValue()));
80 | }
81 | });
82 | }
83 |
84 | private void doSend(String accessToken, ScheduleVO s) {
85 | Schedule schedule = scheduleRepository.findOne(new Long(s.getScheduleId().intValue()));
86 | User creator = userRepository.findOneByOpenId(s.getOpenId());
87 | schedule.getParticipants().stream().forEach(p -> {
88 | NotifyMessageTemplate template = new NotifyMessageTemplate(creator, schedule, p, templateId);
89 | String result = restTemplate.postForObject(sendMessageUrl, template.toTemplateMessage(), String.class,
90 | accessToken);
91 | logger.info("send notify message result is :" + result);
92 | });
93 | }
94 |
95 | /* 会议开始前15分钟开始发送通知. */
96 | private boolean isNeedSendMessageNow(ScheduleVO scheduleVO) {
97 | long now = System.currentTimeMillis();
98 | return (now + 15 * 60 * 1000) > DateUtils.parse(
99 | DateUtils.format(scheduleVO.getDate(), DateUtils.PATTERN_SIMPLE_DATE) + " " + scheduleVO.getStartTime(),
100 | DateUtils.PATTERN_SIMPLE_DATE + " HH:mm").getTime();
101 | }
102 |
103 | private String getAccessToken() {
104 | String result = restTemplate.getForObject(acquireAccessTokenUrl, String.class, appId, appSecret);
105 | return (String) JsonUtils.decode(result).get("access_token");
106 |
107 | }
108 |
109 | public void setAppId(String appId) {
110 | this.appId = appId;
111 | }
112 |
113 | public void setAppSecret(String appSecret) {
114 | this.appSecret = appSecret;
115 | }
116 |
117 | public void setTemplateId(String templateId) {
118 | this.templateId = templateId;
119 | }
120 |
121 | public void setScheduleRepository(ScheduleRepository scheduleRepository) {
122 | this.scheduleRepository = scheduleRepository;
123 | }
124 |
125 | public void setUserRepository(UserRepository userRepository) {
126 | this.userRepository = userRepository;
127 | }
128 |
129 | public void setRestTemplate(RestTemplate restTemplate) {
130 | this.restTemplate = restTemplate;
131 | }
132 | }
133 |
--------------------------------------------------------------------------------
/src/main/java/org/catframework/agileworking/service/ScheduleService.java:
--------------------------------------------------------------------------------
1 | package org.catframework.agileworking.service;
2 |
3 | import java.util.Date;
4 | import java.util.List;
5 |
6 | import org.catframework.agileworking.domain.Schedule;
7 |
8 | public interface ScheduleService {
9 |
10 | /**
11 | * 查找指定会议室指定日期区间的排期,会自动对按周重复的排期进行计算.
12 | *
13 | * @param meetingRoomId 会议室 id
14 | * @param date 指定日期
15 | * @return 符合条件的排期列表
16 | */
17 | List find(Long meetingRoomId, Date date);
18 | }
19 |
--------------------------------------------------------------------------------
/src/main/java/org/catframework/agileworking/service/WebTokenService.java:
--------------------------------------------------------------------------------
1 | package org.catframework.agileworking.service;
2 |
3 | /**
4 | * 小程序的会话使用 WebToken 的方式跟踪,非使用传统的 session ,此服务提供 WebToken 相关的功能.
5 | *
6 | * @author devzzm
7 | */
8 | public interface WebTokenService {
9 |
10 | /**
11 | * 使用指定的主题生成 token
12 | *
13 | * @param subject 指定的信息
14 | * @return 生成的 token
15 | */
16 | String generate(String subject);
17 |
18 | /**
19 | * 校验指定的主题的 token 是否福匹配
20 | *
21 | * @param token 被校验的 token
22 | * @return 当校验成功时返回 true
23 | */
24 | boolean verify(String subject, String token);
25 | }
26 |
--------------------------------------------------------------------------------
/src/main/java/org/catframework/agileworking/service/impl/ScheduleServiceImpl.java:
--------------------------------------------------------------------------------
1 | package org.catframework.agileworking.service.impl;
2 |
3 | import java.util.Date;
4 | import java.util.List;
5 | import java.util.stream.Collectors;
6 |
7 | import org.catframework.agileworking.domain.MeetingRoom;
8 | import org.catframework.agileworking.domain.MeetingRoomRepository;
9 | import org.catframework.agileworking.domain.Schedule;
10 | import org.catframework.agileworking.domain.ScheduleRepository;
11 | import org.catframework.agileworking.service.ScheduleService;
12 | import org.catframework.agileworking.utils.DateUtils;
13 | import org.springframework.beans.factory.annotation.Autowired;
14 | import org.springframework.stereotype.Component;
15 |
16 | @Component
17 | public class ScheduleServiceImpl implements ScheduleService {
18 |
19 | @Autowired
20 | private ScheduleRepository scheduleRepository;
21 |
22 | @Autowired
23 | private MeetingRoomRepository meetingRoomRepository;
24 |
25 | @Override
26 | public List find(Long meetingRoomId, Date date) {
27 | MeetingRoom meetingRoom = meetingRoomRepository.findOne(meetingRoomId);
28 | List schedules = scheduleRepository.findByMeetingRoomAndDate(meetingRoom, date);
29 | List weeklySchedules = scheduleRepository.findByMeetingRoomAndRepeatMode(meetingRoom,
30 | Schedule.REPEAT_MODE_WEEKLY);
31 | weeklySchedules.stream().forEach((s1) -> {
32 | if (schedules.stream().noneMatch((s2) -> s1.getId().equals(s2.getId()))) {
33 | if (s1.getDate().compareTo(date) < 0) {
34 | if (DateUtils.isSameWeekOfday(s1.getDate(), date)) {
35 | s1.setDate(date);
36 | schedules.add(s1);
37 | }
38 | }
39 | }
40 | });
41 | // 按照开始时间进行排序
42 | return schedules.stream().sorted((s1, s2) -> s1.getStartTime().compareTo(s2.getStartTime()))
43 | .collect(Collectors.toList());
44 | }
45 |
46 | public void setScheduleRepository(ScheduleRepository scheduleRepository) {
47 | this.scheduleRepository = scheduleRepository;
48 | }
49 |
50 | public void setMeetingRoomRepository(MeetingRoomRepository meetingRoomRepository) {
51 | this.meetingRoomRepository = meetingRoomRepository;
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/src/main/java/org/catframework/agileworking/service/impl/SimpleJJWTWebTokenServiceImpl.java:
--------------------------------------------------------------------------------
1 | package org.catframework.agileworking.service.impl;
2 |
3 | import java.security.Key;
4 |
5 | import org.apache.commons.logging.Log;
6 | import org.apache.commons.logging.LogFactory;
7 | import org.catframework.agileworking.service.WebTokenService;
8 | import org.springframework.stereotype.Component;
9 |
10 | import io.jsonwebtoken.Jwts;
11 | import io.jsonwebtoken.SignatureAlgorithm;
12 | import io.jsonwebtoken.impl.crypto.MacProvider;
13 |
14 | /**
15 | * 使用 JJWT 做为 WebToken 的实现,当前的实现每次服务启动都生成一个新的 key,且不支持集群环境下使用,后续考虑配置文件或者数据库的方式.
16 | *
17 | * @author devzzm
18 | */
19 | @Component
20 | public class SimpleJJWTWebTokenServiceImpl implements WebTokenService {
21 |
22 | private static final Log logger = LogFactory.getLog(SimpleJJWTWebTokenServiceImpl.class);
23 |
24 | private Key key = MacProvider.generateKey();
25 |
26 | @Override
27 | public String generate(String subject) {
28 | return Jwts.builder().setSubject(subject).signWith(SignatureAlgorithm.HS512, key).compact();
29 | }
30 |
31 | @Override
32 | public boolean verify(String subject, String token) {
33 | try {
34 | return Jwts.parser().setSigningKey(key).parseClaimsJws(token).getBody().getSubject().equals(subject);
35 | } catch (Exception e) {
36 | logger.error("Verify fail:", e);
37 | return false;
38 | }
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/src/main/java/org/catframework/agileworking/service/package-info.java:
--------------------------------------------------------------------------------
1 | /**
2 | * 服务层
3 | * @author devzzm
4 | *
5 | */
6 | package org.catframework.agileworking.service;
--------------------------------------------------------------------------------
/src/main/java/org/catframework/agileworking/utils/DateUtils.java:
--------------------------------------------------------------------------------
1 | package org.catframework.agileworking.utils;
2 |
3 | import java.text.ParseException;
4 | import java.text.SimpleDateFormat;
5 | import java.util.Calendar;
6 | import java.util.Date;
7 |
8 | /**
9 | * {@link Date} 解析、格式化的脚手架构工具类.
10 | *
11 | * @author devzzm
12 | *
13 | */
14 | public final class DateUtils {
15 |
16 | /** 简单的日期格式: yyyy-MM-dd */
17 | public static final String PATTERN_SIMPLE_DATE = "yyyy-MM-dd";
18 |
19 | public static final Date parse(String source, String pattern) {
20 | try {
21 | return new SimpleDateFormat(pattern).parse(source);
22 | } catch (ParseException e) {
23 | throw new RuntimeException(e);
24 | }
25 | }
26 |
27 | public static final String format(Date date, String pattern) {
28 | return new SimpleDateFormat(pattern).format(date);
29 | }
30 |
31 | /**
32 | * 判断指定的两个日期的所属的星期是否相同.
33 | *
34 | * @return 当两个日期相同时返回 true
35 | */
36 | public static boolean isSameWeekOfday(Date d1, Date d2) {
37 | Calendar d1Cal = Calendar.getInstance();
38 | d1Cal.setTime(d1);
39 | Calendar d2Cal = Calendar.getInstance();
40 | d2Cal.setTime(d2);
41 | return d1Cal.get(Calendar.DAY_OF_WEEK) == d2Cal.get(Calendar.DAY_OF_WEEK);
42 | }
43 |
44 | }
45 |
--------------------------------------------------------------------------------
/src/main/java/org/catframework/agileworking/utils/JsonUtils.java:
--------------------------------------------------------------------------------
1 | package org.catframework.agileworking.utils;
2 |
3 | import java.io.IOException;
4 | import java.util.Map;
5 |
6 | import com.fasterxml.jackson.databind.ObjectMapper;
7 |
8 | public final class JsonUtils {
9 |
10 | @SuppressWarnings("unchecked")
11 | public static final Map decode(String jsonString) {
12 | ObjectMapper mapper = new ObjectMapper();
13 | try {
14 | return mapper.readValue(jsonString, Map.class);
15 | } catch (IOException e) {
16 | throw new RuntimeException("JSON解析失败", e);
17 | }
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/main/java/org/catframework/agileworking/vo/NotifyMessageTemplate.java:
--------------------------------------------------------------------------------
1 | package org.catframework.agileworking.vo;
2 |
3 | import java.util.HashMap;
4 | import java.util.Map;
5 |
6 | import org.catframework.agileworking.domain.Participant;
7 | import org.catframework.agileworking.domain.Schedule;
8 | import org.catframework.agileworking.domain.User;
9 | import org.catframework.agileworking.utils.DateUtils;
10 |
11 | public class NotifyMessageTemplate {
12 |
13 | private String openId;
14 |
15 | private String templateId;
16 |
17 | private String formId;
18 |
19 | private String title;
20 |
21 | private String roomNo;
22 |
23 | private String time;
24 |
25 | private String participantInfo;
26 |
27 | private String creator;
28 |
29 | public NotifyMessageTemplate(User creator, Schedule schedule, Participant participant, String templateId) {
30 | this.openId = participant.getOpenId();
31 | this.templateId = templateId;
32 | this.formId = participant.getFormId();
33 | this.title = schedule.getTitle();
34 | this.roomNo = schedule.getMeetingRoom().getRoomNo();
35 | this.time = (DateUtils.format(schedule.getDate(), "yyyy年MM月dd日") + " " + schedule.getStartTime() + "-"
36 | + schedule.getEndTime());
37 | this.participantInfo = schedule.getParticipants().size() + "人参加";
38 | this.creator = creator.getNickName() + "/" + creator.getName()+"("+creator.getMobileNo()+")";
39 | }
40 |
41 | public String getOpenId() {
42 | return openId;
43 | }
44 |
45 | public String getTemplateId() {
46 | return templateId;
47 | }
48 |
49 | public String getFormId() {
50 | return formId;
51 | }
52 |
53 | public String getTitle() {
54 | return title;
55 | }
56 |
57 | public String getRoomNo() {
58 | return roomNo;
59 | }
60 |
61 | public String getTime() {
62 | return time;
63 | }
64 |
65 | public String getParticipantInfo() {
66 | return participantInfo;
67 | }
68 |
69 | public String getCreator() {
70 | return creator;
71 | }
72 |
73 | /**
74 | * 按照微信小程序模板消息的数据标准进行数据转换.
75 | *
76 | * @return 微信小程序格式的模板消息
77 | */
78 | public Map toTemplateMessage() {
79 | Map message = new HashMap<>();
80 | message.put("touser", this.openId);
81 | message.put("template_id", this.templateId);
82 | message.put("form_id", this.formId);
83 | Map data = new HashMap<>();
84 | Map keyword1 = new HashMap<>();
85 | keyword1.put("value", this.title);
86 | data.put("keyword1", keyword1);
87 | Map keyword2 = new HashMap<>();
88 | keyword2.put("value", this.time);
89 | data.put("keyword2", keyword2);
90 |
91 | Map keyword3 = new HashMap<>();
92 | keyword3.put("value", this.roomNo);
93 | data.put("keyword3", keyword3);
94 |
95 | Map keyword4 = new HashMap<>();
96 | keyword4.put("value", this.participantInfo);
97 | data.put("keyword4", keyword4);
98 |
99 | Map keyword5 = new HashMap<>();
100 | keyword5.put("value", this.creator);
101 | data.put("keyword5", keyword5);
102 | message.put("data", data);
103 | return message;
104 | }
105 | }
106 |
--------------------------------------------------------------------------------
/src/main/java/org/catframework/agileworking/vo/ScheduleVO.java:
--------------------------------------------------------------------------------
1 | package org.catframework.agileworking.vo;
2 |
3 | import java.math.BigInteger;
4 | import java.util.Date;
5 |
6 | import org.catframework.agileworking.domain.Schedule;
7 | import org.catframework.agileworking.domain.ScheduleRepository;
8 | import org.catframework.agileworking.utils.DateUtils;
9 |
10 | import com.fasterxml.jackson.annotation.JsonFormat;
11 |
12 | /**
13 | * 排期 {@link Schedule} 的值对象聚合了排期及会议室相关的信息.
14 | *
15 | * @author devzzm
16 | * @see ScheduleRepository#findByOpenIdAndDate(String, Date)
17 | */
18 | public class ScheduleVO implements Comparable {
19 |
20 | private BigInteger scheduleId;
21 |
22 | private Date date;
23 |
24 | private BigInteger meetingRoomId;
25 |
26 | private String title;
27 |
28 | private String openId;
29 |
30 | private String roomNo;
31 |
32 | private String startTime;
33 |
34 | private String endTime;
35 |
36 | private String repeatMode;
37 |
38 | public BigInteger getScheduleId() {
39 | return scheduleId;
40 | }
41 |
42 | public void setScheduleId(BigInteger scheduleId) {
43 | this.scheduleId = scheduleId;
44 | }
45 |
46 | @JsonFormat(pattern = "yyyy-MM-dd")
47 | public Date getDate() {
48 | return date;
49 | }
50 |
51 | public void setDate(Date date) {
52 | this.date = date;
53 | }
54 |
55 | public BigInteger getMeetingRoomId() {
56 | return meetingRoomId;
57 | }
58 |
59 | public void setMeetingRoomId(BigInteger meetingRoomId) {
60 | this.meetingRoomId = meetingRoomId;
61 | }
62 |
63 | public String getTitle() {
64 | return title;
65 | }
66 |
67 | public void setTitle(String title) {
68 | this.title = title;
69 | }
70 |
71 | public String getOpenId() {
72 | return openId;
73 | }
74 |
75 | public void setOpenId(String openId) {
76 | this.openId = openId;
77 | }
78 |
79 | public String getRoomNo() {
80 | return roomNo;
81 | }
82 |
83 | public void setRoomNo(String roomNo) {
84 | this.roomNo = roomNo;
85 | }
86 |
87 | public String getStartTime() {
88 | return startTime;
89 | }
90 |
91 | public void setStartTime(String startTime) {
92 | this.startTime = startTime;
93 | }
94 |
95 | public String getEndTime() {
96 | return endTime;
97 | }
98 |
99 | public void setEndTime(String endTime) {
100 | this.endTime = endTime;
101 | }
102 |
103 | public String getRepeatMode() {
104 | return repeatMode;
105 | }
106 |
107 | public void setRepeatMode(String repeatMode) {
108 | this.repeatMode = repeatMode;
109 | }
110 |
111 | /**
112 | * 判断当前的排期值对象是否同指定的日期具有相同的星期属性且排期日期小于指定的日期.
113 | * 此方法用于查询本人某一日的排期清单使用.
114 | *
115 | * @param date 指定日期
116 | * @return 当排期的日期同指定的日期的星期属性相同且日期小于指定的日期时返回 true
117 | */
118 | public boolean isNeedInclude(Date date) {
119 | if (getDate().equals(date)) {
120 | return true;
121 | }
122 | // 排除未来的排期
123 | if (getDate().compareTo(date) > 0) {
124 | return false;
125 | }
126 | return DateUtils.isSameWeekOfday(date, getDate());
127 | }
128 |
129 | @Override
130 | public int compareTo(ScheduleVO o) {
131 | int result = getStartTime().compareTo(o.getStartTime());
132 | if (result == 0) {
133 | return getRoomNo().compareTo(o.getRoomNo());
134 | }
135 | return result;
136 | }
137 | }
138 |
--------------------------------------------------------------------------------
/src/main/java/org/catframework/agileworking/web/MeetingRoomController.java:
--------------------------------------------------------------------------------
1 | package org.catframework.agileworking.web;
2 |
3 | import java.util.Date;
4 | import java.util.List;
5 | import java.util.stream.Collectors;
6 |
7 | import org.catframework.agileworking.domain.MeetingRoom;
8 | import org.catframework.agileworking.domain.MeetingRoomRepository;
9 | import org.catframework.agileworking.domain.Participant;
10 | import org.catframework.agileworking.domain.Schedule;
11 | import org.catframework.agileworking.domain.ScheduleRepository;
12 | import org.catframework.agileworking.domain.User;
13 | import org.catframework.agileworking.domain.UserRepository;
14 | import org.catframework.agileworking.service.ScheduleService;
15 | import org.catframework.agileworking.utils.DateUtils;
16 | import org.catframework.agileworking.web.support.DefaultResult;
17 | import org.catframework.agileworking.web.support.Result;
18 | import org.springframework.beans.factory.annotation.Autowired;
19 | import org.springframework.format.annotation.DateTimeFormat;
20 | import org.springframework.util.Assert;
21 | import org.springframework.web.bind.annotation.PathVariable;
22 | import org.springframework.web.bind.annotation.RequestBody;
23 | import org.springframework.web.bind.annotation.RequestMapping;
24 | import org.springframework.web.bind.annotation.RequestMethod;
25 | import org.springframework.web.bind.annotation.RequestParam;
26 | import org.springframework.web.bind.annotation.RestController;
27 |
28 | @RestController
29 | public class MeetingRoomController {
30 |
31 | @Autowired
32 | private MeetingRoomRepository meetingRoomRepository;
33 |
34 | @Autowired
35 | private ScheduleRepository scheduleRepository;
36 |
37 | @Autowired
38 | private ScheduleService scheduleService;
39 |
40 | @Autowired
41 | private UserRepository userRepository;
42 |
43 | @RequestMapping(path = "/meetingRooms/{id}", method = RequestMethod.GET)
44 | public Result> list(@PathVariable Long id) {
45 | return DefaultResult.newResult(meetingRoomRepository.findByTeamId(id));
46 | }
47 |
48 | /**
49 | * 创新排期,单 JVM 并发量可期的情况下,简单粗暴的使用 synchronized 来解决并发创建、更新排期的问题.
50 | *
51 | * @param id 会议室 id
52 | * @param schedule 新建的排期
53 | */
54 | @RequestMapping(path = "/meetingRooms/{id}/schedule", method = RequestMethod.POST)
55 | public synchronized Result createOrUpdateSchedule(@PathVariable(name = "id") Long id,
56 | @RequestParam(name = "formId", required = false) String formId, @RequestBody Schedule schedule) {
57 | MeetingRoom meetingRoom = meetingRoomRepository.findOne(id);
58 | validate(id, schedule);
59 | if (null == schedule.getId()) {
60 | schedule.setMeetingRoom(meetingRoom);
61 | schedule.addParticipant(creatorAsParticipant(schedule, formId));
62 | scheduleRepository.save(schedule);
63 | } else {
64 | Schedule s = scheduleRepository.findOne(schedule.getId());
65 | Assert.notNull(s, "修改的排期不存在.");
66 | s.setTitle(schedule.getTitle());
67 | s.setStartTime(schedule.getStartTime());
68 | s.setEndTime(schedule.getEndTime());
69 | s.setDate(schedule.getDate());
70 | s.setRepeatMode(schedule.getRepeatMode());
71 | scheduleRepository.save(s);
72 | }
73 | return DefaultResult.newResult(schedule);
74 | }
75 |
76 | private Participant creatorAsParticipant(Schedule schedule, String formId) {
77 | Participant p = new Participant();
78 | p.setAvatarUrl(schedule.getCreatorAvatarUrl());
79 | p.setDate(schedule.getDate());
80 | p.setNickName(schedule.getCreatorNickName());
81 | p.setSchedule(schedule);
82 | p.setOpenId(schedule.getCreatorOpenId());
83 | p.setFormId(formId);
84 | return p;
85 | }
86 |
87 | /**
88 | * 取消已设置的排期.
89 | *
90 | * @param id 排期 id
91 | */
92 | @RequestMapping(path = "/meetingRooms/schedule/{id}", method = RequestMethod.DELETE)
93 | public Result> cancelSchedule(@PathVariable Long id) {
94 | Schedule schedule = scheduleRepository.findOne(id);
95 | scheduleRepository.delete(schedule);
96 | return DefaultResult.newResult();
97 | }
98 |
99 | /**
100 | * 查询指定会议室下指定日期区间的排期.
101 | *
102 | * @param id 会议室 id
103 | * @param date 指定日期
104 | * @return 指定的会议室指定日期的排期列表
105 | */
106 | @RequestMapping(path = "/meetingRooms/{id}/schedule", method = RequestMethod.GET)
107 | public Result> schedules(@PathVariable Long id,
108 | @RequestParam(name = "date") @DateTimeFormat(pattern = DateUtils.PATTERN_SIMPLE_DATE) Date date) {
109 | return DefaultResult.newResult(scheduleService.find(id, date).stream().map(s -> {
110 | User user = userRepository.findOneByOpenId(s.getCreatorOpenId());
111 | s.setCreatorNickName(s.getCreatorNickName() + "/" + user.getName());
112 | return s;
113 | }).collect(Collectors.toList()));
114 | }
115 |
116 | private void validate(Long id, Schedule schedule) {
117 | Assert.isTrue(schedule.getStartTime().compareTo(schedule.getEndTime()) < 0, "会议开始时间需小于结束时间.");
118 | Assert.isTrue(!scheduleService.find(id, schedule.getDate()).stream().anyMatch(s -> s.isConflict(schedule)),
119 | "同已有排期冲突.");
120 | }
121 |
122 | public void setMeetingRoomRepository(MeetingRoomRepository meetingRoomRepository) {
123 | this.meetingRoomRepository = meetingRoomRepository;
124 | }
125 |
126 | public void setScheduleRepository(ScheduleRepository scheduleRepository) {
127 | this.scheduleRepository = scheduleRepository;
128 | }
129 |
130 | public void setScheduleService(ScheduleService scheduleService) {
131 | this.scheduleService = scheduleService;
132 | }
133 |
134 | public void setUserRepository(UserRepository userRepository) {
135 | this.userRepository = userRepository;
136 | }
137 |
138 | }
139 |
--------------------------------------------------------------------------------
/src/main/java/org/catframework/agileworking/web/ParticipantController.java:
--------------------------------------------------------------------------------
1 | package org.catframework.agileworking.web;
2 |
3 | import java.util.Date;
4 | import java.util.List;
5 |
6 | import org.catframework.agileworking.domain.ScheduleRepository;
7 | import org.catframework.agileworking.utils.DateUtils;
8 | import org.catframework.agileworking.vo.ScheduleVO;
9 | import org.catframework.agileworking.web.support.DefaultResult;
10 | import org.catframework.agileworking.web.support.Result;
11 | import org.springframework.beans.factory.annotation.Autowired;
12 | import org.springframework.format.annotation.DateTimeFormat;
13 | import org.springframework.web.bind.annotation.PathVariable;
14 | import org.springframework.web.bind.annotation.RequestMapping;
15 | import org.springframework.web.bind.annotation.RequestMethod;
16 | import org.springframework.web.bind.annotation.RequestParam;
17 | import org.springframework.web.bind.annotation.RestController;
18 |
19 | @RestController
20 | public class ParticipantController {
21 |
22 | @Autowired
23 | private ScheduleRepository scheduleRepository;
24 |
25 | @RequestMapping(path = "/participant/{openId}", method = RequestMethod.GET)
26 | public Result> participants(@PathVariable String openId,
27 | @RequestParam(name = "date") @DateTimeFormat(pattern = DateUtils.PATTERN_SIMPLE_DATE) Date date) {
28 | return DefaultResult.newResult(scheduleRepository.findByOpenIdAndDate(openId, date));
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src/main/java/org/catframework/agileworking/web/ScheduleController.java:
--------------------------------------------------------------------------------
1 | package org.catframework.agileworking.web;
2 |
3 | import java.util.Optional;
4 |
5 | import org.catframework.agileworking.domain.MeetingRoomRepository;
6 | import org.catframework.agileworking.domain.Participant;
7 | import org.catframework.agileworking.domain.Schedule;
8 | import org.catframework.agileworking.domain.ScheduleRepository;
9 | import org.catframework.agileworking.domain.TeamRepository;
10 | import org.catframework.agileworking.domain.User;
11 | import org.catframework.agileworking.domain.UserRepository;
12 | import org.catframework.agileworking.web.support.DefaultResult;
13 | import org.catframework.agileworking.web.support.Result;
14 | import org.springframework.beans.factory.annotation.Autowired;
15 | import org.springframework.util.Assert;
16 | import org.springframework.web.bind.annotation.PathVariable;
17 | import org.springframework.web.bind.annotation.RequestBody;
18 | import org.springframework.web.bind.annotation.RequestMapping;
19 | import org.springframework.web.bind.annotation.RequestMethod;
20 | import org.springframework.web.bind.annotation.RestController;
21 |
22 | @RestController
23 | public class ScheduleController {
24 |
25 | @Autowired
26 | private ScheduleRepository scheduleRepository;
27 |
28 | @Autowired
29 | private MeetingRoomRepository meetingRoomRepository;
30 |
31 | @Autowired
32 | private TeamRepository teamRepository;
33 |
34 | @Autowired
35 | private UserRepository userRepository;
36 |
37 | @RequestMapping(path = "/schedules/{id}/join", method = RequestMethod.POST)
38 | public Result join(@PathVariable Long id, @RequestBody Participant participant) {
39 | Schedule schedule = scheduleRepository.findOne(id);
40 | // 根据 MeetingRoom 找到 Team 找到其下的 Users 判断是否当前用户已加入其中
41 | Optional user = teamRepository
42 | .findOne(meetingRoomRepository.findOne(schedule.getMeetingRoom().getId()).getTeamId()).getUsers()
43 | .stream().filter(s -> s.getOpenId().equals(participant.getOpenId())).findAny();
44 | Assert.isTrue(user.isPresent(), "用户未绑定.");
45 | if (!schedule.getParticipants().stream().anyMatch((p) -> {
46 | return p.getOpenId().equals(participant.getOpenId());
47 | })) {
48 | schedule.addParticipant(participant);
49 | scheduleRepository.save(schedule);
50 | return DefaultResult.newResult(schedule);
51 | } else {
52 | throw new RuntimeException("您已加入过此会议啦.");
53 | }
54 | }
55 |
56 | @RequestMapping(path = "/schedules/{id}", method = RequestMethod.GET)
57 | public Result get(@PathVariable Long id) {
58 | Schedule schedule = scheduleRepository.findOne(id);
59 | User user = userRepository.findOneByOpenId(schedule.getCreatorOpenId());
60 | schedule.setCreatorNickName(schedule.getCreatorNickName()+"/"+user.getName());
61 | return DefaultResult.newResult(schedule);
62 | }
63 |
64 | public void setScheduleRepository(ScheduleRepository scheduleRepository) {
65 | this.scheduleRepository = scheduleRepository;
66 | }
67 |
68 | public void setMeetingRoomRepository(MeetingRoomRepository meetingRoomRepository) {
69 | this.meetingRoomRepository = meetingRoomRepository;
70 | }
71 |
72 | public void setTeamRepository(TeamRepository teamRepository) {
73 | this.teamRepository = teamRepository;
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/src/main/java/org/catframework/agileworking/web/TeamController.java:
--------------------------------------------------------------------------------
1 | package org.catframework.agileworking.web;
2 |
3 | import java.util.List;
4 | import java.util.Optional;
5 |
6 | import org.catframework.agileworking.domain.Team;
7 | import org.catframework.agileworking.domain.TeamRepository;
8 | import org.catframework.agileworking.domain.User;
9 | import org.catframework.agileworking.domain.UserRepository;
10 | import org.catframework.agileworking.service.WebTokenService;
11 | import org.catframework.agileworking.web.support.DefaultResult;
12 | import org.catframework.agileworking.web.support.Result;
13 | import org.springframework.beans.factory.annotation.Autowired;
14 | import org.springframework.util.Assert;
15 | import org.springframework.web.bind.annotation.PathVariable;
16 | import org.springframework.web.bind.annotation.RequestBody;
17 | import org.springframework.web.bind.annotation.RequestMapping;
18 | import org.springframework.web.bind.annotation.RequestMethod;
19 | import org.springframework.web.bind.annotation.RequestParam;
20 | import org.springframework.web.bind.annotation.RestController;
21 |
22 | @RestController
23 | public class TeamController {
24 |
25 | @Autowired
26 | private TeamRepository teamRepository;
27 |
28 | @Autowired
29 | private UserRepository userRepository;
30 |
31 | @Autowired
32 | private WebTokenService webTokenService;
33 |
34 | // 加入团队
35 | @RequestMapping(path = "/team/{id}/join", method = RequestMethod.POST)
36 | public Result join(@PathVariable Long id, @RequestBody User user,
37 | @RequestParam(name = "token") String token) {
38 | Team team = teamRepository.findOne(id);
39 | Assert.notNull(team, "团队不存在.");
40 | Assert.isTrue(team.getToken().equals(token), "口令校验失败.");
41 | if (user.getId() == null) {
42 | userRepository.save(user);
43 | }
44 | if (team.getUsers().stream().noneMatch(u -> user.getId().equals(u.getId()))) {
45 | team.addUser(user);
46 | teamRepository.save(team);
47 | return DefaultResult.newResult(user).setHeader("token", webTokenService.generate(user.getOpenId()));
48 | } else {
49 | throw new RuntimeException("不可重复加入团队.");
50 | }
51 |
52 | }
53 |
54 | // 查询所有的团队
55 | @RequestMapping(path = "/teams", method = RequestMethod.GET)
56 | public Result> findAll() {
57 | return DefaultResult.newResult(teamRepository.findAll());
58 | }
59 |
60 | // 根据团队 id 及 微信 openId 查询绑定的用户
61 | @RequestMapping(path = "/team/{teamId}/user/{openId}", method = RequestMethod.GET)
62 | public Result getUser(@PathVariable(name = "teamId") Long teamId,
63 | @PathVariable(name = "openId") String openId) {
64 | Team team = teamRepository.findOne(teamId);
65 | Optional optional = team.getUsers().stream().filter(s -> s.getOpenId().equals(openId)).findAny();
66 | if (optional.isPresent()) {
67 | String token = webTokenService.generate(optional.get().getOpenId());
68 | return DefaultResult.newResult(optional.get()).setHeader("token", token);
69 | } else {
70 | return DefaultResult.newFailResult("用户未绑定.");
71 | }
72 | }
73 |
74 | public void setTeamRepository(TeamRepository teamRepository) {
75 | this.teamRepository = teamRepository;
76 | }
77 |
78 | public void setUserRepository(UserRepository userRepository) {
79 | this.userRepository = userRepository;
80 | }
81 |
82 | public void setWebTokenService(WebTokenService webTokenService) {
83 | this.webTokenService = webTokenService;
84 | }
85 | }
86 |
--------------------------------------------------------------------------------
/src/main/java/org/catframework/agileworking/web/WechatController.java:
--------------------------------------------------------------------------------
1 | package org.catframework.agileworking.web;
2 |
3 | import java.util.Map;
4 |
5 | import org.catframework.agileworking.utils.JsonUtils;
6 | import org.catframework.agileworking.web.support.DefaultResult;
7 | import org.catframework.agileworking.web.support.Result;
8 | import org.springframework.beans.factory.annotation.Value;
9 | import org.springframework.util.Assert;
10 | import org.springframework.web.bind.annotation.PathVariable;
11 | import org.springframework.web.bind.annotation.RequestMapping;
12 | import org.springframework.web.bind.annotation.RequestMethod;
13 | import org.springframework.web.bind.annotation.RestController;
14 | import org.springframework.web.client.RestTemplate;
15 |
16 | @RestController
17 | public class WechatController {
18 |
19 | @Value("${wechat.app-id}")
20 | private String appId;
21 |
22 | @Value("${wechat.app-secret}")
23 | private String appSecret;
24 |
25 | @Value("${wecaht.jscode2session-url}")
26 | private String jscode2sessionUrl;
27 |
28 | private RestTemplate restTemplate = new RestTemplate();
29 |
30 | @RequestMapping(path = "/wechat/openid/{jsCode}", method = RequestMethod.GET)
31 | public Result getOpenId(@PathVariable String jsCode) {
32 | Map result = JsonUtils
33 | .decode(restTemplate.getForObject(jscode2sessionUrl, String.class, appId, appSecret, jsCode));
34 | String openid = (String) result.get("openid");
35 | Assert.hasLength(openid, "获取openid失败:" + result);
36 | return DefaultResult.newResult(openid);
37 | }
38 |
39 | public void setAppId(String appId) {
40 | this.appId = appId;
41 | }
42 |
43 | public void setAppSecret(String appSecret) {
44 | this.appSecret = appSecret;
45 | }
46 |
47 | public void setJscode2sessionUrl(String jscode2sessionUrl) {
48 | this.jscode2sessionUrl = jscode2sessionUrl;
49 | }
50 |
51 | public void setRestTemplate(RestTemplate restTemplate) {
52 | this.restTemplate = restTemplate;
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/src/main/java/org/catframework/agileworking/web/package-info.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Web 层
3 | * @author devzzm
4 | *
5 | */
6 | package org.catframework.agileworking.web;
--------------------------------------------------------------------------------
/src/main/java/org/catframework/agileworking/web/support/DefaultResult.java:
--------------------------------------------------------------------------------
1 | package org.catframework.agileworking.web.support;
2 |
3 | import java.util.HashMap;
4 | import java.util.Map;
5 |
6 | import org.catframework.agileworking.BusinessException;
7 | import org.catframework.agileworking.common.ResponseCodes;
8 |
9 | public class DefaultResult implements Result {
10 |
11 | /**
12 | * 创建一个成功的结果,不含 payload
13 | */
14 | public static Result> newResult() {
15 | DefaultResult> result = new DefaultResult<>();
16 | result.status = Result.STATUS_SUCCESS;
17 | result.responseCode = ResponseCodes.RESPONSE_CODE_SUCCESS;
18 | return result;
19 | }
20 |
21 | /**
22 | * 创建一个成功的结果
23 | *
24 | * @param payload 结果中的数据
25 | * @return 新创建的交易结果
26 | */
27 | public static Result newResult(T payload) {
28 | DefaultResult result = new DefaultResult<>();
29 | result.status = Result.STATUS_SUCCESS;
30 | result.responseCode = ResponseCodes.RESPONSE_CODE_SUCCESS;
31 | result.payload = payload;
32 | return result;
33 | }
34 |
35 | /**
36 | * 创建一个失败的结果
37 | *
38 | * @param ex 导致交易失败的具体异常
39 | * @return 新创建的交易结果
40 | */
41 | public static Result newFailResult(Throwable ex) {
42 | DefaultResult result = new DefaultResult<>();
43 | result.status = Result.STATUS_FAIL;
44 | result.responseMessage = ex.getMessage();
45 | result.responseCode = (ex instanceof BusinessException) ? ((BusinessException) ex).getCode()
46 | : ResponseCodes.RESPONSE_CODE_SYSTEM_ERROR;
47 | return result;
48 | }
49 |
50 | /**
51 | * 创建一个具有指定错误消息的失败结果
52 | *
53 | * @param message 错误消息
54 | * @return 新创建的交易结果
55 | */
56 | public static Result newFailResult(String message) {
57 | DefaultResult result = new DefaultResult<>();
58 | result.status = Result.STATUS_FAIL;
59 | result.responseMessage = message;
60 | result.responseCode = ResponseCodes.RESPONSE_CODE_SYSTEM_ERROR;
61 | return result;
62 | }
63 |
64 | private DefaultResult() {
65 | }
66 |
67 | private Map headers = new HashMap<>();
68 |
69 | private int status;
70 |
71 | private T payload;
72 |
73 | private String responseCode;
74 |
75 | private String responseMessage;
76 |
77 | @Override
78 | public int getStatus() {
79 | return status;
80 | }
81 |
82 | @Override
83 | public boolean isSuccess() {
84 | return Result.STATUS_SUCCESS == status;
85 | }
86 |
87 | @Override
88 | public String getResponseCode() {
89 | return responseCode;
90 | }
91 |
92 | @Override
93 | public String getResponseMessage() {
94 | return responseMessage;
95 | }
96 |
97 | @Override
98 | public T getPayload() {
99 | return payload;
100 | }
101 |
102 | @Override
103 | public Map getHeaders() {
104 | return headers;
105 | }
106 |
107 | @Override
108 | public Result setHeader(String key, Object value) {
109 | headers.put(key, value);
110 | return this;
111 | }
112 |
113 | @Override
114 | public Object getHeader(String key) {
115 | return headers.get(key);
116 | }
117 | }
118 |
--------------------------------------------------------------------------------
/src/main/java/org/catframework/agileworking/web/support/GlobalExceptionHandler.java:
--------------------------------------------------------------------------------
1 | package org.catframework.agileworking.web.support;
2 |
3 | import javax.servlet.http.HttpServletRequest;
4 |
5 | import org.apache.commons.logging.Log;
6 | import org.apache.commons.logging.LogFactory;
7 | import org.catframework.agileworking.BusinessException;
8 | import org.springframework.http.HttpStatus;
9 | import org.springframework.http.ResponseEntity;
10 | import org.springframework.web.bind.annotation.ControllerAdvice;
11 | import org.springframework.web.bind.annotation.ExceptionHandler;
12 | import org.springframework.web.bind.annotation.ResponseBody;
13 |
14 | /**
15 | * 全局异常处理器,统一将异常转换为 {@link Result}.
16 | *
17 | * @author devzzm
18 | */
19 | @ControllerAdvice
20 | public class GlobalExceptionHandler {
21 |
22 | private static final Log logger = LogFactory.getLog(GlobalExceptionHandler.class);
23 |
24 | @ExceptionHandler({ BusinessException.class, Exception.class })
25 | @ResponseBody
26 | ResponseEntity> handleControllerException(HttpServletRequest request, Throwable ex) {
27 | logger.error("handle exception:", ex);
28 | return new ResponseEntity<>(DefaultResult.newFailResult(ex), HttpStatus.INTERNAL_SERVER_ERROR);
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src/main/java/org/catframework/agileworking/web/support/Result.java:
--------------------------------------------------------------------------------
1 | package org.catframework.agileworking.web.support;
2 |
3 | import java.util.Map;
4 |
5 | /**
6 | * 统一封装请求结果的接口,便于集中的进行异常处理以及便于调用方使用.
7 | *
8 | * @author devzzm
9 | */
10 | public interface Result {
11 |
12 | /** 交易结果状态:成功. */
13 | public static final int STATUS_SUCCESS = 0;
14 |
15 | /** 交易结果状态:失败. */
16 | public static final int STATUS_FAIL = 1;
17 |
18 | /**
19 | * @return 此结果的状态
20 | * @see Result#STATUS_FAIL
21 | * @see Result#STATUS_SUCCESS
22 | */
23 | int getStatus();
24 |
25 | /**
26 | * @return 结果状态为 {@link Result#STATUS_SUCCESS} 返回 true
27 | */
28 | boolean isSuccess();
29 |
30 | /**
31 | * @return 响应码
32 | */
33 | String getResponseCode();
34 |
35 | /**
36 | * @return 响应消息
37 | */
38 | String getResponseMessage();
39 |
40 | /**
41 | * @return 消息所承载的数据
42 | */
43 | T getPayload();
44 |
45 | /**
46 | * @return 消息头承载的数据
47 | */
48 | Map getHeaders();
49 |
50 | /**
51 | * 向结果中添加 header
52 | *
53 | * @param key 添加 header 的 key
54 | * @param value 添加 header 的值
55 | * @return 当前结果对象
56 | */
57 | Result setHeader(String key, Object value);
58 |
59 | /**
60 | * 取指定 key 的头的值
61 | *
62 | * @param key 取值的 key
63 | * @return header 的值
64 | */
65 | Object getHeader(String key);
66 | }
67 |
--------------------------------------------------------------------------------
/src/main/java/org/catframework/agileworking/web/support/WebTokenHandlerInterceptor.java:
--------------------------------------------------------------------------------
1 | package org.catframework.agileworking.web.support;
2 |
3 | import java.util.Arrays;
4 |
5 | import javax.servlet.http.HttpServletRequest;
6 | import javax.servlet.http.HttpServletResponse;
7 |
8 | import org.catframework.agileworking.service.WebTokenService;
9 | import org.springframework.beans.factory.annotation.Autowired;
10 | import org.springframework.beans.factory.annotation.Value;
11 | import org.springframework.util.AntPathMatcher;
12 | import org.springframework.util.Assert;
13 | import org.springframework.util.PathMatcher;
14 | import org.springframework.util.StringUtils;
15 | import org.springframework.web.servlet.HandlerInterceptor;
16 | import org.springframework.web.servlet.ModelAndView;
17 |
18 | /**
19 | * 自定义拦截器实现了 WebToken 的校验.
20 | *
21 | * @author devzzm
22 | */
23 | public class WebTokenHandlerInterceptor implements HandlerInterceptor {
24 |
25 | @Autowired
26 | private WebTokenService webTokenService;
27 |
28 | private String[] ignoreUriPatterns;
29 |
30 | private PathMatcher pathMatcher = new AntPathMatcher();
31 |
32 | private String tokenKey = "Authorization";
33 |
34 | private String subjectKey = "Subject";
35 |
36 | @Override
37 | public boolean preHandle(HttpServletRequest req, HttpServletResponse resp, Object handler) throws Exception {
38 | if (isIgnoreUri(req.getRequestURI())) {
39 | return true;
40 | } else {
41 | String token = req.getHeader(tokenKey);
42 | Assert.notNull(token, "Authorization 不可为空.");
43 | String subject = req.getHeader(subjectKey);
44 | Assert.notNull(subject, "Subject 不可为空.");
45 | Assert.isTrue(webTokenService.verify(subject, token), "Web Token 校验失败.");
46 | return true;
47 | }
48 | }
49 |
50 | private boolean isIgnoreUri(String uri) {
51 | if (null == ignoreUriPatterns)
52 | return false;
53 | return Arrays.asList(ignoreUriPatterns).stream().anyMatch(p -> pathMatcher.match(p, uri));
54 | }
55 |
56 | @Override
57 | public void afterCompletion(HttpServletRequest req, HttpServletResponse resp, Object handler, Exception e)
58 | throws Exception {
59 | // do nothing
60 | }
61 |
62 | @Override
63 | public void postHandle(HttpServletRequest req, HttpServletResponse resp, Object handler, ModelAndView m)
64 | throws Exception {
65 | // do nothing
66 | }
67 |
68 | public void setWebTokenService(WebTokenService webTokenService) {
69 | this.webTokenService = webTokenService;
70 | }
71 |
72 | @Value("${web.token.ignore.uri.pattern}")
73 | public void setIgnoreUripattern(String ignoreUripattern) {
74 | if (!StringUtils.isEmpty(ignoreUripattern))
75 | ignoreUriPatterns = StringUtils.tokenizeToStringArray(ignoreUripattern, ",");
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/src/main/resources/application-dev.properties:
--------------------------------------------------------------------------------
1 | # profile 为单元测试时使用的配置
2 | spring.datasource.url=jdbc:mysql://119.29.92.158/agileworking_dev
3 | spring.datasource.username=agileworking_dev
4 | spring.datasource.password=iyah1984
5 | spring.jpa.hibernate.ddl-auto=create
--------------------------------------------------------------------------------
/src/main/resources/application-prd.properties:
--------------------------------------------------------------------------------
1 | # profile 为服务器部署运行时使用的配置
2 | #spring.jpa.hibernate.ddl-auto=create
3 | spring.datasource.url=jdbc:mysql://localhost/agileworking
4 | spring.datasource.username=agileworking
5 | spring.datasource.password=iyah1984
--------------------------------------------------------------------------------
/src/main/resources/application.properties:
--------------------------------------------------------------------------------
1 | debug=true
2 | spring.profiles.active=prd
3 | ## Server Common Config
4 | server.context-path=/agileworking
5 |
6 | ## TimeZone Configuration
7 | spring.jackson.time-zone=GMT+8
8 |
9 | ## DataSource Common Config
10 | spring.datasource.driver-class-name=com.mysql.jdbc.Driver
11 | spring.datasource.max-idle=10
12 | spring.datasource.max-wait=10000
13 | spring.datasource.min-idle=5
14 | spring.datasource.initial-size=5
15 | spring.datasource.validation-query=SELECT 1
16 | spring.datasource.validation-interval=10000
17 | spring.datasource.time-between-eviction-runs-millis=10000
18 | spring.datasource.min-evictable-idle-time-millis=10000
19 | spring.datasource.remove-abandoned=true
20 | spring.datasource.remove-abandoned-timeout=60
21 | spring.datasource.test-on-borrow=true
22 | spring.datasource.test-while-idle=true
23 |
24 | ## JPA Common Configuration
25 | spring.jpa.open-in-view=true
26 | spring.jpa.show-sql=true
27 | spring.jpa.properties.hibernate.format_sql=true
28 |
29 | ## Web Token Configuration
30 | # 不需要进行 web token 的 uri 列表使用 ',' 分隔,支持 Ant-style 路径模式
31 | web.token.ignore.uri.pattern=/agileworking/teams,/agileworking/team/*/join,/agileworking/team/*/user/*,/agileworking/wechat/openid/*,/agileworking/schedules/*
32 |
33 | ## 消息提醒参数配置
34 | # 小程序应用 id
35 | wechat.app-id=wx4d8a2edf037d61e9
36 | # 小程序应用秘钥
37 | wechat.app-secret=bbd52b658d07b218be171f1e99f1b092
38 | # 小程序消息通知模板 id
39 | wechat.notify.template-id=NWlDmDjPmc_E-czrsxZQH1qr-LqrYsR9jRl4mJ_luQo
40 | wecaht.acquire-access-token-url=http://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={appId}&secret={appSecret}
41 | wecaht.send-messag-url=http://api.weixin.qq.com/cgi-bin/message/wxopen/template/send?access_token={ACCESS_TOKEN}
42 | wecaht.jscode2session-url=http://api.weixin.qq.com/sns/jscode2session?appid={appId}&secret={appSecret}&js_code={jsCode}&grant_type=authorization_code
--------------------------------------------------------------------------------
/src/main/resources/logback.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
6 | ${APP_LOG_PATH}/app.log
7 |
8 | app-%d{yyyy-MM-dd}.log
9 |
10 |
11 | %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
12 |
13 |
14 |
15 |
16 |
17 |
18 | %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/src/test/java/org/catframework/agileworking/domain/MeetingRoomFactory.java:
--------------------------------------------------------------------------------
1 | package org.catframework.agileworking.domain;
2 |
3 | import java.util.ArrayList;
4 | import java.util.List;
5 |
6 | /**
7 | * 用于单元测试的 {@link MeetingRoom} 工厂,毕竟在案例中繁琐的重复的构建初始化数据.
8 | *
9 | * @author devzzm
10 | */
11 | public final class MeetingRoomFactory {
12 |
13 | public static final Long DEFAULT_TEAM_ID = 1L;
14 |
15 | public static final List defaultMeetingRooms() {
16 | List meetingRooms = new ArrayList<>();
17 | meetingRooms.add(MeetingRoomFactory.newVideoMeetingRoom("3201", "大", "思科", "182.207.96.163", "21169"));
18 | meetingRooms.add(MeetingRoomFactory.newVideoMeetingRoom("3202", "中", "华为", "182.207.96.166", "120706"));
19 | meetingRooms.add(MeetingRoomFactory.newMeetingRoom("3203", "小"));
20 | meetingRooms.add(MeetingRoomFactory.newMeetingRoom("3206", "小"));
21 | return meetingRooms;
22 | }
23 |
24 | public static final MeetingRoom newVideoMeetingRoom(String roomNo, String size, String vendor, String ip,
25 | String terminalIp) {
26 | MeetingRoom meetingRoom = new MeetingRoom();
27 | meetingRoom.setRoomNo(roomNo);
28 | meetingRoom.setIp(ip);
29 | meetingRoom.setSize(size);
30 | meetingRoom.setTerminalId(terminalIp);
31 | meetingRoom.setType("视频");
32 | meetingRoom.setVnedor(vendor);
33 | meetingRoom.setTeamId(MeetingRoomFactory.DEFAULT_TEAM_ID);
34 | return meetingRoom;
35 | }
36 |
37 | public static final MeetingRoom newMeetingRoom(String roomNo, String size) {
38 | MeetingRoom meetingRoom = new MeetingRoom();
39 | meetingRoom.setRoomNo(roomNo);
40 | meetingRoom.setSize(size);
41 | meetingRoom.setType("普通");
42 | meetingRoom.setTeamId(MeetingRoomFactory.DEFAULT_TEAM_ID);
43 | return meetingRoom;
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/src/test/java/org/catframework/agileworking/domain/ScheduleFactory.java:
--------------------------------------------------------------------------------
1 | package org.catframework.agileworking.domain;
2 |
3 | import org.catframework.agileworking.utils.DateUtils;
4 |
5 | public final class ScheduleFactory {
6 |
7 | public static final String DEFAULT_CREATOR_AVATAR_URL = "http://baidu.com";
8 |
9 | public static Schedule newSchedule(String title, String creator, String date, String startTime, String endTime) {
10 | Schedule s = new Schedule();
11 | s.setTitle(title);
12 | s.setCreatorNickName(creator);
13 | s.setCreatorOpenId(creator);
14 | s.setCreatorAvatarUrl(ScheduleFactory.DEFAULT_CREATOR_AVATAR_URL);
15 | s.setDate(DateUtils.parse(date, DateUtils.PATTERN_SIMPLE_DATE));
16 | s.setStartTime(startTime);
17 | s.setEndTime(endTime);
18 | s.setRepeatMode(Schedule.REPEAT_MODE_NO);
19 | return s;
20 | }
21 |
22 | public static Schedule newWeeklySchedule(String title, String creator, String date, String startTime,
23 | String endTime) {
24 | Schedule s = newSchedule(title, creator, date, startTime, endTime);
25 | s.setRepeatMode(Schedule.REPEAT_MODE_WEEKLY);
26 | return s;
27 | }
28 |
29 | }
30 |
--------------------------------------------------------------------------------
/src/test/java/org/catframework/agileworking/domain/ScheduleRepositoryTest.java:
--------------------------------------------------------------------------------
1 | package org.catframework.agileworking.domain;
2 |
3 | import java.util.List;
4 |
5 | import org.catframework.agileworking.utils.DateUtils;
6 | import org.catframework.agileworking.vo.ScheduleVO;
7 | import org.catframework.agileworking.web.MeetingRoomController;
8 | import org.catframework.agileworking.web.ScheduleController;
9 | import org.catframework.agileworking.web.support.Result;
10 | import org.junit.After;
11 | import org.junit.Assert;
12 | import org.junit.Before;
13 | import org.junit.Test;
14 | import org.junit.runner.RunWith;
15 | import org.springframework.beans.factory.annotation.Autowired;
16 | import org.springframework.boot.test.context.SpringBootTest;
17 | import org.springframework.test.context.junit4.SpringRunner;
18 |
19 | @RunWith(SpringRunner.class)
20 | @SpringBootTest
21 | public class ScheduleRepositoryTest {
22 |
23 | @Autowired
24 | private ScheduleRepository scheduleRepository;
25 |
26 | @Autowired
27 | private ScheduleController scheduleController;
28 |
29 | @Autowired
30 | private MeetingRoomController meetingRoomController;
31 |
32 | @Autowired
33 | private MeetingRoomRepository meetingRoomRepository;
34 |
35 |
36 | @Autowired
37 | private TeamRepository teamRepository;
38 |
39 | @Autowired
40 | private UserRepository userRepository;
41 |
42 | private Team team = TeamFactory.newDefaultTeam();
43 |
44 | private User user = UserFactory.newDefaultUser();
45 |
46 | private List meetingRooms = MeetingRoomFactory.defaultMeetingRooms();
47 |
48 | @Before
49 | public void before() {
50 | userRepository.save(user);
51 | teamRepository.save(team);
52 | team.addUser(user);
53 | teamRepository.save(team);
54 | meetingRooms.stream().forEach(m-> m.setTeamId(team.getId()));
55 | meetingRoomRepository.save(meetingRooms);
56 | }
57 |
58 | @After
59 | public void after() {
60 | teamRepository.delete(team.getId());
61 | userRepository.delete(user);
62 | meetingRoomRepository.delete(meetingRooms);
63 | }
64 |
65 | @Test
66 | public void testFindScheules() {
67 |
68 | Result> result = meetingRoomController.list(MeetingRoomFactory.DEFAULT_TEAM_ID);
69 | Schedule s = ScheduleFactory.newWeeklySchedule("分行业务平台项目组临时会议", "七猫", "2017-08-02", "13:00", "14:00");
70 | meetingRoomController.createOrUpdateSchedule(result.getPayload().get(0).getId(),"fakeFormId", s);
71 | Participant p = new Participant();
72 | p.setDate(DateUtils.parse("2017-08-02", DateUtils.PATTERN_SIMPLE_DATE));
73 | p.setAvatarUrl("some url");
74 | p.setNickName("7upcat");
75 | p.setOpenId("7upcat_open_id");
76 | scheduleController.join(s.getId(), p);
77 | s = scheduleRepository.findOne(s.getId());
78 | Assert.assertEquals(2, s.getParticipants().size());
79 | Assert.assertEquals("七猫", s.getParticipants().get(0).getNickName());
80 | try {
81 | scheduleController.join(s.getId(), p);
82 | Assert.fail();
83 | } catch (Exception e) {
84 | Assert.assertEquals("您已加入过此会议啦.", e.getMessage());
85 | }
86 | List scheduleVOs=scheduleRepository.findByOpenIdAndDate("7upcat_open_id",DateUtils.parse("2017-08-09", DateUtils.PATTERN_SIMPLE_DATE));
87 | Assert.assertEquals(1, scheduleVOs.size());
88 | scheduleRepository.delete(s);
89 | }
90 |
91 | }
92 |
--------------------------------------------------------------------------------
/src/test/java/org/catframework/agileworking/domain/ScheduleTest.java:
--------------------------------------------------------------------------------
1 | package org.catframework.agileworking.domain;
2 |
3 | import static org.junit.Assert.assertSame;
4 | import static org.junit.Assert.assertTrue;
5 |
6 | import java.util.ArrayList;
7 | import java.util.List;
8 | import java.util.stream.Collectors;
9 |
10 | import org.junit.Assert;
11 | import org.junit.Test;
12 |
13 | public class ScheduleTest {
14 |
15 | @Test
16 | public void testCompareTo() {
17 | Schedule s1 = ScheduleFactory.newSchedule("XXX1", "发哥", "2017-08-02", "13:30", "15:00");
18 | Schedule s2 = ScheduleFactory.newSchedule("XXX2", "发哥", "2017-08-03", "13:30", "15:00");
19 | assertTrue(s1.compareTo(s2) < 0);
20 | Schedule s3 = ScheduleFactory.newSchedule("XXX2", "发哥", "2017-08-02", "16:30", "17:00");
21 | assertTrue(s1.compareTo(s3) < 0);
22 | List schedules = new ArrayList<>();
23 | schedules.add(s3);
24 | schedules.add(s2);
25 | schedules.add(s1);
26 | schedules = schedules.stream().sorted().collect(Collectors.toList());
27 | assertSame(s1, schedules.get(0));
28 | assertSame(s3, schedules.get(1));
29 | assertSame(s2, schedules.get(2));
30 | }
31 |
32 | @Test
33 | public void isConflict() {
34 | Schedule s1 = ScheduleFactory.newSchedule("XXX1", "发哥", "2017-08-02", "13:30", "15:00");
35 | s1.setId((long) 1);
36 | Schedule s2 = ScheduleFactory.newSchedule("XXX2", "发哥", "2017-08-03", "13:30", "15:00");
37 | s2.setId((long) 2);
38 | Assert.assertFalse(s1.isConflict(s2));
39 | Schedule s3 = ScheduleFactory.newSchedule("XXX2", "发哥", "2017-08-02", "13:30", "15:00");
40 | s3.setId((long) 3);
41 | Assert.assertTrue(s1.isConflict(s3));
42 | Schedule s4 = ScheduleFactory.newSchedule("XXX2", "发哥", "2017-08-02", "10:00", "16:00");
43 | s4.setId((long) 4);
44 | Assert.assertTrue(s1.isConflict(s4));
45 | Schedule s5 = ScheduleFactory.newSchedule("XXX2", "发哥", "2017-08-02", "10:00", "13:30");
46 | s5.setId((long) 5);
47 | Assert.assertFalse(s1.isConflict(s5));
48 | Schedule s6 = ScheduleFactory.newSchedule("XXX2", "发哥", "2017-08-02", "15:00", "17:00");
49 | s6.setId((long) 6);
50 | Assert.assertFalse(s1.isConflict(s6));
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/src/test/java/org/catframework/agileworking/domain/TeamFactory.java:
--------------------------------------------------------------------------------
1 | package org.catframework.agileworking.domain;
2 |
3 | public final class TeamFactory {
4 |
5 | /** 团队注册时使用的默认的口令. */
6 | public static final String DEFAULT_TEAM_TOKEN = "9527";
7 |
8 | public static Team newDefaultTeam() {
9 | Team team = new Team();
10 | team.setName("深研二部");
11 | team.setDesc("一个敏捷的团队");
12 | team.setToken(TeamFactory.DEFAULT_TEAM_TOKEN);
13 | return team;
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/src/test/java/org/catframework/agileworking/domain/UserFactory.java:
--------------------------------------------------------------------------------
1 | package org.catframework.agileworking.domain;
2 |
3 | public final class UserFactory {
4 |
5 | public static User newDefaultUser() {
6 | User user = new User();
7 | user.setOpenId("7upcat_open_id");
8 | user.setAvatarUrl("http://catframework.cn");
9 | user.setMobileNo("18603010499");
10 | user.setName("郑明明");
11 | user.setNickName("7upcat");
12 | return user;
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/src/test/java/org/catframework/agileworking/package-info.java:
--------------------------------------------------------------------------------
1 | /**
2 | * @author devzzm
3 | */
4 | package org.catframework.agileworking;
--------------------------------------------------------------------------------
/src/test/java/org/catframework/agileworking/scheduling/SendNotifyMessageJobTest.java:
--------------------------------------------------------------------------------
1 | package org.catframework.agileworking.scheduling;
2 |
3 | import java.util.Date;
4 |
5 | import org.catframework.agileworking.utils.DateUtils;
6 | import org.junit.Test;
7 |
8 | public class SendNotifyMessageJobTest {
9 |
10 | @Test
11 | public void testIsNeedSendMessageNow() {
12 | Date d =DateUtils.parse("2017-08-31" + " " + "10:20",DateUtils.PATTERN_SIMPLE_DATE + " HH:mm");
13 | Long time =d.getTime()+15 * 60 * 1000;
14 | System.out.println(new Date(time));
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/src/test/java/org/catframework/agileworking/service/impl/ScheduleServiceImplTest.java:
--------------------------------------------------------------------------------
1 | package org.catframework.agileworking.service.impl;
2 |
3 | import static org.junit.Assert.assertEquals;
4 |
5 | import java.text.DateFormat;
6 | import java.text.ParseException;
7 | import java.text.SimpleDateFormat;
8 | import java.util.Calendar;
9 | import java.util.List;
10 |
11 | import org.catframework.agileworking.domain.MeetingRoom;
12 | import org.catframework.agileworking.domain.MeetingRoomFactory;
13 | import org.catframework.agileworking.domain.MeetingRoomRepository;
14 | import org.catframework.agileworking.domain.Schedule;
15 | import org.catframework.agileworking.domain.ScheduleFactory;
16 | import org.catframework.agileworking.domain.ScheduleRepository;
17 | import org.catframework.agileworking.domain.User;
18 | import org.catframework.agileworking.domain.UserFactory;
19 | import org.catframework.agileworking.domain.UserRepository;
20 | import org.catframework.agileworking.utils.DateUtils;
21 | import org.catframework.agileworking.web.MeetingRoomController;
22 | import org.catframework.agileworking.web.support.Result;
23 | import org.junit.After;
24 | import org.junit.Before;
25 | import org.junit.Test;
26 | import org.junit.runner.RunWith;
27 | import org.springframework.beans.factory.annotation.Autowired;
28 | import org.springframework.boot.test.context.SpringBootTest;
29 | import org.springframework.test.context.junit4.SpringRunner;
30 |
31 | @RunWith(SpringRunner.class)
32 | @SpringBootTest
33 | public class ScheduleServiceImplTest {
34 |
35 | @Autowired
36 | private MeetingRoomController meetingRoomController;
37 |
38 | @Autowired
39 | private MeetingRoomRepository meetingRoomRepository;
40 |
41 | @Autowired
42 | private ScheduleRepository scheduleRepository;
43 |
44 | @Autowired
45 | private UserRepository userRepository;
46 |
47 | private List meetingRooms = MeetingRoomFactory.defaultMeetingRooms();
48 |
49 | private User user = UserFactory.newDefaultUser();
50 |
51 | @Before
52 | public void before() {
53 | user.setOpenId("七猫");
54 | userRepository.save(user);
55 | meetingRoomRepository.save(meetingRooms);
56 | }
57 |
58 | @After
59 | public void after() {
60 | meetingRoomRepository.delete(meetingRooms);
61 | userRepository.delete(user);
62 | }
63 |
64 | @Test
65 | public void testCalendar() throws ParseException {
66 | Calendar cal = Calendar.getInstance();
67 | DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
68 | cal.setTime(dateFormat.parse("2017-08-01"));
69 | assertEquals(Calendar.TUESDAY, cal.get(Calendar.DAY_OF_WEEK));
70 | }
71 |
72 | @Test
73 | public void testFind() {
74 | Schedule s1 = ScheduleFactory.newSchedule("分行业务平台项目组临时会议", "七猫", "2017-08-09", "09:00", "12:00");
75 | Schedule s2 = ScheduleFactory.newWeeklySchedule("分行业务平台项目组临时会议", "七猫", "2017-08-02", "12:00", "14:00");
76 | Schedule s3 = ScheduleFactory.newSchedule("分行业务平台项目组临时会议", "七猫", "2017-08-16", "14:00", "16:00");
77 | try {
78 | meetingRoomController.createOrUpdateSchedule(meetingRooms.get(0).getId(),"fakeFormId" ,s1);
79 | meetingRoomController.createOrUpdateSchedule(meetingRooms.get(0).getId(),"fakeFormId",s2);
80 | meetingRoomController.createOrUpdateSchedule(meetingRooms.get(0).getId(),"fakeFormId",s3);
81 | Result> result = meetingRoomController.schedules(meetingRooms.get(0).getId(),
82 | DateUtils.parse("2017-08-09", DateUtils.PATTERN_SIMPLE_DATE));
83 | assertEquals(2, result.getPayload().size());
84 | } finally {
85 | scheduleRepository.delete(s1);
86 | scheduleRepository.delete(s2);
87 | scheduleRepository.delete(s3);
88 | }
89 | }
90 | }
91 |
--------------------------------------------------------------------------------
/src/test/java/org/catframework/agileworking/service/impl/SimpleJJWTWebTokenServiceImplTest.java:
--------------------------------------------------------------------------------
1 | package org.catframework.agileworking.service.impl;
2 |
3 | import static org.junit.Assert.*;
4 |
5 | import org.catframework.agileworking.service.WebTokenService;
6 | import org.junit.Test;
7 |
8 | public class SimpleJJWTWebTokenServiceImplTest {
9 |
10 | private static WebTokenService webTokenService = new SimpleJJWTWebTokenServiceImpl();
11 |
12 | private static String token = null;
13 |
14 | private static String subject = "7upcat";
15 |
16 | @Test
17 | public void testGenerate() {
18 | token = webTokenService.generate(subject);
19 | assertNotNull(token);
20 | }
21 |
22 | @Test
23 | public void testVerify() {
24 | assertTrue(webTokenService.verify(subject, token));
25 | }
26 |
27 | }
28 |
--------------------------------------------------------------------------------
/src/test/java/org/catframework/agileworking/service/impl/WeChatApiIntegrationTest.java:
--------------------------------------------------------------------------------
1 | package org.catframework.agileworking.service.impl;
2 |
3 | import java.util.HashMap;
4 | import java.util.Map;
5 |
6 | import org.apache.commons.logging.Log;
7 | import org.apache.commons.logging.LogFactory;
8 | import org.catframework.agileworking.utils.JsonUtils;
9 | import org.junit.Assert;
10 | import org.junit.Before;
11 | import org.junit.Test;
12 | import org.junit.runner.RunWith;
13 | import org.springframework.beans.factory.annotation.Value;
14 | import org.springframework.boot.test.context.SpringBootTest;
15 | import org.springframework.test.context.junit4.SpringRunner;
16 | import org.springframework.web.client.RestTemplate;
17 |
18 | /**
19 | * 集成测试验证,需要提供 form_id 才可以进行测试.
20 | * @author devzzm
21 | */
22 | @RunWith(SpringRunner.class)
23 | @SpringBootTest
24 | public class WeChatApiIntegrationTest {
25 |
26 | private static final Log logger = LogFactory.getLog(WeChatApiIntegrationTest.class);
27 |
28 | @Value("${wechat.app-id}")
29 | private String appId;
30 |
31 | @Value("${wechat.app-secret}")
32 | private String appSecret;
33 |
34 | @Value("${wechat.notify.template-id}")
35 | private String templateId;
36 |
37 | private String accessToken;
38 |
39 | private RestTemplate restTemplate = new RestTemplate();
40 |
41 | @Before
42 | public void init() {
43 | String result = restTemplate.getForObject(
44 | "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={appId}&secret={appSecret}",
45 | String.class, appId, appSecret);
46 | accessToken = (String) JsonUtils.decode(result).get("access_token");
47 | Assert.assertNotNull(accessToken);
48 | }
49 |
50 | @Test
51 | public void testSendMessage() {
52 | Map request = new HashMap<>();
53 | // TODO 目标用户的 openId 需要按实际指定
54 | request.put("touser", "oFb4O0XMZrijTGa_5kli4E6shEV0");
55 | request.put("template_id", templateId);
56 | // TODO 后续验证需要提供 from_id才可以进行
57 | request.put("form_id", "1504144250621");
58 | Map data = new HashMap<>();
59 | Map keyword1 = new HashMap<>();
60 | keyword1.put("value", "11111");
61 | data.put("keyword1", keyword1);
62 |
63 | Map keyword2 = new HashMap<>();
64 | keyword2.put("value", "22222");
65 | data.put("keyword2", keyword2);
66 |
67 | Map keyword3 = new HashMap<>();
68 | keyword3.put("value", "33333");
69 | data.put("keyword3", keyword3);
70 |
71 | Map keyword4 = new HashMap<>();
72 | keyword4.put("value", "44444");
73 | data.put("keyword4", keyword4);
74 |
75 | Map keyword5 = new HashMap<>();
76 | keyword5.put("value", "5555");
77 | data.put("keyword5", keyword5);
78 | request.put("data", data);
79 | String result = restTemplate.postForObject(
80 | "https://api.weixin.qq.com/cgi-bin/message/wxopen/template/send?access_token={ACCESS_TOKEN}", request,
81 | String.class, accessToken);
82 | logger.info(result);
83 | }
84 |
85 |
86 | public static void main(String[] args) {
87 | Map data = new HashMap<>();
88 | data.put("REQUEST_MESSAGE", "2222");
89 | new RestTemplate().postForObject(
90 | "http://localhost:8080/MOBX-ANTD-ADMIN/loginProcess.json?REQUEST_MESSAGE=2222", data,String.class);
91 | }
92 | }
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
--------------------------------------------------------------------------------
/src/test/java/org/catframework/agileworking/utils/DateUtilsTest.java:
--------------------------------------------------------------------------------
1 | package org.catframework.agileworking.utils;
2 |
3 | import java.util.Date;
4 |
5 | import org.junit.Assert;
6 | import org.junit.Test;
7 |
8 | public class DateUtilsTest {
9 |
10 | @Test
11 | public void testFormat() {
12 | Date date = DateUtils.parse("2017-08-31", DateUtils.PATTERN_SIMPLE_DATE);
13 | Assert.assertEquals("2017年08月31日",DateUtils.format(date, "yyyy年MM月dd日"));
14 | }
15 |
16 | }
17 |
--------------------------------------------------------------------------------
/src/test/java/org/catframework/agileworking/utils/JsonUtilsTest.java:
--------------------------------------------------------------------------------
1 | package org.catframework.agileworking.utils;
2 |
3 | import static org.junit.Assert.*;
4 |
5 | import java.util.Map;
6 |
7 | import org.junit.Test;
8 |
9 | public class JsonUtilsTest {
10 |
11 | @Test
12 | public void testDecode() {
13 | Map result =JsonUtils.decode("{\"access_token\":\"ExV9F-XgAZR3uLnUv269VoEuCkeSoSISWigFuwYHw3zlQMKqNn88hW4tsQbp-Ie46oI3MIKYBeKm1nsCO4qBr_PiyC4dqkpqBMgI0n8buVhHBZC4qrsYDqf8Zb2GxQdUMPTgAJAILP\",\"expires_in\":7200}");
14 | assertEquals(7200,result.get("expires_in"));
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/src/test/java/org/catframework/agileworking/vo/NotifyMessageTemplateTest.java:
--------------------------------------------------------------------------------
1 | package org.catframework.agileworking.vo;
2 |
3 | import java.util.Map;
4 |
5 | import org.catframework.agileworking.domain.MeetingRoom;
6 | import org.catframework.agileworking.domain.Participant;
7 | import org.catframework.agileworking.domain.Schedule;
8 | import org.catframework.agileworking.domain.User;
9 | import org.catframework.agileworking.utils.DateUtils;
10 | import org.junit.Assert;
11 | import org.junit.Test;
12 |
13 | public class NotifyMessageTemplateTest {
14 |
15 | @SuppressWarnings("unchecked")
16 | @Test
17 | public void testToTemplateMessage() {
18 | User creator = new User();
19 | creator.setOpenId("xxOpenId");
20 | creator.setName("郑明明");
21 | creator.setNickName("七猫");
22 | creator.setMobileNo("18603010498");
23 | MeetingRoom meetingRoom = new MeetingRoom();
24 | meetingRoom.setRoomNo("3203");
25 | Schedule schedule = new Schedule();
26 | schedule.setTitle("讨论一下明天吃点咩");
27 | schedule.setMeetingRoom(meetingRoom);
28 | schedule.setDate(DateUtils.parse("2017-08-31", DateUtils.PATTERN_SIMPLE_DATE));
29 | schedule.setStartTime("10:00");
30 | schedule.setEndTime("12:00");
31 | Participant participant = new Participant();
32 | participant.setNickName("七猫");
33 | participant.setOpenId(creator.getOpenId());
34 | participant.setFormId("mockFormId");
35 | schedule.addParticipant(participant);
36 | NotifyMessageTemplate teamplate = new NotifyMessageTemplate(creator, schedule, participant, "mockTemplateId");
37 | Map message = teamplate.toTemplateMessage();
38 |
39 | Assert.assertEquals("xxOpenId", message.get("touser"));
40 | Assert.assertEquals("mockTemplateId", message.get("template_id"));
41 | Assert.assertEquals("mockFormId", message.get("form_id"));
42 | Map data = (Map) message.get("data");
43 | Map keyword1 = (Map) data.get("keyword1");
44 | Assert.assertEquals(schedule.getTitle(), keyword1.get("value"));
45 | Map keyword2 = (Map) data.get("keyword2");
46 | Assert.assertEquals("2017年08月31日 10:00-12:00", keyword2.get("value"));
47 | Map keyword3 = (Map) data.get("keyword3");
48 | Assert.assertEquals(meetingRoom.getRoomNo(), keyword3.get("value"));
49 | Map keyword4 = (Map) data.get("keyword4");
50 | Assert.assertEquals("1人参加", keyword4.get("value"));
51 | Map keyword5 = (Map) data.get("keyword5");
52 | Assert.assertEquals(creator.getNickName()+"/"+creator.getName()+"("+creator.getMobileNo()+")", keyword5.get("value"));
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/src/test/java/org/catframework/agileworking/web/MeetingRoomControllerIntegrationTest.java:
--------------------------------------------------------------------------------
1 | package org.catframework.agileworking.web;
2 |
3 | import org.catframework.agileworking.domain.ScheduleFactory;
4 | import org.junit.Test;
5 | import org.springframework.web.client.RestTemplate;
6 |
7 | public class MeetingRoomControllerIntegrationTest {
8 |
9 | private String url = "http://localhost:8080";
10 |
11 | @Test
12 | public void schedules() {
13 | RestTemplate restTemplate= new RestTemplate();
14 | restTemplate.postForObject(url+"/meetingRooms/{id}/schedule", ScheduleFactory.newSchedule("测试排期", "7upcat", "2017-02-02", "12:00", "16:00"), String.class,"1");
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/src/test/java/org/catframework/agileworking/web/MeetingRoomControllerTest.java:
--------------------------------------------------------------------------------
1 | package org.catframework.agileworking.web;
2 |
3 | import java.util.List;
4 |
5 | import org.catframework.agileworking.domain.MeetingRoom;
6 | import org.catframework.agileworking.domain.MeetingRoomFactory;
7 | import org.catframework.agileworking.domain.MeetingRoomRepository;
8 | import org.catframework.agileworking.domain.Schedule;
9 | import org.catframework.agileworking.domain.ScheduleFactory;
10 | import org.catframework.agileworking.domain.ScheduleRepository;
11 | import org.catframework.agileworking.domain.User;
12 | import org.catframework.agileworking.domain.UserFactory;
13 | import org.catframework.agileworking.domain.UserRepository;
14 | import org.catframework.agileworking.utils.DateUtils;
15 | import org.catframework.agileworking.web.support.Result;
16 | import org.junit.After;
17 | import org.junit.Assert;
18 | import org.junit.Before;
19 | import org.junit.Test;
20 | import org.junit.runner.RunWith;
21 | import org.springframework.beans.factory.annotation.Autowired;
22 | import org.springframework.boot.test.context.SpringBootTest;
23 | import org.springframework.test.context.junit4.SpringRunner;
24 |
25 | @RunWith(SpringRunner.class)
26 | @SpringBootTest
27 | public class MeetingRoomControllerTest {
28 |
29 | @Autowired
30 | private MeetingRoomController meetingRoomController;
31 |
32 | @Autowired
33 | private MeetingRoomRepository meetingRoomRepository;
34 |
35 | @Autowired
36 | private ScheduleRepository scheduleRepository;
37 |
38 | @Autowired
39 | private UserRepository userRepository;
40 |
41 | private User user1 = UserFactory.newDefaultUser();
42 | private User user2 = UserFactory.newDefaultUser();
43 |
44 | private List meetingRooms = MeetingRoomFactory.defaultMeetingRooms();
45 |
46 | @Before
47 | public void before() {
48 | user1.setOpenId("七猫");
49 | user2.setOpenId("发哥");
50 | userRepository.save(user1);
51 | userRepository.save(user2);
52 | meetingRoomRepository.save(meetingRooms);
53 | }
54 |
55 | @After
56 | public void after() {
57 | meetingRoomRepository.delete(meetingRooms);
58 | userRepository.delete(user1);
59 | userRepository.delete(user2);
60 |
61 | }
62 |
63 | @Test
64 | public void list() throws Exception {
65 | Result> result = meetingRoomController.list(MeetingRoomFactory.DEFAULT_TEAM_ID);
66 | Assert.assertTrue(result.isSuccess());
67 | org.junit.Assert.assertEquals(4, result.getPayload().size());
68 | }
69 |
70 | @Test
71 | public void newSchedule() {
72 | Result> result = meetingRoomController.list(MeetingRoomFactory.DEFAULT_TEAM_ID);
73 |
74 | // 创建一个每周重复的排期
75 | Schedule schedule1 = ScheduleFactory.newWeeklySchedule("分行业务平台项目组临时会议", "七猫", "2017-08-02", "09:00", "12:00");
76 | try {
77 | meetingRoomController.createOrUpdateSchedule(result.getPayload().get(0).getId(), "fakeFormId", schedule1);
78 | Assert.assertNotNull(schedule1.getId());
79 | // 案例验证更新排期
80 | schedule1.setTitle("分行业务平台项目组临时会议-修订后");
81 | meetingRoomController.createOrUpdateSchedule(result.getPayload().get(0).getId(), "fakeFormId", schedule1);
82 | Schedule dbSchedule = scheduleRepository.findOne(schedule1.getId());
83 | Assert.assertEquals(schedule1.getTitle(), dbSchedule.getTitle());
84 | // 创建人默人参加会议
85 | Assert.assertEquals(1, dbSchedule.getParticipants().size());
86 | Assert.assertEquals(schedule1.getCreatorAvatarUrl(), dbSchedule.getParticipants().get(0).getAvatarUrl());
87 |
88 | // 当天时间有冲突的案例
89 | Schedule schedule2 = ScheduleFactory.newSchedule("POS清算代码评审", "发哥", "2017-08-02", "10:00", "11:00");
90 | try {
91 | meetingRoomController.createOrUpdateSchedule(result.getPayload().get(0).getId(), "fakeFormId",
92 | schedule2);
93 | Assert.fail();
94 | } catch (Exception e) {
95 | Assert.assertEquals("同已有排期冲突.", e.getMessage());
96 | }
97 |
98 | // 下周时间有冲突的案例
99 | Schedule schedule3 = ScheduleFactory.newSchedule("POS清算代码评审", "发哥", "2017-08-09", "09:30", "15:00");
100 | try {
101 | meetingRoomController.createOrUpdateSchedule(result.getPayload().get(0).getId(), "fakeFormId",
102 | schedule3);
103 | Assert.fail();
104 | } catch (Exception e) {
105 | Assert.assertEquals("同已有排期冲突.", e.getMessage());
106 | }
107 | } finally {
108 | scheduleRepository.delete(schedule1);
109 | }
110 |
111 | }
112 |
113 | @Test
114 | public void cancelSchedule() {
115 | Result> result = meetingRoomController.list(MeetingRoomFactory.DEFAULT_TEAM_ID);
116 | // 创建一个每周重复的排期
117 | Schedule s = ScheduleFactory.newWeeklySchedule("分行业务平台项目组临时会议", "七猫", "2017-08-02", "13:00", "14:00");
118 | meetingRoomController.createOrUpdateSchedule(result.getPayload().get(0).getId(), "fakeFormId", s);
119 | Assert.assertNotNull(scheduleRepository.findOne(s.getId()));
120 | meetingRoomController.cancelSchedule(s.getId());
121 | Assert.assertNull(scheduleRepository.findOne(s.getId()));
122 | }
123 |
124 | @Test
125 | public void schedules() {
126 | Result> result = meetingRoomController.list(MeetingRoomFactory.DEFAULT_TEAM_ID);
127 | // 创建一个每周重复的排期
128 | Schedule schedule1 = ScheduleFactory.newWeeklySchedule("分行业务平台项目组临时会议", "七猫", "2017-08-02", "13:00", "14:00");
129 | meetingRoomController.createOrUpdateSchedule(result.getPayload().get(0).getId(), "fakeFormId", schedule1);
130 | Schedule schedule2 = ScheduleFactory.newWeeklySchedule("CPOS临时例会", "发哥", "2017-08-09", "14:00", "16:00");
131 | meetingRoomController.createOrUpdateSchedule(result.getPayload().get(0).getId(), "fakeFormId", schedule2);
132 | try {
133 | Result> schedulesResult = meetingRoomController.schedules(result.getPayload().get(0).getId(),
134 | DateUtils.parse("2017-08-09", DateUtils.PATTERN_SIMPLE_DATE));
135 | Assert.assertEquals(2, schedulesResult.getPayload().size());
136 | } finally {
137 | scheduleRepository.delete(schedule1);
138 | scheduleRepository.delete(schedule2);
139 | }
140 | }
141 | }
142 |
--------------------------------------------------------------------------------
/src/test/java/org/catframework/agileworking/web/ScheduleControllerTest.java:
--------------------------------------------------------------------------------
1 | package org.catframework.agileworking.web;
2 |
3 | import java.util.List;
4 |
5 | import org.catframework.agileworking.domain.MeetingRoom;
6 | import org.catframework.agileworking.domain.MeetingRoomFactory;
7 | import org.catframework.agileworking.domain.MeetingRoomRepository;
8 | import org.catframework.agileworking.domain.Participant;
9 | import org.catframework.agileworking.domain.Schedule;
10 | import org.catframework.agileworking.domain.ScheduleFactory;
11 | import org.catframework.agileworking.domain.ScheduleRepository;
12 | import org.catframework.agileworking.domain.Team;
13 | import org.catframework.agileworking.domain.TeamFactory;
14 | import org.catframework.agileworking.domain.TeamRepository;
15 | import org.catframework.agileworking.domain.User;
16 | import org.catframework.agileworking.domain.UserFactory;
17 | import org.catframework.agileworking.domain.UserRepository;
18 | import org.catframework.agileworking.web.support.Result;
19 | import org.junit.After;
20 | import org.junit.Assert;
21 | import org.junit.Before;
22 | import org.junit.Test;
23 | import org.junit.runner.RunWith;
24 | import org.springframework.beans.factory.annotation.Autowired;
25 | import org.springframework.boot.test.context.SpringBootTest;
26 | import org.springframework.test.context.junit4.SpringRunner;
27 |
28 | @RunWith(SpringRunner.class)
29 | @SpringBootTest
30 | public class ScheduleControllerTest {
31 |
32 | @Autowired
33 | private ScheduleController scheduleController;
34 |
35 | @Autowired
36 | private MeetingRoomController meetingRoomController;
37 |
38 | @Autowired
39 | private ScheduleRepository scheduleRepository;
40 |
41 | @Autowired
42 | private MeetingRoomRepository meetingRoomRepository;
43 |
44 | @Autowired
45 | private TeamRepository teamRepository;
46 |
47 | @Autowired
48 | private UserRepository userRepository;
49 |
50 | private Team team = TeamFactory.newDefaultTeam();
51 |
52 | private User user = UserFactory.newDefaultUser();
53 |
54 | private List meetingRooms = MeetingRoomFactory.defaultMeetingRooms();
55 |
56 | @Before
57 | public void before() {
58 | userRepository.save(user);
59 | teamRepository.save(team);
60 | team.addUser(user);
61 | teamRepository.save(team);
62 | meetingRooms.stream().forEach(m -> m.setTeamId(team.getId()));
63 | meetingRoomRepository.save(meetingRooms);
64 | }
65 |
66 | @After
67 | public void after() {
68 | teamRepository.delete(team.getId());
69 | userRepository.delete(user);
70 | meetingRoomRepository.delete(meetingRooms);
71 | }
72 |
73 | @Test
74 | public void testJoin() {
75 | Result> result = meetingRoomController.list(team.getId());
76 | Schedule s = ScheduleFactory.newWeeklySchedule("分行业务平台项目组临时会议", "七猫", "2017-08-02", "13:00", "14:00");
77 | meetingRoomController.createOrUpdateSchedule(result.getPayload().get(0).getId(), "fakeFormId", s);
78 | Participant p = new Participant();
79 | p.setAvatarUrl("some url");
80 | p.setNickName("7upcat");
81 | p.setOpenId("7upcat_open_id");
82 | p.setFormId("fakeFormId");
83 | scheduleController.join(s.getId(), p);
84 | s = scheduleRepository.findOne(s.getId());
85 | Assert.assertEquals(2, s.getParticipants().size());
86 | Assert.assertEquals("七猫", s.getParticipants().get(0).getNickName());
87 | Assert.assertEquals("7upcat", s.getParticipants().get(1).getNickName());
88 | Assert.assertEquals("fakeFormId", s.getParticipants().get(1).getFormId());
89 | try {
90 | scheduleController.join(s.getId(), p);
91 | Assert.fail();
92 | } catch (Exception e) {
93 | Assert.assertEquals("您已加入过此会议啦.", e.getMessage());
94 | }
95 | scheduleRepository.delete(s);
96 | }
97 |
98 | @Test
99 | public void testGet() {
100 | Result> result = meetingRoomController.list(team.getId());
101 | Schedule s = ScheduleFactory.newWeeklySchedule("分行业务平台项目组临时会议", "7upcat_open_id", "2017-08-02", "13:00", "14:00");
102 | Result sResult = meetingRoomController.createOrUpdateSchedule(result.getPayload().get(0).getId(),
103 | "fakeFormId", s);
104 | Assert.assertTrue(sResult.isSuccess());
105 | sResult = scheduleController.get(sResult.getPayload().getId());
106 | Assert.assertNotNull(sResult);
107 | scheduleRepository.delete(s);
108 | }
109 | }
110 |
--------------------------------------------------------------------------------
/src/test/java/org/catframework/agileworking/web/TeamControllerTest.java:
--------------------------------------------------------------------------------
1 | package org.catframework.agileworking.web;
2 |
3 | import java.util.List;
4 |
5 | import org.catframework.agileworking.domain.Team;
6 | import org.catframework.agileworking.domain.TeamFactory;
7 | import org.catframework.agileworking.domain.TeamRepository;
8 | import org.catframework.agileworking.domain.User;
9 | import org.catframework.agileworking.domain.UserFactory;
10 | import org.catframework.agileworking.domain.UserRepository;
11 | import org.catframework.agileworking.service.WebTokenService;
12 | import org.catframework.agileworking.web.support.Result;
13 | import org.junit.Assert;
14 | import org.junit.Test;
15 | import org.junit.runner.RunWith;
16 | import org.springframework.beans.factory.annotation.Autowired;
17 | import org.springframework.boot.test.context.SpringBootTest;
18 | import org.springframework.test.context.junit4.SpringRunner;
19 |
20 | @RunWith(SpringRunner.class)
21 | @SpringBootTest
22 | public class TeamControllerTest {
23 |
24 | @Autowired
25 | private TeamRepository teamRepository;
26 |
27 | @Autowired
28 | private TeamController teamController;
29 |
30 | @Autowired
31 | private UserRepository userRepository;
32 |
33 | @Autowired
34 | private WebTokenService webTokenService;
35 |
36 | @Test
37 | public void testJoin() {
38 | Team team = teamRepository.save(TeamFactory.newDefaultTeam());
39 | User user = UserFactory.newDefaultUser();
40 | userRepository.save(user);
41 | team = teamRepository.findOne(team.getId());
42 | Result result = teamController.join(team.getId(), user, team.getToken());
43 | String token = (String) result.getHeader("token");
44 | Assert.assertEquals(webTokenService.generate(user.getOpenId()), token);
45 | team = teamRepository.findOne(team.getId());
46 | Assert.assertEquals(1, team.getUsers().size());
47 | team.getUsers().clear();
48 | teamRepository.save(team);
49 | team = teamRepository.findOne(team.getId());
50 | Assert.assertEquals(0, team.getUsers().size());
51 | userRepository.delete(user);
52 | teamRepository.delete(team);
53 | }
54 |
55 | @Test
56 | public void testList() {
57 | Team team = teamRepository.save(TeamFactory.newDefaultTeam());
58 | Result> result = teamController.findAll();
59 | Assert.assertEquals(1, result.getPayload().size());
60 | teamRepository.delete(team);
61 | }
62 |
63 | @Test
64 | public void testGetUser() {
65 | Team team = teamRepository.save(TeamFactory.newDefaultTeam());
66 | User user = UserFactory.newDefaultUser();
67 | userRepository.save(user);
68 | Result result = teamController.getUser(team.getId(), user.getOpenId());
69 | Assert.assertFalse(result.isSuccess());
70 | Assert.assertEquals("用户未绑定.", result.getResponseMessage());
71 | teamController.join(team.getId(), user, team.getToken());
72 | result = teamController.getUser(team.getId(), user.getOpenId());
73 | Assert.assertTrue(result.isSuccess());
74 | String token = (String) result.getHeader("token");
75 | Assert.assertEquals(webTokenService.generate(user.getOpenId()), token);
76 | userRepository.delete(user);
77 | teamRepository.delete(team);
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/src/test/java/org/catframework/agileworking/web/WechatControllerIntegrationTest.java:
--------------------------------------------------------------------------------
1 | package org.catframework.agileworking.web;
2 |
3 | import org.apache.commons.logging.Log;
4 | import org.apache.commons.logging.LogFactory;
5 | import org.catframework.agileworking.web.support.Result;
6 | import org.junit.Test;
7 | import org.junit.runner.RunWith;
8 | import org.springframework.beans.factory.annotation.Autowired;
9 | import org.springframework.boot.test.context.SpringBootTest;
10 | import org.springframework.test.context.junit4.SpringRunner;
11 |
12 | /**
13 | * 集成测试获取 openid ,需要指定 jsCode
14 | * @author devzzm
15 | */
16 | @RunWith(SpringRunner.class)
17 | @SpringBootTest
18 | public class WechatControllerIntegrationTest {
19 |
20 | private static final Log logger = LogFactory.getLog(WechatControllerIntegrationTest.class);
21 |
22 | @Autowired
23 | private WechatController wechatController;
24 |
25 | @Test
26 | public void test() {
27 | String jsCode = "someJsCode";
28 | Result result = wechatController.getOpenId(jsCode);
29 | logger.info(result.getPayload());
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/src/test/resources/logback-test.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------